Merge "Update CorePerfTests with collector libraries."
diff --git a/apct-tests/perftests/windowmanager/Android.bp b/apct-tests/perftests/windowmanager/Android.bp
index 9e95a10..9fa99853 100644
--- a/apct-tests/perftests/windowmanager/Android.bp
+++ b/apct-tests/perftests/windowmanager/Android.bp
@@ -19,6 +19,7 @@
"androidx.test.rules",
"androidx.annotation_annotation",
"apct-perftests-utils",
+ "collector-device-lib",
"platform-test-annotations",
],
test_suites: ["device-tests"],
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index ef30cb4..f474389 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -52,14 +52,23 @@
* if in the same profile group.
* Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
*/
- public static final int ALLOW_NON_FULL_IN_PROFILE = 1;
+ public static final int ALLOW_NON_FULL_IN_PROFILE_OR_FULL = 1;
public static final int ALLOW_FULL_ONLY = 2;
/**
* Allows access to a caller with {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES}
* or {@link android.Manifest.permission#INTERACT_ACROSS_USERS} if in the same profile group.
* Otherwise, {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required.
*/
- public static final int ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE = 3;
+ public static final int ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL = 3;
+ /**
+ * Requires {@link android.Manifest.permission#INTERACT_ACROSS_PROFILES},
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS}, or
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL} if in same profile group,
+ * otherwise {@link android.Manifest.permission#INTERACT_ACROSS_USERS} or
+ * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL}. (so this is an extension
+ * to {@link #ALLOW_NON_FULL})
+ */
+ public static final int ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL = 4;
/**
* Verify that calling app has access to the given provider.
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 20aa064..a789169 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -919,7 +919,7 @@
/**
* Only specialized platform agents should overload this entry point to support
- * restores to crazy non-app locations.
+ * restores to non-app locations.
* @hide
*/
protected void onRestoreFile(ParcelFileDescriptor data, long size,
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index f4645ca..39c196d 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -1,6 +1,14 @@
{
"presubmit": [
- // TODO(159590499) add BugreportManagerTestCases
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "BugreportManagerTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ },
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "CtsBugreportTestCases",
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index bd7e610..cfe7edd 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10855,17 +10855,6 @@
public static final String MODE_RINGER = "mode_ringer";
/**
- * Specifies whether Enhanced Connectivity is enabled or not. This setting allows the
- * Connectivity Thermal Power Manager to actively help the device to save power in 5G
- * scenarios
- * Type: int 1 is enabled, 0 is disabled
- *
- * @hide
- */
- public static final String ENHANCED_CONNECTIVITY_ENABLED =
- "enhanced_connectivity_enable";
-
- /**
* Overlay display devices setting.
* The associated value is a specially formatted string that describes the
* size and density of simulated secondary display devices.
diff --git a/core/java/android/service/notification/NotificationStats.java b/core/java/android/service/notification/NotificationStats.java
index 8be114c..2cd8b8b 100644
--- a/core/java/android/service/notification/NotificationStats.java
+++ b/core/java/android/service/notification/NotificationStats.java
@@ -73,6 +73,11 @@
* Notification has been dismissed from the notification shade.
*/
public static final int DISMISSAL_SHADE = 3;
+ /**
+ * Notification has been dismissed as a bubble.
+ * @hide
+ */
+ public static final int DISMISSAL_BUBBLE = 3;
/** @hide */
@IntDef(prefix = { "DISMISS_SENTIMENT_" }, value = {
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6beea876..eaa7eaf 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -595,6 +595,256 @@
}
/**
+ * A common arguments class used for various screenshot requests. This contains arguments that
+ * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs}
+ * @hide
+ */
+ public abstract static class CaptureArgs {
+ private final int mPixelFormat;
+ private final Rect mSourceCrop = new Rect();
+ private final float mFrameScale;
+ private final boolean mCaptureSecureLayers;
+
+ private CaptureArgs(Builder<? extends Builder<?>> builder) {
+ mPixelFormat = builder.mPixelFormat;
+ mSourceCrop.set(builder.mSourceCrop);
+ mFrameScale = builder.mFrameScale;
+ mCaptureSecureLayers = builder.mCaptureSecureLayers;
+ }
+
+ /**
+ * The Builder class used to construct {@link CaptureArgs}
+ *
+ * @param <T> A builder that extends {@link Builder}
+ */
+ public abstract static class Builder<T extends Builder<T>> {
+ private int mPixelFormat = PixelFormat.RGBA_8888;
+ private final Rect mSourceCrop = new Rect();
+ private float mFrameScale = 1;
+ private boolean mCaptureSecureLayers;
+
+ /**
+ * The desired pixel format of the returned buffer.
+ */
+ public T setPixelFormat(int pixelFormat) {
+ mPixelFormat = pixelFormat;
+ return getThis();
+ }
+
+ /**
+ * The portion of the screen to capture into the buffer. Caller may pass in
+ * 'new Rect()' if no cropping is desired.
+ */
+ public T setSourceCrop(Rect sourceCrop) {
+ mSourceCrop.set(sourceCrop);
+ return getThis();
+ }
+
+ /**
+ * The desired scale of the returned buffer. The raw screen will be scaled up/down.
+ */
+ public T setFrameScale(float frameScale) {
+ mFrameScale = frameScale;
+ return getThis();
+ }
+
+ /**
+ * Whether to allow the screenshot of secure layers. Warning: This should only be done
+ * if the content will be placed in a secure SurfaceControl.
+ *
+ * @see ScreenshotHardwareBuffer#containsSecureLayers()
+ */
+ public T setCaptureSecureLayers(boolean captureSecureLayers) {
+ mCaptureSecureLayers = captureSecureLayers;
+ return getThis();
+ }
+
+ /**
+ * Each sub class should return itself to allow the builder to chain properly
+ */
+ public abstract T getThis();
+ }
+ }
+
+ /**
+ * The arguments class used to make display capture requests.
+ *
+ * @see #nativeScreenshot(IBinder, Rect, int, int, boolean, int, boolean)
+ * @hide
+ */
+ public static class DisplayCaptureArgs extends CaptureArgs {
+ private final IBinder mDisplayToken;
+ private final int mWidth;
+ private final int mHeight;
+ private final boolean mUseIdentityTransform;
+ private final int mRotation;
+
+ private DisplayCaptureArgs(Builder builder) {
+ super(builder);
+ mDisplayToken = builder.mDisplayToken;
+ mWidth = builder.mWidth;
+ mHeight = builder.mHeight;
+ mUseIdentityTransform = builder.mUseIdentityTransform;
+ mRotation = builder.mRotation;
+ }
+
+ /**
+ * The Builder class used to construct {@link DisplayCaptureArgs}
+ */
+ public static class Builder extends CaptureArgs.Builder<Builder> {
+ private IBinder mDisplayToken;
+ private int mWidth;
+ private int mHeight;
+ private boolean mUseIdentityTransform;
+ private @Surface.Rotation int mRotation = Surface.ROTATION_0;
+
+ /**
+ * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
+ * remains valid.
+ */
+ public DisplayCaptureArgs build() {
+ if (mDisplayToken == null) {
+ throw new IllegalStateException(
+ "Can't take screenshot with null display token");
+ }
+ return new DisplayCaptureArgs(this);
+ }
+
+ public Builder(IBinder displayToken) {
+ setDisplayToken(displayToken);
+ }
+
+ /**
+ * The display to take the screenshot of.
+ */
+ public Builder setDisplayToken(IBinder displayToken) {
+ mDisplayToken = displayToken;
+ return this;
+ }
+
+ /**
+ * Set the desired size of the returned buffer. The raw screen will be scaled down to
+ * this size
+ *
+ * @param width The desired width of the returned buffer. Caller may pass in 0 if no
+ * scaling is desired.
+ * @param height The desired height of the returned buffer. Caller may pass in 0 if no
+ * scaling is desired.
+ */
+ public Builder setSize(int width, int height) {
+ mWidth = width;
+ mHeight = height;
+ return this;
+ }
+
+ /**
+ * Replace whatever transformation (rotation, scaling, translation) the surface
+ * layers are currently using with the identity transformation while taking the
+ * screenshot.
+ */
+ public Builder setUseIdentityTransform(boolean useIdentityTransform) {
+ mUseIdentityTransform = useIdentityTransform;
+ return this;
+ }
+
+ /**
+ * Apply a custom clockwise rotation to the screenshot, i.e.
+ * Surface.ROTATION_0,90,180,270. SurfaceFlinger will always take screenshots in its
+ * native portrait orientation by default, so this is useful for returning screenshots
+ * that are independent of device orientation.
+ */
+ public Builder setRotation(@Surface.Rotation int rotation) {
+ mRotation = rotation;
+ return this;
+ }
+
+ @Override
+ public Builder getThis() {
+ return this;
+ }
+ }
+ }
+
+ /**
+ * The arguments class used to make layer capture requests.
+ *
+ * @see #nativeCaptureLayers(IBinder, long, Rect, float, long[], int)
+ * @hide
+ */
+ public static class LayerCaptureArgs extends CaptureArgs {
+ private final long mNativeLayer;
+ private final long[] mNativeExcludeLayers;
+ private final boolean mChildrenOnly;
+
+ private LayerCaptureArgs(Builder builder) {
+ super(builder);
+ mChildrenOnly = builder.mChildrenOnly;
+ mNativeLayer = builder.mLayer.mNativeObject;
+ mNativeExcludeLayers = new long[builder.mExcludeLayers.length];
+ for (int i = 0; i < builder.mExcludeLayers.length; i++) {
+ mNativeExcludeLayers[i] = builder.mExcludeLayers[i].mNativeObject;
+ }
+ }
+
+ /**
+ * The Builder class used to construct {@link LayerCaptureArgs}
+ */
+ public static class Builder extends CaptureArgs.Builder<Builder> {
+ private SurfaceControl mLayer;
+ private SurfaceControl[] mExcludeLayers;
+ private boolean mChildrenOnly = true;
+
+ /**
+ * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder
+ * remains valid.
+ */
+ public LayerCaptureArgs build() {
+ if (mLayer == null) {
+ throw new IllegalStateException(
+ "Can't take screenshot with null layer");
+ }
+ return new LayerCaptureArgs(this);
+ }
+
+ public Builder(SurfaceControl layer) {
+ setLayer(layer);
+ }
+
+ /**
+ * The root layer to capture.
+ */
+ public Builder setLayer(SurfaceControl layer) {
+ mLayer = layer;
+ return this;
+ }
+
+
+ /**
+ * An array of layer handles to exclude.
+ */
+ public Builder setExcludeLayers(@Nullable SurfaceControl[] excludeLayers) {
+ mExcludeLayers = excludeLayers;
+ return this;
+ }
+
+ /**
+ * Whether to include the layer itself in the screenshot or just the children and their
+ * descendants.
+ */
+ public Builder setChildrenOnly(boolean childrenOnly) {
+ mChildrenOnly = childrenOnly;
+ return this;
+ }
+
+ @Override
+ public Builder getThis() {
+ return this;
+ }
+
+ }
+ }
+
+ /**
* Builder class for {@link SurfaceControl} objects.
*
* By default the surface will be hidden, and have "unset" bounds, meaning it can
@@ -1969,37 +2219,6 @@
}
/**
- * @see SurfaceControl#screenshot(IBinder, Surface, Rect, int, int, boolean, int)
- * @hide
- */
- public static void screenshot(IBinder display, Surface consumer) {
- screenshot(display, consumer, new Rect(), 0, 0, false, 0);
- }
-
- /**
- * Copy the current screen contents into the provided {@link Surface}
- *
- * @param consumer The {@link Surface} to take the screenshot into.
- * @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)
- * @hide
- */
- public static void screenshot(IBinder display, Surface consumer, Rect sourceCrop, int width,
- int height, boolean useIdentityTransform, int rotation) {
- if (consumer == null) {
- throw new IllegalArgumentException("consumer must not be null");
- }
-
- final ScreenshotHardwareBuffer buffer = screenshotToBuffer(display, sourceCrop, width,
- height, useIdentityTransform, rotation);
- try {
- consumer.attachAndQueueBufferWithColorSpace(buffer.getHardwareBuffer(),
- buffer.getColorSpace());
- } catch (RuntimeException e) {
- Log.w(TAG, "Failed to take screenshot - " + e.getMessage());
- }
- }
-
- /**
* @see SurfaceControl#screenshot(Rect, int, int, boolean, int)}
* @hide
*/
@@ -2014,8 +2233,7 @@
* a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)}
*
* CAVEAT: Versions of screenshot that return a {@link Bitmap} can be extremely slow; avoid use
- * unless absolutely necessary; prefer the versions that use a {@link Surface} such as
- * {@link SurfaceControl#screenshot(IBinder, Surface)} or {@link HardwareBuffer} such as
+ * unless absolutely necessary; prefer the versions that use a {@link HardwareBuffer} such as
* {@link SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}.
*
* @see SurfaceControl#screenshotToBuffer(IBinder, Rect, int, int, boolean, int)}
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
index cce1090..6300320 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsRequest.java
@@ -47,6 +47,9 @@
/**
* Max number of suggestions expected from the response. It must be a positive value.
* Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set.
+ *
+ * <p>In practice, it is recommended that the max suggestion count does not exceed <b>5</b>
+ * for performance reasons.</p>
*/
private final int mMaxSuggestionCount;
@@ -67,6 +70,9 @@
/**
* The IME provided locales for the request. If non-empty, the inline suggestions should
* return languages from the supported locales. If not provided, it'll default to system locale.
+ *
+ * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
+ * to have one locale to guarantee consistent UI rendering.</p>
*/
private @NonNull LocaleList mSupportedLocales;
@@ -227,6 +233,9 @@
/**
* Max number of suggestions expected from the response. It must be a positive value.
* Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set.
+ *
+ * <p>In practice, it is recommended that the max suggestion count does not exceed <b>5</b>
+ * for performance reasons.</p>
*/
@DataClass.Generated.Member
public int getMaxSuggestionCount() {
@@ -256,6 +265,9 @@
/**
* The IME provided locales for the request. If non-empty, the inline suggestions should
* return languages from the supported locales. If not provided, it'll default to system locale.
+ *
+ * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
+ * to have one locale to guarantee consistent UI rendering.</p>
*/
@DataClass.Generated.Member
public @NonNull LocaleList getSupportedLocales() {
@@ -458,6 +470,9 @@
/**
* Max number of suggestions expected from the response. It must be a positive value.
* Defaults to {@code SUGGESTION_COUNT_UNLIMITED} if not set.
+ *
+ * <p>In practice, it is recommended that the max suggestion count does not exceed <b>5</b>
+ * for performance reasons.</p>
*/
@DataClass.Generated.Member
public @NonNull Builder setMaxSuggestionCount(int value) {
@@ -508,6 +523,9 @@
/**
* The IME provided locales for the request. If non-empty, the inline suggestions should
* return languages from the supported locales. If not provided, it'll default to system locale.
+ *
+ * <p>Note for Autofill Providers: It is <b>recommended</b> for the returned inline suggestions
+ * to have one locale to guarantee consistent UI rendering.</p>
*/
@DataClass.Generated.Member
public @NonNull Builder setSupportedLocales(@NonNull LocaleList value) {
@@ -604,7 +622,7 @@
}
@DataClass.Generated(
- time = 1588109685838L,
+ time = 1595457701315L,
codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsRequest.java",
inputSignatures = "public static final int SUGGESTION_COUNT_UNLIMITED\nprivate final int mMaxSuggestionCount\nprivate final @android.annotation.NonNull java.util.List<android.widget.inline.InlinePresentationSpec> mInlinePresentationSpecs\nprivate @android.annotation.NonNull java.lang.String mHostPackageName\nprivate @android.annotation.NonNull android.os.LocaleList mSupportedLocales\nprivate @android.annotation.NonNull android.os.Bundle mExtras\nprivate @android.annotation.Nullable android.os.IBinder mHostInputToken\nprivate int mHostDisplayId\npublic void setHostInputToken(android.os.IBinder)\nprivate boolean extrasEquals(android.os.Bundle)\nprivate void parcelHostInputToken(android.os.Parcel,int)\nprivate @android.annotation.Nullable android.os.IBinder unparcelHostInputToken(android.os.Parcel)\npublic void setHostDisplayId(int)\nprivate void onConstructed()\npublic void filterContentTypes()\nprivate static int defaultMaxSuggestionCount()\nprivate static java.lang.String defaultHostPackageName()\nprivate static android.os.LocaleList defaultSupportedLocales()\nprivate static @android.annotation.Nullable android.os.IBinder defaultHostInputToken()\nprivate static @android.annotation.Nullable int defaultHostDisplayId()\nprivate static @android.annotation.NonNull android.os.Bundle defaultExtras()\nclass InlineSuggestionsRequest extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genBuilder=true)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setInlinePresentationSpecs(java.util.List<android.widget.inline.InlinePresentationSpec>)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostPackageName(java.lang.String)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostInputToken(android.os.IBinder)\nabstract android.view.inputmethod.InlineSuggestionsRequest.Builder setHostDisplayId(int)\nclass BaseBuilder extends java.lang.Object implements []")
diff --git a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
index be833df..b393c67 100644
--- a/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
+++ b/core/java/android/view/inputmethod/InlineSuggestionsResponse.java
@@ -32,7 +32,18 @@
*/
@DataClass(genEqualsHashCode = true, genToString = true, genHiddenConstructor = true)
public final class InlineSuggestionsResponse implements Parcelable {
- private final @NonNull List<InlineSuggestion> mInlineSuggestions;
+ /**
+ * List of {@link InlineSuggestion}s returned as a part of this response.
+ *
+ * <p>When the host app requests to inflate this <b>ordered</b> list of inline suggestions by
+ * calling {@link InlineSuggestion#inflate}, it is the host's responsibility to track the
+ * order of the inflated {@link android.view.View}s. These views are to be added in
+ * order to the view hierarchy, because the inflation calls will return asynchronously.</p>
+ *
+ * <p>The inflation ordering does not apply to the pinned icon.</p>
+ */
+ @NonNull
+ private final List<InlineSuggestion> mInlineSuggestions;
/**
* Creates a new {@link InlineSuggestionsResponse}, for testing purpose.
@@ -48,7 +59,7 @@
- // Code below generated by codegen v1.0.14.
+ // Code below generated by codegen v1.0.15.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
@@ -64,6 +75,15 @@
/**
* Creates a new InlineSuggestionsResponse.
*
+ * @param inlineSuggestions
+ * List of {@link InlineSuggestion}s returned as a part of this response.
+ *
+ * <p>When the host app requests to inflate this <b>ordered</b> list of inline suggestions by
+ * calling {@link InlineSuggestion#inflate}, it is the host's responsibility to track the
+ * order of the inflated {@link android.view.View}s. These views are to be added in
+ * order to the view hierarchy, because the inflation calls will return asynchronously.</p>
+ *
+ * <p>The inflation ordering does not apply to the pinned icon.</p>
* @hide
*/
@DataClass.Generated.Member
@@ -76,6 +96,16 @@
// onConstructed(); // You can define this method to get a callback
}
+ /**
+ * List of {@link InlineSuggestion}s returned as a part of this response.
+ *
+ * <p>When the host app requests to inflate this <b>ordered</b> list of inline suggestions by
+ * calling {@link InlineSuggestion#inflate}, it is the host's responsibility to track the
+ * order of the inflated {@link android.view.View}s. These views are to be added in
+ * order to the view hierarchy, because the inflation calls will return asynchronously.</p>
+ *
+ * <p>The inflation ordering does not apply to the pinned icon.</p>
+ */
@DataClass.Generated.Member
public @NonNull List<InlineSuggestion> getInlineSuggestions() {
return mInlineSuggestions;
@@ -164,8 +194,8 @@
};
@DataClass.Generated(
- time = 1578972149519L,
- codegenVersion = "1.0.14",
+ time = 1595891876037L,
+ codegenVersion = "1.0.15",
sourceFile = "frameworks/base/core/java/android/view/inputmethod/InlineSuggestionsResponse.java",
inputSignatures = "private final @android.annotation.NonNull java.util.List<android.view.inputmethod.InlineSuggestion> mInlineSuggestions\npublic static @android.annotation.TestApi @android.annotation.NonNull android.view.inputmethod.InlineSuggestionsResponse newInlineSuggestionsResponse(java.util.List<android.view.inputmethod.InlineSuggestion>)\nclass InlineSuggestionsResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genEqualsHashCode=true, genToString=true, genHiddenConstructor=true)")
@Deprecated
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 16e87f8..97e0689 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1514,7 +1514,8 @@
if (mOnScrollListener != null) {
mOnScrollListener.onScroll(this, mFirstPosition, getChildCount(), mItemCount);
}
- onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
+ // placeholder values, View's implementation does not use these.
+ onScrollChanged(0, 0, 0, 0);
}
/**
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index 8c0061d..d969a88 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -431,7 +431,8 @@
mSelectedCenterOffset = childLeft + childCenter - galleryCenter;
}
- onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
+ // placeholder values, View's implementation does not use these.
+ onScrollChanged(0, 0, 0, 0);
invalidate();
}
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index e58f08a..b4379ec 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -505,7 +505,7 @@
public void reset() {
count = 0;
- // by default there is at least one dummy view type
+ // by default there is at least one placeholder view type
viewTypeCount = 1;
hasStableIds = true;
loadingTemplate = null;
@@ -948,7 +948,7 @@
private void updateTemporaryMetaData(IRemoteViewsFactory factory) {
try {
// get the properties/first view (so that we can use it to
- // measure our dummy views)
+ // measure our placeholder views)
boolean hasStableIds = factory.hasStableIds();
int viewTypeCount = factory.getViewTypeCount();
int count = factory.getCount();
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 6ef570c..2eadb56 100755
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -1789,7 +1789,7 @@
return createIntent(action, dataUri, extraData, query, actionKey, actionMsg);
} catch (RuntimeException e ) {
int rowNum;
- try { // be really paranoid now
+ try { // be really defensive now
rowNum = c.getPosition();
} catch (RuntimeException e2 ) {
rowNum = -1;
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.cpp b/core/jni/android_hardware_input_InputApplicationHandle.cpp
index 9137ee0..ff73c74 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.cpp
+++ b/core/jni/android_hardware_input_InputApplicationHandle.cpp
@@ -77,32 +77,28 @@
return mInfo.token.get() != nullptr;
}
-
// --- Global functions ---
-sp<InputApplicationHandle> android_view_InputApplicationHandle_getHandle(
+std::shared_ptr<InputApplicationHandle> android_view_InputApplicationHandle_getHandle(
JNIEnv* env, jobject inputApplicationHandleObj) {
if (!inputApplicationHandleObj) {
return NULL;
}
AutoMutex _l(gHandleMutex);
-
jlong ptr = env->GetLongField(inputApplicationHandleObj, gInputApplicationHandleClassInfo.ptr);
- NativeInputApplicationHandle* handle;
+ std::shared_ptr<NativeInputApplicationHandle>* handle;
if (ptr) {
- handle = reinterpret_cast<NativeInputApplicationHandle*>(ptr);
+ handle = reinterpret_cast<std::shared_ptr<NativeInputApplicationHandle>*>(ptr);
} else {
jweak objWeak = env->NewWeakGlobalRef(inputApplicationHandleObj);
- handle = new NativeInputApplicationHandle(objWeak);
- handle->incStrong((void*)android_view_InputApplicationHandle_getHandle);
+ handle = new std::shared_ptr(std::make_shared<NativeInputApplicationHandle>(objWeak));
env->SetLongField(inputApplicationHandleObj, gInputApplicationHandleClassInfo.ptr,
reinterpret_cast<jlong>(handle));
}
- return handle;
+ return *handle;
}
-
// --- JNI ---
static void android_view_InputApplicationHandle_nativeDispose(JNIEnv* env, jobject obj) {
@@ -112,8 +108,9 @@
if (ptr) {
env->SetLongField(obj, gInputApplicationHandleClassInfo.ptr, 0);
- NativeInputApplicationHandle* handle = reinterpret_cast<NativeInputApplicationHandle*>(ptr);
- handle->decStrong((void*)android_view_InputApplicationHandle_getHandle);
+ std::shared_ptr<NativeInputApplicationHandle>* handle =
+ reinterpret_cast<std::shared_ptr<NativeInputApplicationHandle>*>(ptr);
+ delete handle;
}
}
diff --git a/core/jni/android_hardware_input_InputApplicationHandle.h b/core/jni/android_hardware_input_InputApplicationHandle.h
index 52ab3e6..ec99d6d 100644
--- a/core/jni/android_hardware_input_InputApplicationHandle.h
+++ b/core/jni/android_hardware_input_InputApplicationHandle.h
@@ -39,8 +39,7 @@
jweak mObjWeak;
};
-
-extern sp<InputApplicationHandle> android_view_InputApplicationHandle_getHandle(
+extern std::shared_ptr<InputApplicationHandle> android_view_InputApplicationHandle_getHandle(
JNIEnv* env, jobject inputApplicationHandleObj);
} // namespace android
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index bdb4544..796c5c4 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -168,8 +168,8 @@
jobject inputApplicationHandleObj = env->GetObjectField(obj,
gInputWindowHandleClassInfo.inputApplicationHandle);
if (inputApplicationHandleObj) {
- sp<InputApplicationHandle> inputApplicationHandle =
- android_view_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
+ std::shared_ptr<InputApplicationHandle> inputApplicationHandle =
+ android_view_InputApplicationHandle_getHandle(env, inputApplicationHandleObj);
if (inputApplicationHandle != nullptr) {
inputApplicationHandle->updateInfo();
mInfo.applicationInfo = *(inputApplicationHandle->getInfo());
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 6ede806..6307c35 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -1937,7 +1937,7 @@
<item quantity="other"><xliff:g id="COUNT">%1$s</xliff:g> suggestions de saisie automatique</item>
</plurals>
<string name="autofill_save_title" msgid="7719802414283739775">"Enregistrer dans "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ?"</string>
- <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Enregistrer <xliff:g id="TYPE">%1$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ?"</string>
+ <string name="autofill_save_title_with_type" msgid="3002460014579799605">"Enregistrer la <xliff:g id="TYPE">%1$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%2$s</xliff:g>"</b>" ?"</string>
<string name="autofill_save_title_with_2types" msgid="3783270967447869241">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g> et <xliff:g id="TYPE_1">%2$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%3$s</xliff:g>"</b>" ?"</string>
<string name="autofill_save_title_with_3types" msgid="6598228952100102578">"Enregistrer <xliff:g id="TYPE_0">%1$s</xliff:g>, <xliff:g id="TYPE_1">%2$s</xliff:g> et <xliff:g id="TYPE_2">%3$s</xliff:g> dans "<b>"<xliff:g id="LABEL">%4$s</xliff:g>"</b>" ?"</string>
<string name="autofill_update_title" msgid="3630695947047069136">"Mettre à jour cet élément dans "<b>"<xliff:g id="LABEL">%1$s</xliff:g>"</b>" ?"</string>
diff --git a/core/tests/bugreports/Android.bp b/core/tests/bugreports/Android.bp
index 1edd962..e42b4b4 100644
--- a/core/tests/bugreports/Android.bp
+++ b/core/tests/bugreports/Android.bp
@@ -20,7 +20,11 @@
"android.test.runner",
"android.test.base",
],
- static_libs: ["androidx.test.rules", "truth-prebuilt"],
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
+ "truth-prebuilt",
+ ],
test_suites: ["general-tests"],
sdk_version: "test_current",
platform_apis: true,
diff --git a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
index 1533377..9246a23 100644
--- a/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
+++ b/core/tests/bugreports/src/com/android/os/bugreports/tests/BugreportManagerTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.Manifest;
@@ -25,17 +27,27 @@
import android.os.BugreportManager;
import android.os.BugreportManager.BugreportCallback;
import android.os.BugreportParams;
+import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
+import android.os.StrictMode;
import android.util.Log;
+import androidx.annotation.NonNull;
import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.ExternalResource;
import org.junit.rules.TestName;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@@ -51,9 +63,11 @@
@RunWith(JUnit4.class)
public class BugreportManagerTest {
@Rule public TestName name = new TestName();
+ @Rule public ExtendedStrictModeVmPolicy mTemporaryVmPolicy = new ExtendedStrictModeVmPolicy();
private static final String TAG = "BugreportManagerTest";
private static final long BUGREPORT_TIMEOUT_MS = TimeUnit.MINUTES.toMillis(10);
+ private static final long UIAUTOMATOR_TIMEOUT_MS = TimeUnit.SECONDS.toMillis(10);
private Handler mHandler;
private Executor mExecutor;
@@ -86,6 +100,8 @@
@After
public void teardown() throws Exception {
dropPermissions();
+ FileUtils.closeQuietly(mBugreportFd);
+ FileUtils.closeQuietly(mScreenshotFd);
}
@@ -95,47 +111,45 @@
// wifi bugreport does not take screenshot
mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, wifi(),
mExecutor, callback);
+ shareConsentDialog(ConsentReply.ALLOW);
waitTillDoneOrTimeout(callback);
assertThat(callback.isDone()).isTrue();
// Wifi bugreports should not receive any progress.
assertThat(callback.hasReceivedProgress()).isFalse();
- // TODO: Because of b/130234145, consent dialog is not shown; so we get a timeout error.
- // When the bug is fixed, accept consent via UIAutomator and verify contents
- // of mBugreportFd.
- assertThat(callback.getErrorCode()).isEqualTo(
- BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+ assertThat(mBugreportFile.length()).isGreaterThan(0L);
assertFdsAreClosed(mBugreportFd);
}
+ @LargeTest
@Test
public void normalFlow_interactive() throws Exception {
BugreportCallbackImpl callback = new BugreportCallbackImpl();
// interactive bugreport does not take screenshot
mBrm.startBugreport(mBugreportFd, null /*screenshotFd = null*/, interactive(),
mExecutor, callback);
-
+ shareConsentDialog(ConsentReply.ALLOW);
waitTillDoneOrTimeout(callback);
+
assertThat(callback.isDone()).isTrue();
// Interactive bugreports show progress updates.
assertThat(callback.hasReceivedProgress()).isTrue();
- assertThat(callback.getErrorCode()).isEqualTo(
- BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+ assertThat(mBugreportFile.length()).isGreaterThan(0L);
assertFdsAreClosed(mBugreportFd);
}
+ @LargeTest
@Test
public void normalFlow_full() throws Exception {
BugreportCallbackImpl callback = new BugreportCallbackImpl();
mBrm.startBugreport(mBugreportFd, mScreenshotFd, full(), mExecutor, callback);
-
+ shareConsentDialog(ConsentReply.ALLOW);
waitTillDoneOrTimeout(callback);
+
assertThat(callback.isDone()).isTrue();
- assertThat(callback.getErrorCode()).isEqualTo(
- BugreportCallback.BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
- // bugreport and screenshot files should be empty when user consent timed out.
- assertThat(mBugreportFile.length()).isEqualTo(0);
- assertThat(mScreenshotFile.length()).isEqualTo(0);
+ // bugreport and screenshot files shouldn't be empty when user consents.
+ assertThat(mBugreportFile.length()).isGreaterThan(0L);
+ assertThat(mScreenshotFile.length()).isGreaterThan(0L);
assertFdsAreClosed(mBugreportFd, mScreenshotFd);
}
@@ -144,6 +158,8 @@
// Start bugreport #1
BugreportCallbackImpl callback = new BugreportCallbackImpl();
mBrm.startBugreport(mBugreportFd, mScreenshotFd, wifi(), mExecutor, callback);
+ // TODO(b/162389762) Make sure the wait time is reasonable
+ shareConsentDialog(ConsentReply.ALLOW);
// Before #1 is done, try to start #2.
assertThat(callback.isDone()).isFalse();
@@ -375,4 +391,88 @@
private static BugreportParams full() {
return new BugreportParams(BugreportParams.BUGREPORT_MODE_FULL);
}
+
+ /* Allow/deny the consent dialog to sharing bugreport data or check existence only. */
+ private enum ConsentReply {
+ ALLOW,
+ DENY,
+ TIMEOUT
+ }
+
+ /*
+ * Ensure the consent dialog is shown and take action according to <code>consentReply<code/>.
+ * It will fail if the dialog is not shown when <code>ignoreNotFound<code/> is false.
+ */
+ private void shareConsentDialog(@NonNull ConsentReply consentReply) throws Exception {
+ mTemporaryVmPolicy.permitIncorrectContextUse();
+ final UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+
+ // Unlock before finding/clicking an object.
+ device.wakeUp();
+ device.executeShellCommand("wm dismiss-keyguard");
+
+ final BySelector consentTitleObj = By.res("android", "alertTitle");
+ if (!device.wait(Until.hasObject(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS)) {
+ fail("The consent dialog is not found");
+ }
+ if (consentReply.equals(ConsentReply.TIMEOUT)) {
+ return;
+ }
+ final BySelector selector;
+ if (consentReply.equals(ConsentReply.ALLOW)) {
+ selector = By.res("android", "button1");
+ Log.d(TAG, "Allow the consent dialog");
+ } else { // ConsentReply.DENY
+ selector = By.res("android", "button2");
+ Log.d(TAG, "Deny the consent dialog");
+ }
+ final UiObject2 btnObj = device.findObject(selector);
+ assertNotNull("The button of consent dialog is not found", btnObj);
+ btnObj.click();
+
+ Log.d(TAG, "Wait for the dialog to be dismissed");
+ assertTrue(device.wait(Until.gone(consentTitleObj), UIAUTOMATOR_TIMEOUT_MS));
+ }
+
+ /**
+ * A rule to change strict mode vm policy temporarily till test method finished.
+ *
+ * To permit the non-visual context usage in tests while taking bugreports need user consent,
+ * or UiAutomator/BugreportManager.DumpstateListener would run into error.
+ * UiDevice#findObject creates UiObject2, its Gesture object and ViewConfiguration and
+ * UiObject2#click need to know bounds. Both of them access to WindowManager internally without
+ * visual context comes from InstrumentationRegistry and violate the policy.
+ * Also <code>DumpstateListener<code/> violate the policy when onScreenshotTaken is called.
+ *
+ * TODO(b/161201609) Remove this class once violations fixed.
+ */
+ static class ExtendedStrictModeVmPolicy extends ExternalResource {
+ private boolean mWasVmPolicyChanged = false;
+ private StrictMode.VmPolicy mOldVmPolicy;
+
+ @Override
+ protected void after() {
+ restoreVmPolicyIfNeeded();
+ }
+
+ public void permitIncorrectContextUse() {
+ // Allow to call multiple times without losing old policy.
+ if (mOldVmPolicy == null) {
+ mOldVmPolicy = StrictMode.getVmPolicy();
+ }
+ StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
+ .detectAll()
+ .permitIncorrectContextUse()
+ .penaltyLog()
+ .build());
+ mWasVmPolicyChanged = true;
+ }
+
+ private void restoreVmPolicyIfNeeded() {
+ if (mWasVmPolicyChanged && mOldVmPolicy != null) {
+ StrictMode.setVmPolicy(mOldVmPolicy);
+ mOldVmPolicy = null;
+ }
+ }
+ }
}
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index a80600c..57e8e43 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -70,6 +70,7 @@
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
+import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.CRC32;
@@ -2095,7 +2096,10 @@
try {
// Move the original file to temporary file.
if (mFilename != null) {
- tempFile = new File(mFilename + ".tmp");
+ String parent = originalFile.getParent();
+ String name = originalFile.getName();
+ String tempPrefix = UUID.randomUUID().toString() + "_";
+ tempFile = new File(parent, tempPrefix + name);
if (!originalFile.renameTo(tempFile)) {
throw new IOException("Couldn't rename to " + tempFile.getAbsolutePath());
}
diff --git a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
index c7af8c8..d0776ae 100644
--- a/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
+++ b/packages/BackupEncryption/src/com/android/server/backup/encryption/chunking/cdc/Hkdf.java
@@ -40,18 +40,18 @@
*
* <p>IMPORTANT: The use or edit of this method requires a security review.
*
- * @param masterKey Master key from which to derive sub-keys.
+ * @param mainKey Main key from which to derive sub-keys.
* @param salt A randomly generated 256-bit byte string.
* @param data Arbitrary information that is bound to the derived key (i.e., used in its
* creation).
- * @return Raw derived key bytes = HKDF-SHA256(masterKey, salt, data).
+ * @return Raw derived key bytes = HKDF-SHA256(mainKey, salt, data).
* @throws InvalidKeyException If the salt can not be used as a valid key.
*/
- static byte[] hkdf(byte[] masterKey, byte[] salt, byte[] data) throws InvalidKeyException {
- Objects.requireNonNull(masterKey, "HKDF requires master key to be set.");
+ static byte[] hkdf(byte[] mainKey, byte[] salt, byte[] data) throws InvalidKeyException {
+ Objects.requireNonNull(mainKey, "HKDF requires main key to be set.");
Objects.requireNonNull(salt, "HKDF requires a salt.");
Objects.requireNonNull(data, "No data provided to HKDF.");
- return hkdfSha256Expand(hkdfSha256Extract(masterKey, salt), data);
+ return hkdfSha256Expand(hkdfSha256Extract(mainKey, salt), data);
}
private Hkdf() {}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index 8e8368f..03161d0 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -659,9 +659,6 @@
<!-- Setting Checkbox title for enabling Bluetooth Gabeldorsche. [CHAR LIMIT=40] -->
<string name="bluetooth_enable_gabeldorsche">Enable Gabeldorsche</string>
- <!-- Setting Checkbox title for enabling Enhanced Connectivity [CHAR LIMIT=80] -->
- <string name="enhanced_connectivity">Enhanced Connectivity</string>
-
<!-- UI debug setting: Select Bluetooth AVRCP Version -->
<string name="bluetooth_select_avrcp_version_string">Bluetooth AVRCP Version</string>
<!-- UI debug setting: Select Bluetooth AVRCP Version -->
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
index 6e5b889..66aa7ba 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WriteFallbackSettingsFilesJobService.java
@@ -35,19 +35,17 @@
public class WriteFallbackSettingsFilesJobService extends JobService {
@Override
public boolean onStartJob(final JobParameters params) {
- switch (params.getJobId()) {
- case WRITE_FALLBACK_SETTINGS_FILES_JOB_ID:
- final List<String> settingsFiles = new ArrayList<>();
- settingsFiles.add(params.getExtras().getString(TABLE_GLOBAL, ""));
- settingsFiles.add(params.getExtras().getString(TABLE_SYSTEM, ""));
- settingsFiles.add(params.getExtras().getString(TABLE_SECURE, ""));
- settingsFiles.add(params.getExtras().getString(TABLE_SSAID, ""));
- settingsFiles.add(params.getExtras().getString(TABLE_CONFIG, ""));
- SettingsProvider.writeFallBackSettingsFiles(settingsFiles);
- return true;
- default:
- return false;
+ if (params.getJobId() != WRITE_FALLBACK_SETTINGS_FILES_JOB_ID) {
+ return false;
}
+ final List<String> settingsFiles = new ArrayList<>();
+ settingsFiles.add(params.getExtras().getString(TABLE_GLOBAL, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_SYSTEM, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_SECURE, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_SSAID, ""));
+ settingsFiles.add(params.getExtras().getString(TABLE_CONFIG, ""));
+ SettingsProvider.writeFallBackSettingsFiles(settingsFiles);
+ return false;
}
@Override
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 5d77a2a..e49fd6f 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -270,7 +270,6 @@
Settings.Global.SMART_REPLIES_IN_NOTIFICATIONS_FLAGS,
Settings.Global.SMART_SUGGESTIONS_IN_NOTIFICATIONS_FLAGS,
Settings.Global.ENABLE_ADB_INCREMENTAL_INSTALL_DEFAULT,
- Settings.Global.ENHANCED_CONNECTIVITY_ENABLED,
Settings.Global.ENHANCED_4G_MODE_ENABLED,
Settings.Global.EPHEMERAL_COOKIE_MAX_SIZE_BYTES,
Settings.Global.ERROR_LOGCAT_PREFIX,
diff --git a/packages/SystemUI/res/drawable-hdpi/one_handed_tutorial.png b/packages/SystemUI/res/drawable-hdpi/one_handed_tutorial.png
new file mode 100644
index 0000000..6c1f1cf
--- /dev/null
+++ b/packages/SystemUI/res/drawable-hdpi/one_handed_tutorial.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-mdpi/one_handed_tutorial.png b/packages/SystemUI/res/drawable-mdpi/one_handed_tutorial.png
new file mode 100644
index 0000000..6983c3b8
--- /dev/null
+++ b/packages/SystemUI/res/drawable-mdpi/one_handed_tutorial.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xhdpi/one_handed_tutorial.png b/packages/SystemUI/res/drawable-xhdpi/one_handed_tutorial.png
new file mode 100644
index 0000000..3ff692f
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xhdpi/one_handed_tutorial.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxhdpi/one_handed_tutorial.png b/packages/SystemUI/res/drawable-xxhdpi/one_handed_tutorial.png
new file mode 100644
index 0000000..75723fb
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxhdpi/one_handed_tutorial.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-xxxhdpi/one_handed_tutorial.png b/packages/SystemUI/res/drawable-xxxhdpi/one_handed_tutorial.png
new file mode 100644
index 0000000..173abed
--- /dev/null
+++ b/packages/SystemUI/res/drawable-xxxhdpi/one_handed_tutorial.png
Binary files differ
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index f407a8d..2ad0cab 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -522,6 +522,8 @@
<!-- Defines the blacklist for system icons. That is to say, the icons in the status bar that
are part of the blacklist are never displayed. Each item in the blacklist must be a string
defined in core/res/res/config.xml to properly blacklist the icon.
+
+ TODO: See if we can rename this config variable.
-->
<string-array name="config_statusBarIconBlackList" translatable="false">
<item>@*android:string/status_bar_rotate</item>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 8b6543a..77d3f45 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2838,4 +2838,9 @@
<string name="udfps_hbm_enable_command" translatable="false"></string>
<!-- Device-specific payload for disabling the high-brightness mode -->
<string name="udfps_hbm_disable_command" translatable="false"></string>
+
+ <!-- One-Handed Tutorial title [CHAR LIMIT=60] -->
+ <string name="one_handed_tutorial_title">Using one-handed mode</string>
+ <!-- One-Handed Tutorial description [CHAR LIMIT=NONE] -->
+ <string name="one_handed_tutorial_description">To exit, swipe up from the bottom of the screen or tap anywhere above the app</string>
</resources>
diff --git a/packages/SystemUI/res/xml/one_handed_tutorial.xml b/packages/SystemUI/res/xml/one_handed_tutorial.xml
new file mode 100644
index 0000000..dc54caf
--- /dev/null
+++ b/packages/SystemUI/res/xml/one_handed_tutorial.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+ ~ Copyright (C) 2020 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/one_handed_tutorial_layout"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:gravity="center_horizontal | center_vertical"
+ android:background="@android:color/transparent">
+
+ <ImageView
+ android:id="@+id/one_handed_tutorial_image"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="0dp"
+ android:gravity="center_horizontal"
+ android:src="@drawable/one_handed_tutorial"
+ android:scaleType="centerInside" />
+
+ <TextView
+ android:id="@+id/one_handed_tutorial_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="0dp"
+ android:gravity="center_horizontal"
+ android:textAlignment="center"
+ android:fontFamily="google-sans-medium"
+ android:text="@string/one_handed_tutorial_title"
+ android:textSize="16sp"
+ android:textStyle="bold"
+ android:textColor="@android:color/white"/>
+
+ <TextView
+ android:id="@+id/one_handed_tutorial_description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="6dp"
+ android:layout_marginBottom="0dp"
+ android:layout_marginStart="86dp"
+ android:layout_marginEnd="86dp"
+ android:gravity="center_horizontal"
+ android:fontFamily="roboto-regular"
+ android:text="@string/one_handed_tutorial_description"
+ android:textAlignment="center"
+ android:textSize="14sp"
+ android:textStyle="normal"
+ android:alpha="0.7"
+ android:textColor="@android:color/white"/>
+</LinearLayout>
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
index 5869815..521bb8d 100644
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java
@@ -225,7 +225,7 @@
}
Dependency.get(TunerService.class)
- .addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ .addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mIsSubscribedForTunerUpdates = true;
}
@@ -278,8 +278,8 @@
@Override
public void onTuningChanged(String key, String newValue) {
- if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
- ArraySet<String> icons = StatusBarIconController.getIconBlacklist(
+ if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
+ ArraySet<String> icons = StatusBarIconController.getIconHideList(
getContext(), newValue);
setVisibility(icons.contains(mSlotBattery) ? View.GONE : View.VISIBLE);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index 434bf49..bb57219 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -25,6 +25,8 @@
import static android.service.notification.NotificationListenerService.REASON_CANCEL_ALL;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
import static android.service.notification.NotificationListenerService.REASON_GROUP_SUMMARY_CANCELED;
+import static android.service.notification.NotificationStats.DISMISSAL_BUBBLE;
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.View.INVISIBLE;
@@ -100,8 +102,11 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.coordinator.BubbleCoordinator;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.NotificationShadeWindowController;
import com.android.systemui.statusbar.phone.ScrimController;
@@ -277,7 +282,10 @@
* This can happen when an app cancels a bubbled notification or when the user dismisses a
* bubble.
*/
- void removeNotification(@NonNull NotificationEntry entry, int reason);
+ void removeNotification(
+ @NonNull NotificationEntry entry,
+ @NonNull DismissedByUserStats stats,
+ int reason);
/**
* Called when a bubbled notification has changed whether it should be
@@ -543,6 +551,7 @@
}
});
+ // The new pipeline takes care of this as a NotifDismissInterceptor BubbleCoordinator
mNotificationEntryManager.addNotificationRemoveInterceptor(
new NotificationRemoveInterceptor() {
@Override
@@ -551,7 +560,7 @@
NotificationEntry entry,
int dismissReason) {
final boolean isClearAll = dismissReason == REASON_CANCEL_ALL;
- final boolean isUserDimiss = dismissReason == REASON_CANCEL
+ final boolean isUserDismiss = dismissReason == REASON_CANCEL
|| dismissReason == REASON_CLICK;
final boolean isAppCancel = dismissReason == REASON_APP_CANCEL
|| dismissReason == REASON_APP_CANCEL_ALL;
@@ -562,7 +571,7 @@
// previously been dismissed & entry.isRowDismissed would still be true
boolean userRemovedNotif =
(entry != null && entry.isRowDismissed() && !isAppCancel)
- || isClearAll || isUserDimiss || isSummaryCancel;
+ || isClearAll || isUserDismiss || isSummaryCancel;
if (userRemovedNotif) {
return handleDismissalInterception(entry);
@@ -591,8 +600,13 @@
addNotifCallback(new NotifCallback() {
@Override
- public void removeNotification(NotificationEntry entry, int reason) {
- mNotificationEntryManager.performRemoveNotification(entry.getSbn(), reason);
+ public void removeNotification(
+ NotificationEntry entry,
+ DismissedByUserStats dismissedByUserStats,
+ int reason
+ ) {
+ mNotificationEntryManager.performRemoveNotification(entry.getSbn(),
+ dismissedByUserStats, reason);
}
@Override
@@ -612,7 +626,9 @@
mNotificationEntryManager.getActiveNotificationUnfiltered(
mBubbleData.getSummaryKey(groupKey));
if (summary != null) {
- mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
+ mNotificationEntryManager.performRemoveNotification(
+ summary.getSbn(),
+ getDismissedByUserStats(summary, false),
UNDEFINED_DISMISS_REASON);
}
}
@@ -634,7 +650,9 @@
boolean isSummaryThisNotif = summary.getKey().equals(entry.getKey());
if (!isSummaryThisNotif && (summaryChildren == null
|| summaryChildren.isEmpty())) {
- mNotificationEntryManager.performRemoveNotification(summary.getSbn(),
+ mNotificationEntryManager.performRemoveNotification(
+ summary.getSbn(),
+ getDismissedByUserStats(summary, false),
UNDEFINED_DISMISS_REASON);
}
}
@@ -1331,7 +1349,10 @@
// time to actually remove it
for (NotifCallback cb : mCallbacks) {
if (entry != null) {
- cb.removeNotification(entry, REASON_CANCEL);
+ cb.removeNotification(
+ entry,
+ getDismissedByUserStats(entry, true),
+ REASON_CANCEL);
}
}
} else {
@@ -1487,7 +1508,10 @@
} else {
// non-bubbled children can be removed
for (NotifCallback cb : mCallbacks) {
- cb.removeNotification(child, REASON_GROUP_SUMMARY_CANCELED);
+ cb.removeNotification(
+ child,
+ getDismissedByUserStats(child, true),
+ REASON_GROUP_SUMMARY_CANCELED);
}
}
}
@@ -1502,6 +1526,25 @@
}
/**
+ * Gets the DismissedByUserStats used by {@link NotificationEntryManager}.
+ * Will not be necessary when using the new notification pipeline's {@link NotifCollection}.
+ * Instead, this is taken care of by {@link BubbleCoordinator}.
+ */
+ private DismissedByUserStats getDismissedByUserStats(
+ NotificationEntry entry,
+ boolean isVisible) {
+ return new DismissedByUserStats(
+ DISMISSAL_BUBBLE,
+ DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(
+ entry.getKey(),
+ entry.getRanking().getRank(),
+ mNotificationEntryManager.getActiveNotificationsCount(),
+ isVisible,
+ NotificationLogger.getNotificationLocation(entry)));
+ }
+
+ /**
* Updates the visibility of the bubbles based on current state.
* Does not un-bubble, just hides or un-hides.
* Updates stack description for TalkBack focus.
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 075318b..de53168 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -135,6 +135,7 @@
}
override fun onOverlayChanged() {
+ recreatePlayers()
inflateSettingsButton()
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
index c2631c9..1ae54d6 100644
--- a/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/SeekBarObserver.kt
@@ -55,16 +55,16 @@
holder.seekBar.maxHeight = seekBarDefaultMaxHeight
}
- data.elapsedTime?.let {
- holder.seekBar.setProgress(it)
- holder.elapsedTimeView.setText(DateUtils.formatElapsedTime(
- it / DateUtils.SECOND_IN_MILLIS))
- }
-
data.duration?.let {
holder.seekBar.setMax(it)
holder.totalTimeView.setText(DateUtils.formatElapsedTime(
it / DateUtils.SECOND_IN_MILLIS))
}
+
+ data.elapsedTime?.let {
+ holder.seekBar.setProgress(it)
+ holder.elapsedTimeView.setText(DateUtils.formatElapsedTime(
+ it / DateUtils.SECOND_IN_MILLIS))
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
index c581ac6..264ace7 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationCallback.java
@@ -42,4 +42,10 @@
default void onOneHandedAnimationCancel(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
}
+
+ /**
+ * Called when OneHanded animator is updating offset
+ */
+ default void onTutorialAnimationUpdate(int offset) {}
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
index 1926c44..2b07ac3 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedAnimationController.java
@@ -28,7 +28,9 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import javax.inject.Inject;
@@ -121,7 +123,8 @@
private T mEndValue;
private T mCurrentValue;
- private OneHandedAnimationCallback mOneHandedAnimationCallback;
+ private final List<OneHandedAnimationCallback> mOneHandedAnimationCallbacks =
+ new ArrayList<>();
private OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
@@ -142,9 +145,11 @@
@Override
public void onAnimationStart(Animator animation) {
mCurrentValue = mStartValue;
- if (mOneHandedAnimationCallback != null) {
- mOneHandedAnimationCallback.onOneHandedAnimationStart(this);
- }
+ mOneHandedAnimationCallbacks.forEach(
+ (callback) -> {
+ callback.onOneHandedAnimationStart(this);
+ }
+ );
}
@Override
@@ -152,17 +157,21 @@
mCurrentValue = mEndValue;
final SurfaceControl.Transaction tx = newSurfaceControlTransaction();
onEndTransaction(mLeash, tx);
- if (mOneHandedAnimationCallback != null) {
- mOneHandedAnimationCallback.onOneHandedAnimationEnd(tx, this);
- }
+ mOneHandedAnimationCallbacks.forEach(
+ (callback) -> {
+ callback.onOneHandedAnimationEnd(tx, this);
+ }
+ );
}
@Override
public void onAnimationCancel(Animator animation) {
mCurrentValue = mEndValue;
- if (mOneHandedAnimationCallback != null) {
- mOneHandedAnimationCallback.onOneHandedAnimationCancel(this);
- }
+ mOneHandedAnimationCallbacks.forEach(
+ (callback) -> {
+ callback.onOneHandedAnimationCancel(this);
+ }
+ );
}
@Override
@@ -173,6 +182,11 @@
public void onAnimationUpdate(ValueAnimator animation) {
applySurfaceControlTransaction(mLeash, newSurfaceControlTransaction(),
animation.getAnimatedFraction());
+ mOneHandedAnimationCallbacks.forEach(
+ (callback) -> {
+ callback.onTutorialAnimationUpdate(((Rect) mCurrentValue).top);
+ }
+ );
}
void onStartTransaction(SurfaceControl leash, SurfaceControl.Transaction tx) {
@@ -192,9 +206,9 @@
mSurfaceTransactionHelper = helper;
}
- OneHandedTransitionAnimator<T> setOneHandedAnimationCallback(
+ OneHandedTransitionAnimator<T> setOneHandedAnimationCallbacks(
OneHandedAnimationCallback callback) {
- mOneHandedAnimationCallback = callback;
+ mOneHandedAnimationCallbacks.add(callback);
return this;
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
index c0b9258..8550959 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -82,6 +82,7 @@
private OneHandedAnimationController mAnimationController;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
+ private OneHandedTutorialHandler mTutorialHandler;
private List<OneHandedTransitionCallback> mTransitionCallbacks = new ArrayList<>();
@VisibleForTesting
@@ -148,7 +149,8 @@
@Inject
public OneHandedDisplayAreaOrganizer(Context context,
DisplayController displayController,
- OneHandedAnimationController animationController) {
+ OneHandedAnimationController animationController,
+ OneHandedTutorialHandler tutorialHandler) {
mUpdateHandler = new Handler(OneHandedThread.get().getLooper(), mUpdateCallback);
mAnimationController = animationController;
mDisplayController = displayController;
@@ -157,6 +159,7 @@
mEnterExitAnimationDurationMs = context.getResources().getInteger(
com.android.systemui.R.integer.config_one_handed_translate_animation_duration);
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
+ mTutorialHandler = tutorialHandler;
}
@Override
@@ -272,7 +275,8 @@
mAnimationController.getAnimator(leash, fromBounds, toBounds);
if (animator != null) {
animator.setTransitionDirection(direction)
- .setOneHandedAnimationCallback(mOneHandedAnimationCallback)
+ .setOneHandedAnimationCallbacks(mOneHandedAnimationCallback)
+ .setOneHandedAnimationCallbacks(mTutorialHandler.getAnimationCallback())
.setDuration(durationMs)
.start();
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
index 71c5f80..ded3861 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedGestureHandler.java
@@ -53,8 +53,8 @@
*/
@Singleton
public class OneHandedGestureHandler implements OneHandedTransitionCallback,
- NavigationModeController.ModeChangedListener,
- DisplayChangeController.OnDisplayChangingListener {
+ NavigationModeController.ModeChangedListener,
+ DisplayChangeController.OnDisplayChangingListener {
private static final String TAG = "OneHandedGestureHandler";
private static final boolean DEBUG_GESTURE = false;
@@ -87,8 +87,8 @@
* Constructor of OneHandedGestureHandler, we only handle the gesture of
* {@link Display#DEFAULT_DISPLAY}
*
- * @param context {@link Context}
- * @param displayController {@link DisplayController}
+ * @param context {@link Context}
+ * @param displayController {@link DisplayController}
* @param navigationModeController {@link NavigationModeController}
*/
@Inject
@@ -103,7 +103,7 @@
mDragDistThreshold = context.getResources().getDimensionPixelSize(
R.dimen.gestures_onehanded_drag_threshold);
final float slop = ViewConfiguration.get(context).getScaledTouchSlop();
- mSquaredSlop = slop * slop;
+ mSquaredSlop = slop * slop;
updateIsEnabled();
}
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
index 70a81aa..51e5875 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedManagerImpl.java
@@ -57,6 +57,7 @@
private final OneHandedGestureHandler mGestureHandler;
private final OneHandedTimeoutHandler mTimeoutHandler;
private final OneHandedTouchHandler mTouchHandler;
+ private final OneHandedTutorialHandler mTutorialHandler;
private final SysUiState mSysUiFlagContainer;
private Context mContext;
@@ -107,6 +108,7 @@
DisplayController displayController,
OneHandedDisplayAreaOrganizer displayAreaOrganizer,
OneHandedTouchHandler touchHandler,
+ OneHandedTutorialHandler tutorialHandler,
OneHandedGestureHandler gestureHandler,
SysUiState sysUiState) {
mContext = context;
@@ -120,6 +122,7 @@
context.getContentResolver());
mTimeoutHandler = OneHandedTimeoutHandler.get();
mTouchHandler = touchHandler;
+ mTutorialHandler = tutorialHandler;
mGestureHandler = gestureHandler;
updateOneHandedEnabled();
setupGestures();
@@ -230,6 +233,7 @@
mDisplayAreaOrganizer.registerTransitionCallback(mTransitionCallback);
mDisplayAreaOrganizer.registerTransitionCallback(mTouchHandler);
mDisplayAreaOrganizer.registerTransitionCallback(mGestureHandler);
+ mDisplayAreaOrganizer.registerTransitionCallback(mTutorialHandler);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
index 3d4338c..d616a3a 100644
--- a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTouchHandler.java
@@ -58,6 +58,7 @@
OneHandedTouchEventCallback mTouchEventCallback;
private boolean mIsEnabled;
+ private boolean mIsOnStopTransitioning;
private boolean mIsInOutsideRegion;
@Inject
@@ -96,8 +97,9 @@
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
mTimeoutHandler.resetTimer();
- if (mIsInOutsideRegion) {
+ if (mIsInOutsideRegion && !mIsOnStopTransitioning) {
mTouchEventCallback.onStop();
+ mIsOnStopTransitioning = true;
}
// Reset flag for next operation
mIsInOutsideRegion = false;
@@ -146,6 +148,7 @@
@Override
public void onStopFinished(Rect bounds) {
mLastUpdatedBounds.set(bounds);
+ mIsOnStopTransitioning = false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
new file mode 100644
index 0000000..8a67da5
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/onehanded/OneHandedTutorialHandler.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.onehanded;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+
+import com.android.systemui.Dumpable;
+import com.android.systemui.R;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+/**
+ * Manages the user tutorial handling for One Handed operations, including animations synchronized
+ * with one-handed translation.
+ * Refer {@link OneHandedGestureHandler} and {@link OneHandedTouchHandler} to see start and stop
+ * one handed gesture
+ */
+@Singleton
+public class OneHandedTutorialHandler implements OneHandedTransitionCallback, Dumpable {
+ private static final String TAG = "OneHandedTutorialHandler";
+ private final Rect mLastUpdatedBounds = new Rect();
+ private final WindowManager mWindowManager;
+
+ private View mTutorialView;
+ private Point mDisplaySize = new Point();
+ private Handler mUpdateHandler;
+
+ /**
+ * Container of the tutorial panel showing at outside region when one handed starting
+ */
+ private ViewGroup mTargetViewContainer;
+ private int mTutorialAreaHeight;
+
+ private final OneHandedAnimationCallback mAnimationCallback = new OneHandedAnimationCallback() {
+ @Override
+ public void onTutorialAnimationUpdate(int offset) {
+ mUpdateHandler.post(() -> onAnimationUpdate(offset));
+ }
+ };
+
+ @Inject
+ public OneHandedTutorialHandler(Context context) {
+ context.getDisplay().getRealSize(mDisplaySize);
+ mUpdateHandler = new Handler();
+ mWindowManager = context.getSystemService(WindowManager.class);
+ mTargetViewContainer = new FrameLayout(context);
+ mTargetViewContainer.setClipChildren(false);
+ mTutorialAreaHeight = Math.round(mDisplaySize.y * context.getResources().getFraction(
+ R.fraction.config_one_handed_offset, 1, 1));
+ mTutorialView = LayoutInflater.from(context).inflate(R.xml.one_handed_tutorial, null);
+ mTargetViewContainer.addView(mTutorialView);
+ createOrUpdateTutorialTarget();
+ }
+
+ @Override
+ public void onStartFinished(Rect bounds) {
+ mUpdateHandler.post(() -> updateFinished(View.VISIBLE, 0f));
+ }
+
+ @Override
+ public void onStopFinished(Rect bounds) {
+ mUpdateHandler.post(() -> updateFinished(
+ View.INVISIBLE, -mTargetViewContainer.getHeight()));
+ }
+
+ private void updateFinished(int visible, float finalPosition) {
+ mTargetViewContainer.setVisibility(visible);
+ mTargetViewContainer.setTranslationY(finalPosition);
+ }
+
+ /**
+ * Adds the tutorial target view to the WindowManager and update its layout, so it's ready
+ * to be animated in.
+ */
+ private void createOrUpdateTutorialTarget() {
+ mUpdateHandler.post(() -> {
+ if (!mTargetViewContainer.isAttachedToWindow()) {
+ mTargetViewContainer.setVisibility(View.INVISIBLE);
+
+ try {
+ mWindowManager.addView(mTargetViewContainer, getTutorialTargetLayoutParams());
+ } catch (IllegalStateException e) {
+ // This shouldn't happen, but if the target is already added, just update its
+ // layout params.
+ mWindowManager.updateViewLayout(
+ mTargetViewContainer, getTutorialTargetLayoutParams());
+ }
+ } else {
+ mWindowManager.updateViewLayout(mTargetViewContainer,
+ getTutorialTargetLayoutParams());
+ }
+ });
+ }
+
+ OneHandedAnimationCallback getAnimationCallback() {
+ return mAnimationCallback;
+ }
+
+ /**
+ * Returns layout params for the dismiss target, using the latest display metrics.
+ */
+ private WindowManager.LayoutParams getTutorialTargetLayoutParams() {
+ final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
+ mDisplaySize.x, mTutorialAreaHeight, 0, 0,
+ WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+ | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ lp.gravity = Gravity.TOP | Gravity.LEFT;
+ lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
+ lp.setFitInsetsTypes(0 /* types */);
+ lp.setTitle("one-handed-tutorial-overlay");
+
+ return lp;
+ }
+
+ @Override
+ public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
+ final String innerPrefix = " ";
+ pw.println(TAG + "states: ");
+ pw.print(innerPrefix + "mLastUpdatedBounds=");
+ pw.println(mLastUpdatedBounds);
+ }
+
+ private void onAnimationUpdate(float value) {
+ mTargetViewContainer.setVisibility(View.VISIBLE);
+ mTargetViewContainer.setTransitionGroup(true);
+ mTargetViewContainer.setTranslationY(value - mTargetViewContainer.getHeight());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
index eb794a8..c64fc50 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BatterySaverTile.java
@@ -130,6 +130,7 @@
: mPowerSave ? Tile.STATE_ACTIVE : Tile.STATE_INACTIVE;
state.icon = mIcon;
state.label = mContext.getString(R.string.battery_detail_switch_title);
+ state.secondaryLabel = "";
state.contentDescription = state.label;
state.value = mPowerSave;
state.expandedAccessibilityClassName = Switch.class.getName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/DismissRunnable.java b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
similarity index 65%
rename from packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/DismissRunnable.java
rename to packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
index 4331142..9d05843 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/DismissRunnable.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContentResolverProvider.kt
@@ -14,17 +14,11 @@
* limitations under the License.
*/
-package com.android.systemui.statusbar.notification.row.dagger;
+package com.android.systemui.settings
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import android.content.ContentResolver
-import java.lang.annotation.Documented;
-import java.lang.annotation.Retention;
+interface CurrentUserContentResolverProvider {
-import javax.inject.Qualifier;
-
-@Qualifier
-@Documented
-@Retention(RUNTIME)
-public @interface DismissRunnable {
-}
+ val currentUserContentResolver: ContentResolver
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
index 825a7f3..d7c4caaa 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
+++ b/packages/SystemUI/src/com/android/systemui/settings/CurrentUserContextTracker.kt
@@ -16,6 +16,7 @@
package com.android.systemui.settings
+import android.content.ContentResolver
import android.content.Context
import android.os.UserHandle
import androidx.annotation.VisibleForTesting
@@ -31,7 +32,7 @@
class CurrentUserContextTracker internal constructor(
private val sysuiContext: Context,
broadcastDispatcher: BroadcastDispatcher
-) {
+) : CurrentUserContentResolverProvider {
private val userTracker: CurrentUserTracker
private var initialized = false
@@ -44,6 +45,9 @@
return _curUserContext!!
}
+ override val currentUserContentResolver: ContentResolver
+ get() = currentUserContext.contentResolver
+
init {
userTracker = object : CurrentUserTracker(broadcastDispatcher) {
override fun onUserSwitched(newUserId: Int) {
@@ -54,8 +58,8 @@
fun initialize() {
initialized = true
- _curUserContext = makeUserContext(userTracker.currentUserId)
userTracker.startTracking()
+ _curUserContext = makeUserContext(userTracker.currentUserId)
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
index 2c5c3ce..eb5bd5c 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/dagger/SettingsModule.java
@@ -19,10 +19,12 @@
import android.content.Context;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.settings.CurrentUserContentResolverProvider;
import com.android.systemui.settings.CurrentUserContextTracker;
import javax.inject.Singleton;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -30,7 +32,7 @@
* Dagger Module for classes found within the com.android.systemui.settings package.
*/
@Module
-public interface SettingsModule {
+public abstract class SettingsModule {
/**
* Provides and initializes a CurrentUserContextTracker
@@ -45,4 +47,9 @@
tracker.initialize();
return tracker;
}
+
+ @Binds
+ @Singleton
+ abstract CurrentUserContentResolverProvider bindCurrentUserContentResolverTracker(
+ CurrentUserContextTracker tracker);
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
index eb72312..5301bbd 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/Divider.java
@@ -16,44 +16,20 @@
package com.android.systemui.stackdivider;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
-import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
-import static android.view.Display.DEFAULT_DISPLAY;
-
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import android.app.ActivityManager;
-import android.app.ActivityTaskManager;
import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.Rect;
-import android.os.Handler;
-import android.provider.Settings;
-import android.util.Slog;
-import android.view.LayoutInflater;
-import android.view.View;
import android.window.WindowContainerToken;
-import android.window.WindowContainerTransaction;
-import android.window.WindowOrganizer;
-import com.android.internal.policy.DividerSnapAlgorithm;
-import com.android.systemui.R;
import com.android.systemui.SystemUI;
import com.android.systemui.recents.Recents;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.wm.shell.common.DisplayChangeController;
-import com.android.wm.shell.common.DisplayController;
-import com.android.wm.shell.common.DisplayImeController;
-import com.android.wm.shell.common.DisplayLayout;
-import com.android.wm.shell.common.SystemWindows;
-import com.android.wm.shell.common.TransactionPool;
import java.io.FileDescriptor;
import java.io.PrintWriter;
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.Optional;
import java.util.function.Consumer;
@@ -65,510 +41,110 @@
* Controls the docked stack divider.
*/
@Singleton
-public class Divider extends SystemUI implements DividerView.DividerCallbacks,
- DisplayController.OnDisplaysChangedListener {
- private static final String TAG = "Divider";
-
- static final boolean DEBUG = false;
-
- static final int DEFAULT_APP_TRANSITION_DURATION = 336;
-
+public class Divider extends SystemUI {
+ private final KeyguardStateController mKeyguardStateController;
+ private final DividerController mDividerController;
private final Optional<Lazy<Recents>> mRecentsOptionalLazy;
- private DividerWindowManager mWindowManager;
- private DividerView mView;
- private final DividerState mDividerState = new DividerState();
- private boolean mVisible = false;
- private boolean mMinimized = false;
- private boolean mAdjustedForIme = false;
- private boolean mHomeStackResizable = false;
- private ForcedResizableInfoActivityController mForcedResizableController;
- private SystemWindows mSystemWindows;
- private DisplayController mDisplayController;
- private DisplayImeController mImeController;
- final TransactionPool mTransactionPool;
-
- // Keeps track of real-time split geometry including snap positions and ime adjustments
- private SplitDisplayLayout mSplitLayout;
-
- // Transient: this contains the layout calculated for a new rotation requested by WM. This is
- // kept around so that we can wait for a matching configuration change and then use the exact
- // layout that we sent back to WM.
- private SplitDisplayLayout mRotateSplitLayout;
-
- private Handler mHandler;
- private KeyguardStateController mKeyguardStateController;
-
- private WindowManagerProxy mWindowManagerProxy;
-
- private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
- new ArrayList<>();
-
- private SplitScreenTaskOrganizer mSplits = new SplitScreenTaskOrganizer(this);
-
- private DisplayChangeController.OnDisplayChangingListener mRotationController =
- (display, fromRotation, toRotation, wct) -> {
- if (!mSplits.isSplitScreenSupported() || mWindowManagerProxy == null) {
- return;
- }
- WindowContainerTransaction t = new WindowContainerTransaction();
- DisplayLayout displayLayout =
- new DisplayLayout(mDisplayController.getDisplayLayout(display));
- SplitDisplayLayout sdl = new SplitDisplayLayout(mContext, displayLayout, mSplits);
- sdl.rotateTo(toRotation);
- mRotateSplitLayout = sdl;
- final int position = isDividerVisible()
- ? (mMinimized ? mView.mSnapTargetBeforeMinimized.position
- : mView.getCurrentPosition())
- // snap resets to middle target when not in split-mode
- : sdl.getSnapAlgorithm().getMiddleTarget().position;
- DividerSnapAlgorithm snap = sdl.getSnapAlgorithm();
- final DividerSnapAlgorithm.SnapTarget target =
- snap.calculateNonDismissingSnapTarget(position);
- sdl.resizeSplits(target.position, t);
-
- if (isSplitActive() && mHomeStackResizable) {
- WindowManagerProxy.applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
- }
- if (mWindowManagerProxy.queueSyncTransactionIfWaiting(t)) {
- // Because sync transactions are serialized, its possible for an "older"
- // bounds-change to get applied after a screen rotation. In that case, we
- // want to actually defer on that rather than apply immediately. Of course,
- // this means that the bounds may not change until after the rotation so
- // the user might see some artifacts. This should be rare.
- Slog.w(TAG, "Screen rotated while other operations were pending, this may"
- + " result in some graphical artifacts.");
- } else {
- wct.merge(t, true /* transfer */);
- }
- };
-
- private final DividerImeController mImePositionProcessor;
-
- private TaskStackChangeListener mActivityRestartListener = new TaskStackChangeListener() {
- @Override
- public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
- boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
- if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
- != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY || !mSplits.isSplitScreenSupported()) {
- return;
- }
-
- if (isMinimized()) {
- onUndockingTask();
- }
- }
- };
-
- public Divider(Context context, Optional<Lazy<Recents>> recentsOptionalLazy,
- DisplayController displayController, SystemWindows systemWindows,
- DisplayImeController imeController, Handler handler,
- KeyguardStateController keyguardStateController, TransactionPool transactionPool) {
+ Divider(Context context, DividerController dividerController,
+ KeyguardStateController keyguardStateController,
+ Optional<Lazy<Recents>> recentsOptionalLazy) {
super(context);
- mDisplayController = displayController;
- mSystemWindows = systemWindows;
- mImeController = imeController;
- mHandler = handler;
+ mDividerController = dividerController;
mKeyguardStateController = keyguardStateController;
mRecentsOptionalLazy = recentsOptionalLazy;
- mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
- mTransactionPool = transactionPool;
- mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler);
- mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler);
}
@Override
public void start() {
- mWindowManager = new DividerWindowManager(mSystemWindows);
- mDisplayController.addDisplayWindowListener(this);
+ mDividerController.start();
// Hide the divider when keyguard is showing. Even though keyguard/statusbar is above
// everything, it is actually transparent except for notifications, so we still need to
// hide any surfaces that are below it.
// TODO(b/148906453): Figure out keyguard dismiss animation for divider view.
mKeyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
- public void onUnlockedChanged() {
-
- }
-
- @Override
public void onKeyguardShowingChanged() {
- if (!isSplitActive() || mView == null) {
- return;
- }
- mView.setHidden(mKeyguardStateController.isShowing());
- if (!mKeyguardStateController.isShowing()) {
- mImePositionProcessor.updateAdjustForIme();
- }
- }
-
- @Override
- public void onKeyguardFadingAwayChanged() {
-
+ mDividerController.onKeyguardShowingChanged(mKeyguardStateController.isShowing());
}
});
// Don't initialize the divider or anything until we get the default display.
- }
- @Override
- public void onDisplayAdded(int displayId) {
- if (displayId != DEFAULT_DISPLAY) {
- return;
- }
- mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
- mDisplayController.getDisplayLayout(displayId), mSplits);
- mImeController.addPositionProcessor(mImePositionProcessor);
- mDisplayController.addDisplayChangingController(mRotationController);
- if (!ActivityTaskManager.supportsSplitScreenMultiWindow(mContext)) {
- removeDivider();
- return;
- }
- try {
- mSplits.init();
- // Set starting tile bounds based on middle target
- final WindowContainerTransaction tct = new WindowContainerTransaction();
- int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
- mSplitLayout.resizeSplits(midPos, tct);
- WindowOrganizer.applyTransaction(tct);
- } catch (Exception e) {
- Slog.e(TAG, "Failed to register docked stack listener", e);
- removeDivider();
- return;
- }
- ActivityManagerWrapper.getInstance().registerTaskStackListener(mActivityRestartListener);
- }
+ ActivityManagerWrapper.getInstance().registerTaskStackListener(
+ new TaskStackChangeListener() {
+ @Override
+ public void onActivityRestartAttempt(ActivityManager.RunningTaskInfo task,
+ boolean homeTaskVisible, boolean clearedTask, boolean wasVisible) {
+ if (!wasVisible || task.configuration.windowConfiguration.getWindowingMode()
+ != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
+ || !mDividerController.isSplitScreenSupported()) {
+ return;
+ }
- @Override
- public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
- if (displayId != DEFAULT_DISPLAY || !mSplits.isSplitScreenSupported()) {
- return;
- }
- mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
- mDisplayController.getDisplayLayout(displayId), mSplits);
- if (mRotateSplitLayout == null) {
- int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
- final WindowContainerTransaction tct = new WindowContainerTransaction();
- mSplitLayout.resizeSplits(midPos, tct);
- WindowOrganizer.applyTransaction(tct);
- } else if (mSplitLayout.mDisplayLayout.rotation()
- == mRotateSplitLayout.mDisplayLayout.rotation()) {
- mSplitLayout.mPrimary = new Rect(mRotateSplitLayout.mPrimary);
- mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
- mRotateSplitLayout = null;
- }
- if (isSplitActive()) {
- update(newConfig);
- }
- }
-
- Handler getHandler() {
- return mHandler;
- }
-
- public DividerView getView() {
- return mView;
- }
-
- public boolean isMinimized() {
- return mMinimized;
- }
-
- public boolean isHomeStackResizable() {
- return mHomeStackResizable;
- }
-
- /** {@code true} if this is visible */
- public boolean isDividerVisible() {
- return mView != null && mView.getVisibility() == View.VISIBLE;
- }
-
- /**
- * This indicates that at-least one of the splits has content. This differs from
- * isDividerVisible because the divider is only visible once *everything* is in split mode
- * while this only cares if some things are (eg. while entering/exiting as well).
- */
- private boolean isSplitActive() {
- return mSplits.mPrimary != null && mSplits.mSecondary != null
- && (mSplits.mPrimary.topActivityType != ACTIVITY_TYPE_UNDEFINED
- || mSplits.mSecondary.topActivityType != ACTIVITY_TYPE_UNDEFINED);
- }
-
- private void addDivider(Configuration configuration) {
- Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
- mView = (DividerView)
- LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
- DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
- mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout,
- mImePositionProcessor, mWindowManagerProxy);
- mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
- mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
- final int size = dctx.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.docked_stack_divider_thickness);
- final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
- final int width = landscape ? size : displayLayout.width();
- final int height = landscape ? displayLayout.height() : size;
- mWindowManager.add(mView, width, height, mContext.getDisplayId());
- }
-
- private void removeDivider() {
- if (mView != null) {
- mView.onDividerRemoved();
- }
- mWindowManager.remove();
- }
-
- private void update(Configuration configuration) {
- final boolean isDividerHidden = mView != null && mKeyguardStateController.isShowing();
-
- removeDivider();
- addDivider(configuration);
-
- if (mMinimized) {
- mView.setMinimizedDockStack(true, mHomeStackResizable, null /* transaction */);
- updateTouchable();
- }
- mView.setHidden(isDividerHidden);
- }
-
- void onTaskVanished() {
- mHandler.post(this::removeDivider);
- }
-
- private void updateVisibility(final boolean visible) {
- if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
- if (mVisible != visible) {
- mVisible = visible;
- mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
-
- if (visible) {
- mView.enterSplitMode(mHomeStackResizable);
- // Update state because animations won't finish.
- mWindowManagerProxy.runInSync(
- t -> mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, t));
-
- } else {
- mView.exitSplitMode();
- mWindowManagerProxy.runInSync(
- t -> mView.setMinimizedDockStack(false, mHomeStackResizable, t));
- }
- // Notify existence listeners
- synchronized (mDockedStackExistsListeners) {
- mDockedStackExistsListeners.removeIf(wf -> {
- Consumer<Boolean> l = wf.get();
- if (l != null) l.accept(visible);
- return l == null;
- });
- }
- }
- }
-
- /** Switch to minimized state if appropriate */
- public void setMinimized(final boolean minimized) {
- if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
- mHandler.post(() -> {
- if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
- if (!mVisible) {
- return;
- }
- setHomeMinimized(minimized, mHomeStackResizable);
- });
- }
-
- private void setHomeMinimized(final boolean minimized, boolean homeStackResizable) {
- if (DEBUG) {
- Slog.d(TAG, "setHomeMinimized min:" + mMinimized + "->" + minimized + " hrsz:"
- + mHomeStackResizable + "->" + homeStackResizable
- + " split:" + isDividerVisible());
- }
- WindowContainerTransaction wct = new WindowContainerTransaction();
- final boolean minimizedChanged = mMinimized != minimized;
- // Update minimized state
- if (minimizedChanged) {
- mMinimized = minimized;
- }
- // Always set this because we could be entering split when mMinimized is already true
- wct.setFocusable(mSplits.mPrimary.token, !mMinimized);
- boolean onlyFocusable = true;
-
- // Update home-stack resizability
- final boolean homeResizableChanged = mHomeStackResizable != homeStackResizable;
- if (homeResizableChanged) {
- mHomeStackResizable = homeStackResizable;
- if (isDividerVisible()) {
- WindowManagerProxy.applyHomeTasksMinimized(
- mSplitLayout, mSplits.mSecondary.token, wct);
- onlyFocusable = false;
- }
- }
-
- // Sync state to DividerView if it exists.
- if (mView != null) {
- final int displayId = mView.getDisplay() != null
- ? mView.getDisplay().getDisplayId() : DEFAULT_DISPLAY;
- // pause ime here (before updateMinimizedDockedStack)
- if (mMinimized) {
- mImePositionProcessor.pause(displayId);
- }
- if (minimizedChanged || homeResizableChanged) {
- // This conflicts with IME adjustment, so only call it when things change.
- mView.setMinimizedDockStack(minimized, getAnimDuration(), homeStackResizable);
- }
- if (!mMinimized) {
- // afterwards so it can end any animations started in view
- mImePositionProcessor.resume(displayId);
- }
- }
- updateTouchable();
- if (onlyFocusable) {
- // If we are only setting focusability, a sync transaction isn't necessary (in fact it
- // can interrupt other animations), so see if it can be submitted on pending instead.
- if (!mSplits.mDivider.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
- WindowOrganizer.applyTransaction(wct);
- }
- } else {
- mWindowManagerProxy.applySyncTransaction(wct);
- }
- }
-
- void setAdjustedForIme(boolean adjustedForIme) {
- if (mAdjustedForIme == adjustedForIme) {
- return;
- }
- mAdjustedForIme = adjustedForIme;
- updateTouchable();
- }
-
- private void updateTouchable() {
- mWindowManager.setTouchable(!mAdjustedForIme);
- }
-
- /**
- * Workaround for b/62528361, at the time recents has drawn, it may happen before a
- * configuration change to the Divider, and internally, the event will be posted to the
- * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
- * register the event handler here and proxy the event to the current DividerView.
- */
- public void onRecentsDrawn() {
- if (mView != null) {
- mView.onRecentsDrawn();
- }
- }
-
- public void onUndockingTask() {
- if (mView != null) {
- mView.onUndockingTask();
- }
- }
-
- public void onDockedFirstAnimationFrame() {
- if (mView != null) {
- mView.onDockedFirstAnimationFrame();
- }
- }
-
- public void onDockedTopTask() {
- if (mView != null) {
- mView.onDockedTopTask();
- }
- }
-
- public void onAppTransitionFinished() {
- if (mView == null) {
- return;
- }
- mForcedResizableController.onAppTransitionFinished();
- }
-
- @Override
- public void onDraggingStart() {
- mForcedResizableController.onDraggingStart();
- }
-
- @Override
- public void onDraggingEnd() {
- mForcedResizableController.onDraggingEnd();
- }
-
- @Override
- public void growRecents() {
- mRecentsOptionalLazy.ifPresent(recentsLazy -> recentsLazy.get().growRecents());
+ if (mDividerController.isMinimized()) {
+ onUndockingTask();
+ }
+ }
+ }
+ );
}
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.print(" mVisible="); pw.println(mVisible);
- pw.print(" mMinimized="); pw.println(mMinimized);
- pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme);
+ mDividerController.dump(pw);
}
- long getAnimDuration() {
- float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(),
- Settings.Global.TRANSITION_ANIMATION_SCALE,
- mContext.getResources().getFloat(
- com.android.internal.R.dimen
- .config_appTransitionAnimationDurationScaleDefault));
- final long transitionDuration = DEFAULT_APP_TRANSITION_DURATION;
- return (long) (transitionDuration * transitionScale);
+ /** Switch to minimized state if appropriate. */
+ public void setMinimized(final boolean minimized) {
+ mDividerController.setMinimized(minimized);
}
- /** Register a listener that gets called whenever the existence of the divider changes */
- public void registerInSplitScreenListener(Consumer<Boolean> listener) {
- listener.accept(isDividerVisible());
- synchronized (mDockedStackExistsListeners) {
- mDockedStackExistsListeners.add(new WeakReference<>(listener));
- }
+ public boolean isMinimized() {
+ return mDividerController.isMinimized();
}
- void startEnterSplit() {
- update(mDisplayController.getDisplayContext(
- mContext.getDisplayId()).getResources().getConfiguration());
- // Set resizable directly here because applyEnterSplit already resizes home stack.
- mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
+ public boolean isHomeStackResizable() {
+ return mDividerController.isHomeStackResizable();
}
- void startDismissSplit() {
- mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, true /* dismissOrMaximize */);
- updateVisibility(false /* visible */);
- mMinimized = false;
- removeDivider();
- mImePositionProcessor.reset();
+ /** Callback for undocking task. */
+ public void onUndockingTask() {
+ mDividerController.onUndockingTask();
}
- void ensureMinimizedSplit() {
- setHomeMinimized(true /* minimized */, mHomeStackResizable);
- if (mView != null && !isDividerVisible()) {
- // Wasn't in split-mode yet, so enter now.
- if (DEBUG) {
- Slog.d(TAG, " entering split mode with minimized=true");
- }
- updateVisibility(true /* visible */);
- }
+ public void onRecentsDrawn() {
+ mDividerController.onRecentsDrawn(() -> mRecentsOptionalLazy.ifPresent(
+ recentsLazy -> recentsLazy.get().growRecents()));
}
- void ensureNormalSplit() {
- setHomeMinimized(false /* minimized */, mHomeStackResizable);
- if (mView != null && !isDividerVisible()) {
- // Wasn't in split-mode, so enter now.
- if (DEBUG) {
- Slog.d(TAG, " enter split mode unminimized ");
- }
- updateVisibility(true /* visible */);
- }
+ public void onDockedFirstAnimationFrame() {
+ mDividerController.onDockedFirstAnimationFrame();
}
- SplitDisplayLayout getSplitLayout() {
- return mSplitLayout;
+ public void onDockedTopTask() {
+ mDividerController.onDockedTopTask();
}
- WindowManagerProxy getWmProxy() {
- return mWindowManagerProxy;
+ public void onAppTransitionFinished() {
+ mDividerController.onAppTransitionFinished();
+ }
+
+ public DividerView getView() {
+ return mDividerController.getDividerView();
}
/** @return the container token for the secondary split root task. */
public WindowContainerToken getSecondaryRoot() {
- if (mSplits == null || mSplits.mSecondary == null) {
- return null;
- }
- return mSplits.mSecondary.token;
+ return mDividerController.getSecondaryRoot();
+ }
+
+ /** Register a listener that gets called whenever the existence of the divider changes */
+ public void registerInSplitScreenListener(Consumer<Boolean> listener) {
+ mDividerController.registerInSplitScreenListener(listener);
+ }
+
+ /** {@code true} if this is visible */
+ public boolean isDividerVisible() {
+ return mDividerController.isDividerVisible();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
new file mode 100644
index 0000000..14fc157
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerController.java
@@ -0,0 +1,516 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.stackdivider;
+
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
+import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.Display.DEFAULT_DISPLAY;
+
+import android.app.ActivityTaskManager;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.provider.Settings;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+import android.window.WindowOrganizer;
+
+import com.android.internal.policy.DividerSnapAlgorithm;
+import com.android.systemui.R;
+import com.android.wm.shell.common.DisplayChangeController;
+import com.android.wm.shell.common.DisplayController;
+import com.android.wm.shell.common.DisplayImeController;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SystemWindows;
+import com.android.wm.shell.common.TransactionPool;
+
+import java.io.PrintWriter;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.function.Consumer;
+
+/**
+ * Controls the docked stack divider.
+ */
+public class DividerController implements DividerView.DividerCallbacks,
+ DisplayController.OnDisplaysChangedListener {
+ static final boolean DEBUG = false;
+ private static final String TAG = "Divider";
+
+ static final int DEFAULT_APP_TRANSITION_DURATION = 336;
+
+ private DividerWindowManager mWindowManager;
+ private DividerView mView;
+ private final DividerState mDividerState = new DividerState();
+ private boolean mVisible = false;
+ private boolean mMinimized = false;
+ private boolean mAdjustedForIme = false;
+ private boolean mHomeStackResizable = false;
+ private ForcedResizableInfoActivityController mForcedResizableController;
+ private SystemWindows mSystemWindows;
+ private DisplayController mDisplayController;
+ private DisplayImeController mImeController;
+ final TransactionPool mTransactionPool;
+
+ // Keeps track of real-time split geometry including snap positions and ime adjustments
+ private SplitDisplayLayout mSplitLayout;
+
+ // Transient: this contains the layout calculated for a new rotation requested by WM. This is
+ // kept around so that we can wait for a matching configuration change and then use the exact
+ // layout that we sent back to WM.
+ private SplitDisplayLayout mRotateSplitLayout;
+
+ private final Handler mHandler;
+ private final WindowManagerProxy mWindowManagerProxy;
+
+ private final ArrayList<WeakReference<Consumer<Boolean>>> mDockedStackExistsListeners =
+ new ArrayList<>();
+
+ private final SplitScreenTaskOrganizer mSplits;
+ private final DisplayChangeController.OnDisplayChangingListener mRotationController;
+ private final DividerImeController mImePositionProcessor;
+ private final Context mContext;
+ private boolean mIsKeyguardShowing;
+
+ public DividerController(Context context,
+ DisplayController displayController, SystemWindows systemWindows,
+ DisplayImeController imeController, Handler handler, TransactionPool transactionPool) {
+ mContext = context;
+ mDisplayController = displayController;
+ mSystemWindows = systemWindows;
+ mImeController = imeController;
+ mHandler = handler;
+ mForcedResizableController = new ForcedResizableInfoActivityController(context, this);
+ mTransactionPool = transactionPool;
+ mWindowManagerProxy = new WindowManagerProxy(mTransactionPool, mHandler);
+ mSplits = new SplitScreenTaskOrganizer(this);
+ mImePositionProcessor = new DividerImeController(mSplits, mTransactionPool, mHandler);
+ mRotationController =
+ (display, fromRotation, toRotation, wct) -> {
+ if (!mSplits.isSplitScreenSupported() || mWindowManagerProxy == null) {
+ return;
+ }
+ WindowContainerTransaction t = new WindowContainerTransaction();
+ DisplayLayout displayLayout =
+ new DisplayLayout(mDisplayController.getDisplayLayout(display));
+ SplitDisplayLayout sdl =
+ new SplitDisplayLayout(mContext, displayLayout, mSplits);
+ sdl.rotateTo(toRotation);
+ mRotateSplitLayout = sdl;
+ final int position = isDividerVisible()
+ ? (mMinimized ? mView.mSnapTargetBeforeMinimized.position
+ : mView.getCurrentPosition())
+ // snap resets to middle target when not in split-mode
+ : sdl.getSnapAlgorithm().getMiddleTarget().position;
+ DividerSnapAlgorithm snap = sdl.getSnapAlgorithm();
+ final DividerSnapAlgorithm.SnapTarget target =
+ snap.calculateNonDismissingSnapTarget(position);
+ sdl.resizeSplits(target.position, t);
+
+ if (isSplitActive() && mHomeStackResizable) {
+ WindowManagerProxy
+ .applyHomeTasksMinimized(sdl, mSplits.mSecondary.token, t);
+ }
+ if (mWindowManagerProxy.queueSyncTransactionIfWaiting(t)) {
+ // Because sync transactions are serialized, its possible for an "older"
+ // bounds-change to get applied after a screen rotation. In that case, we
+ // want to actually defer on that rather than apply immediately. Of course,
+ // this means that the bounds may not change until after the rotation so
+ // the user might see some artifacts. This should be rare.
+ Slog.w(TAG, "Screen rotated while other operations were pending, this may"
+ + " result in some graphical artifacts.");
+ } else {
+ wct.merge(t, true /* transfer */);
+ }
+ };
+ }
+
+ /** Inits the divider service. */
+ public void start() {
+ mWindowManager = new DividerWindowManager(mSystemWindows);
+ mDisplayController.addDisplayWindowListener(this);
+ // Don't initialize the divider or anything until we get the default display.
+ }
+
+ /** Returns {@code true} if split screen is supported on the device. */
+ public boolean isSplitScreenSupported() {
+ return mSplits.isSplitScreenSupported();
+ }
+
+ /** Called when keyguard showing state changed. */
+ public void onKeyguardShowingChanged(boolean isShowing) {
+ if (!isSplitActive() || mView == null) {
+ return;
+ }
+ mView.setHidden(isShowing);
+ if (!isShowing) {
+ mImePositionProcessor.updateAdjustForIme();
+ }
+ mIsKeyguardShowing = isShowing;
+ }
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ if (displayId != DEFAULT_DISPLAY) {
+ return;
+ }
+ mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+ mDisplayController.getDisplayLayout(displayId), mSplits);
+ mImeController.addPositionProcessor(mImePositionProcessor);
+ mDisplayController.addDisplayChangingController(mRotationController);
+ if (!ActivityTaskManager.supportsSplitScreenMultiWindow(mContext)) {
+ removeDivider();
+ return;
+ }
+ try {
+ mSplits.init();
+ // Set starting tile bounds based on middle target
+ final WindowContainerTransaction tct = new WindowContainerTransaction();
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ mSplitLayout.resizeSplits(midPos, tct);
+ WindowOrganizer.applyTransaction(tct);
+ } catch (Exception e) {
+ Slog.e(TAG, "Failed to register docked stack listener", e);
+ removeDivider();
+ return;
+ }
+ }
+
+ @Override
+ public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {
+ if (displayId != DEFAULT_DISPLAY || !mSplits.isSplitScreenSupported()) {
+ return;
+ }
+ mSplitLayout = new SplitDisplayLayout(mDisplayController.getDisplayContext(displayId),
+ mDisplayController.getDisplayLayout(displayId), mSplits);
+ if (mRotateSplitLayout == null) {
+ int midPos = mSplitLayout.getSnapAlgorithm().getMiddleTarget().position;
+ final WindowContainerTransaction tct = new WindowContainerTransaction();
+ mSplitLayout.resizeSplits(midPos, tct);
+ WindowOrganizer.applyTransaction(tct);
+ } else if (mSplitLayout.mDisplayLayout.rotation()
+ == mRotateSplitLayout.mDisplayLayout.rotation()) {
+ mSplitLayout.mPrimary = new Rect(mRotateSplitLayout.mPrimary);
+ mSplitLayout.mSecondary = new Rect(mRotateSplitLayout.mSecondary);
+ mRotateSplitLayout = null;
+ }
+ if (isSplitActive()) {
+ update(newConfig);
+ }
+ }
+
+ /** Posts task to handler dealing with divider. */
+ void post(Runnable task) {
+ mHandler.post(task);
+ }
+
+ /** Returns {@link DividerView}. */
+ public DividerView getDividerView() {
+ return mView;
+ }
+
+ /** Returns {@code true} if one of the split screen is in minimized mode. */
+ public boolean isMinimized() {
+ return mMinimized;
+ }
+
+ public boolean isHomeStackResizable() {
+ return mHomeStackResizable;
+ }
+
+ /** Returns {@code true} if the divider is visible. */
+ public boolean isDividerVisible() {
+ return mView != null && mView.getVisibility() == View.VISIBLE;
+ }
+
+ /**
+ * This indicates that at-least one of the splits has content. This differs from
+ * isDividerVisible because the divider is only visible once *everything* is in split mode
+ * while this only cares if some things are (eg. while entering/exiting as well).
+ */
+ private boolean isSplitActive() {
+ return mSplits.mPrimary != null && mSplits.mSecondary != null
+ && (mSplits.mPrimary.topActivityType != ACTIVITY_TYPE_UNDEFINED
+ || mSplits.mSecondary.topActivityType != ACTIVITY_TYPE_UNDEFINED);
+ }
+
+ private void addDivider(Configuration configuration) {
+ Context dctx = mDisplayController.getDisplayContext(mContext.getDisplayId());
+ mView = (DividerView)
+ LayoutInflater.from(dctx).inflate(R.layout.docked_stack_divider, null);
+ DisplayLayout displayLayout = mDisplayController.getDisplayLayout(mContext.getDisplayId());
+ mView.injectDependencies(mWindowManager, mDividerState, this, mSplits, mSplitLayout,
+ mImePositionProcessor, mWindowManagerProxy);
+ mView.setVisibility(mVisible ? View.VISIBLE : View.INVISIBLE);
+ mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, null /* transaction */);
+ final int size = dctx.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.docked_stack_divider_thickness);
+ final boolean landscape = configuration.orientation == ORIENTATION_LANDSCAPE;
+ final int width = landscape ? size : displayLayout.width();
+ final int height = landscape ? displayLayout.height() : size;
+ mWindowManager.add(mView, width, height, mContext.getDisplayId());
+ }
+
+ private void removeDivider() {
+ if (mView != null) {
+ mView.onDividerRemoved();
+ }
+ mWindowManager.remove();
+ }
+
+ private void update(Configuration configuration) {
+ final boolean isDividerHidden = mView != null && mIsKeyguardShowing;
+
+ removeDivider();
+ addDivider(configuration);
+
+ if (mMinimized) {
+ mView.setMinimizedDockStack(true, mHomeStackResizable, null /* transaction */);
+ updateTouchable();
+ }
+ mView.setHidden(isDividerHidden);
+ }
+
+ void onTaskVanished() {
+ mHandler.post(this::removeDivider);
+ }
+
+ private void updateVisibility(final boolean visible) {
+ if (DEBUG) Slog.d(TAG, "Updating visibility " + mVisible + "->" + visible);
+ if (mVisible != visible) {
+ mVisible = visible;
+ mView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
+
+ if (visible) {
+ mView.enterSplitMode(mHomeStackResizable);
+ // Update state because animations won't finish.
+ mWindowManagerProxy.runInSync(
+ t -> mView.setMinimizedDockStack(mMinimized, mHomeStackResizable, t));
+
+ } else {
+ mView.exitSplitMode();
+ mWindowManagerProxy.runInSync(
+ t -> mView.setMinimizedDockStack(false, mHomeStackResizable, t));
+ }
+ // Notify existence listeners
+ synchronized (mDockedStackExistsListeners) {
+ mDockedStackExistsListeners.removeIf(wf -> {
+ Consumer<Boolean> l = wf.get();
+ if (l != null) l.accept(visible);
+ return l == null;
+ });
+ }
+ }
+ }
+
+ /** Switch to minimized state if appropriate. */
+ public void setMinimized(final boolean minimized) {
+ if (DEBUG) Slog.d(TAG, "posting ext setMinimized " + minimized + " vis:" + mVisible);
+ mHandler.post(() -> {
+ if (DEBUG) Slog.d(TAG, "run posted ext setMinimized " + minimized + " vis:" + mVisible);
+ if (!mVisible) {
+ return;
+ }
+ setHomeMinimized(minimized);
+ });
+ }
+
+ private void setHomeMinimized(final boolean minimized) {
+ if (DEBUG) {
+ Slog.d(TAG, "setHomeMinimized min:" + mMinimized + "->" + minimized + " hrsz:"
+ + mHomeStackResizable + " split:" + isDividerVisible());
+ }
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ final boolean minimizedChanged = mMinimized != minimized;
+ // Update minimized state
+ if (minimizedChanged) {
+ mMinimized = minimized;
+ }
+ // Always set this because we could be entering split when mMinimized is already true
+ wct.setFocusable(mSplits.mPrimary.token, !mMinimized);
+
+ // Sync state to DividerView if it exists.
+ if (mView != null) {
+ final int displayId = mView.getDisplay() != null
+ ? mView.getDisplay().getDisplayId() : DEFAULT_DISPLAY;
+ // pause ime here (before updateMinimizedDockedStack)
+ if (mMinimized) {
+ mImePositionProcessor.pause(displayId);
+ }
+ if (minimizedChanged) {
+ // This conflicts with IME adjustment, so only call it when things change.
+ mView.setMinimizedDockStack(minimized, getAnimDuration(), mHomeStackResizable);
+ }
+ if (!mMinimized) {
+ // afterwards so it can end any animations started in view
+ mImePositionProcessor.resume(displayId);
+ }
+ }
+ updateTouchable();
+
+ // If we are only setting focusability, a sync transaction isn't necessary (in fact it
+ // can interrupt other animations), so see if it can be submitted on pending instead.
+ if (!mWindowManagerProxy.queueSyncTransactionIfWaiting(wct)) {
+ WindowOrganizer.applyTransaction(wct);
+ }
+ }
+
+ void setAdjustedForIme(boolean adjustedForIme) {
+ if (mAdjustedForIme == adjustedForIme) {
+ return;
+ }
+ mAdjustedForIme = adjustedForIme;
+ updateTouchable();
+ }
+
+ private void updateTouchable() {
+ mWindowManager.setTouchable(!mAdjustedForIme);
+ }
+
+ /**
+ * Workaround for b/62528361, at the time recents has drawn, it may happen before a
+ * configuration change to the Divider, and internally, the event will be posted to the
+ * subscriber, or DividerView, which has been removed and prevented from resizing. Instead,
+ * register the event handler here and proxy the event to the current DividerView.
+ */
+ public void onRecentsDrawn(DividerView.RecentDrawnCallback callback) {
+ if (mView != null) {
+ mView.onRecentsDrawn(callback);
+ }
+ }
+
+ /** Called when there's a task undocking. */
+ public void onUndockingTask() {
+ if (mView != null) {
+ mView.onUndockingTask();
+ }
+ }
+
+ /** Called when the first docked animation frame rendered. */
+ public void onDockedFirstAnimationFrame() {
+ if (mView != null) {
+ mView.onDockedFirstAnimationFrame();
+ }
+ }
+
+ /** Called when top task docked. */
+ public void onDockedTopTask() {
+ if (mView != null) {
+ mView.onDockedTopTask();
+ }
+ }
+
+ /** Called when app transition finished. */
+ public void onAppTransitionFinished() {
+ if (mView == null) {
+ return;
+ }
+ mForcedResizableController.onAppTransitionFinished();
+ }
+
+ @Override
+ public void onDraggingStart() {
+ mForcedResizableController.onDraggingStart();
+ }
+
+ @Override
+ public void onDraggingEnd() {
+ mForcedResizableController.onDraggingEnd();
+ }
+
+ /** Dumps current status of Divider.*/
+ public void dump(PrintWriter pw) {
+ pw.print(" mVisible="); pw.println(mVisible);
+ pw.print(" mMinimized="); pw.println(mMinimized);
+ pw.print(" mAdjustedForIme="); pw.println(mAdjustedForIme);
+ }
+
+ long getAnimDuration() {
+ float transitionScale = Settings.Global.getFloat(mContext.getContentResolver(),
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ mContext.getResources().getFloat(
+ com.android.internal.R.dimen
+ .config_appTransitionAnimationDurationScaleDefault));
+ final long transitionDuration = DEFAULT_APP_TRANSITION_DURATION;
+ return (long) (transitionDuration * transitionScale);
+ }
+
+ /** Registers listener that gets called whenever the existence of the divider changes. */
+ public void registerInSplitScreenListener(Consumer<Boolean> listener) {
+ listener.accept(isDividerVisible());
+ synchronized (mDockedStackExistsListeners) {
+ mDockedStackExistsListeners.add(new WeakReference<>(listener));
+ }
+ }
+
+ void startEnterSplit() {
+ update(mDisplayController.getDisplayContext(
+ mContext.getDisplayId()).getResources().getConfiguration());
+ // Set resizable directly here because applyEnterSplit already resizes home stack.
+ mHomeStackResizable = mWindowManagerProxy.applyEnterSplit(mSplits, mSplitLayout);
+ }
+
+ void startDismissSplit() {
+ mWindowManagerProxy.applyDismissSplit(mSplits, mSplitLayout, true /* dismissOrMaximize */);
+ updateVisibility(false /* visible */);
+ mMinimized = false;
+ removeDivider();
+ mImePositionProcessor.reset();
+ }
+
+ void ensureMinimizedSplit() {
+ setHomeMinimized(true /* minimized */);
+ if (mView != null && !isDividerVisible()) {
+ // Wasn't in split-mode yet, so enter now.
+ if (DEBUG) {
+ Slog.d(TAG, " entering split mode with minimized=true");
+ }
+ updateVisibility(true /* visible */);
+ }
+ }
+
+ void ensureNormalSplit() {
+ setHomeMinimized(false /* minimized */);
+ if (mView != null && !isDividerVisible()) {
+ // Wasn't in split-mode, so enter now.
+ if (DEBUG) {
+ Slog.d(TAG, " enter split mode unminimized ");
+ }
+ updateVisibility(true /* visible */);
+ }
+ }
+
+ SplitDisplayLayout getSplitLayout() {
+ return mSplitLayout;
+ }
+
+ WindowManagerProxy getWmProxy() {
+ return mWindowManagerProxy;
+ }
+
+ /** @return the container token for the secondary split root task. */
+ public WindowContainerToken getSecondaryRoot() {
+ if (mSplits == null || mSplits.mSecondary == null) {
+ return null;
+ }
+ return mSplits.mSecondary.token;
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
index d5f7b39..a10242a 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerHandleView.java
@@ -34,7 +34,7 @@
/**
* View for the handle in the docked stack divider.
*/
-public class DividerHandleView extends View {
+class DividerHandleView extends View {
private final static Property<DividerHandleView, Integer> WIDTH_PROPERTY
= new Property<DividerHandleView, Integer>(Integer.class, "width") {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
index 84ec387..c915f07 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerImeController.java
@@ -38,7 +38,7 @@
class DividerImeController implements DisplayImeController.ImePositionProcessor {
private static final String TAG = "DividerImeController";
- private static final boolean DEBUG = Divider.DEBUG;
+ private static final boolean DEBUG = DividerController.DEBUG;
private static final float ADJUSTED_NONFOCUS_DIM = 0.3f;
@@ -100,15 +100,15 @@
}
private DividerView getView() {
- return mSplits.mDivider.getView();
+ return mSplits.mDividerController.getDividerView();
}
private SplitDisplayLayout getLayout() {
- return mSplits.mDivider.getSplitLayout();
+ return mSplits.mDividerController.getSplitLayout();
}
private boolean isDividerVisible() {
- return mSplits.mDivider.isDividerVisible();
+ return mSplits.mDividerController.isDividerVisible();
}
private boolean getSecondaryHasFocus(int displayId) {
@@ -151,7 +151,7 @@
mSecondaryHasFocus = getSecondaryHasFocus(displayId);
final boolean targetAdjusted = splitIsVisible && imeShouldShow && mSecondaryHasFocus
&& !imeIsFloating && !getLayout().mDisplayLayout.isLandscape()
- && !mSplits.mDivider.isMinimized();
+ && !mSplits.mDividerController.isMinimized();
if (mLastAdjustTop < 0) {
mLastAdjustTop = imeShouldShow ? hiddenTop : shownTop;
} else if (mLastAdjustTop != (imeShouldShow ? mShownTop : mHiddenTop)) {
@@ -236,7 +236,7 @@
SCREEN_WIDTH_DP_UNDEFINED, SCREEN_HEIGHT_DP_UNDEFINED);
}
- if (!mSplits.mDivider.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
+ if (!mSplits.mDividerController.getWmProxy().queueSyncTransactionIfWaiting(wct)) {
WindowOrganizer.applyTransaction(wct);
}
}
@@ -250,7 +250,7 @@
: DisplayImeController.ANIMATION_DURATION_HIDE_MS);
}
}
- mSplits.mDivider.setAdjustedForIme(mTargetShown && !mPaused);
+ mSplits.mDividerController.setAdjustedForIme(mTargetShown && !mPaused);
}
public void updateAdjustForIme() {
@@ -343,10 +343,12 @@
mAnimation.setInterpolator(DisplayImeController.INTERPOLATOR);
mAnimation.addListener(new AnimatorListenerAdapter() {
private boolean mCancel = false;
+
@Override
public void onAnimationCancel(Animator animation) {
mCancel = true;
}
+
@Override
public void onAnimationEnd(Animator animation) {
SurfaceControl.Transaction t = mTransactionPool.acquire();
@@ -400,7 +402,8 @@
mTargetAdjusted = mPausedTargetAdjusted;
updateDimTargets();
final DividerView view = getView();
- if ((mTargetAdjusted != mAdjusted) && !mSplits.mDivider.isMinimized() && view != null) {
+ if ((mTargetAdjusted != mAdjusted) && !mSplits.mDividerController.isMinimized()
+ && view != null) {
// End unminimize animations since they conflict with adjustment animations.
view.finishAnimations();
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
index c24431c..cdf44d7 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerModule.java
@@ -46,7 +46,10 @@
DisplayController displayController, SystemWindows systemWindows,
DisplayImeController imeController, @Main Handler handler,
KeyguardStateController keyguardStateController, TransactionPool transactionPool) {
- return new Divider(context, recentsOptionalLazy, displayController, systemWindows,
- imeController, handler, keyguardStateController, transactionPool);
+ // TODO(b/161116823): fetch DividerProxy from WM shell lib.
+ DividerController dividerController = new DividerController(context, displayController,
+ systemWindows, imeController, handler, transactionPool);
+ return new Divider(context, dividerController, keyguardStateController,
+ recentsOptionalLazy);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
index b6c6afd..6447c52 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/DividerView.java
@@ -76,11 +76,14 @@
public class DividerView extends FrameLayout implements OnTouchListener,
OnComputeInternalInsetsListener {
private static final String TAG = "DividerView";
- private static final boolean DEBUG = Divider.DEBUG;
+ private static final boolean DEBUG = DividerController.DEBUG;
public interface DividerCallbacks {
void onDraggingStart();
void onDraggingEnd();
+ }
+
+ interface RecentDrawnCallback {
void growRecents();
}
@@ -437,17 +440,17 @@
releaseBackground();
}
- public void stopDragging(int position, SnapTarget target, long duration,
+ private void stopDragging(int position, SnapTarget target, long duration,
Interpolator interpolator) {
stopDragging(position, target, duration, 0 /* startDelay*/, 0 /* endDelay */, interpolator);
}
- public void stopDragging(int position, SnapTarget target, long duration,
+ private void stopDragging(int position, SnapTarget target, long duration,
Interpolator interpolator, long endDelay) {
stopDragging(position, target, duration, 0 /* startDelay*/, endDelay, interpolator);
}
- public void stopDragging(int position, SnapTarget target, long duration, long startDelay,
+ private void stopDragging(int position, SnapTarget target, long duration, long startDelay,
long endDelay, Interpolator interpolator) {
mHandle.setTouching(false, true /* animate */);
flingTo(position, target, duration, startDelay, endDelay, interpolator);
@@ -1363,7 +1366,7 @@
null /* transaction */);
}
- void onRecentsDrawn() {
+ void onRecentsDrawn(RecentDrawnCallback callback) {
updateDockSide();
final int position = calculatePositionForInsetBounds();
if (mState.animateAfterRecentsDrawn) {
@@ -1380,8 +1383,8 @@
if (mState.growAfterRecentsDrawn) {
mState.growAfterRecentsDrawn = false;
updateDockSide();
- if (mCallback != null) {
- mCallback.growRecents();
+ if (callback != null) {
+ callback.growRecents();
}
stopDragging(position, getSnapAlgorithm().getMiddleTarget(), 336,
Interpolators.FAST_OUT_SLOW_IN);
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
index db7996e..f412cc0 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/ForcedResizableInfoActivityController.java
@@ -75,7 +75,8 @@
}
}
- public ForcedResizableInfoActivityController(Context context, Divider divider) {
+ public ForcedResizableInfoActivityController(Context context,
+ DividerController dividerController) {
mContext = context;
ActivityManagerWrapper.getInstance().registerTaskStackListener(
new TaskStackChangeListener() {
@@ -95,7 +96,7 @@
activityLaunchOnSecondaryDisplayFailed();
}
});
- divider.registerInSplitScreenListener(mDockedStackExistsListener);
+ dividerController.registerInSplitScreenListener(mDockedStackExistsListener);
}
public void onAppTransitionFinished() {
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
index 7a313dc..ef5e8a1 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SplitScreenTaskOrganizer.java
@@ -35,7 +35,7 @@
class SplitScreenTaskOrganizer extends TaskOrganizer {
private static final String TAG = "SplitScreenTaskOrg";
- private static final boolean DEBUG = Divider.DEBUG;
+ private static final boolean DEBUG = DividerController.DEBUG;
RunningTaskInfo mPrimary;
RunningTaskInfo mSecondary;
@@ -44,13 +44,13 @@
SurfaceControl mPrimaryDim;
SurfaceControl mSecondaryDim;
Rect mHomeBounds = new Rect();
- final Divider mDivider;
+ final DividerController mDividerController;
private boolean mSplitScreenSupported = false;
final SurfaceSession mSurfaceSession = new SurfaceSession();
- SplitScreenTaskOrganizer(Divider divider) {
- mDivider = divider;
+ SplitScreenTaskOrganizer(DividerController dividerController) {
+ mDividerController = dividerController;
}
void init() throws RemoteException {
@@ -75,11 +75,11 @@
}
SurfaceControl.Transaction getTransaction() {
- return mDivider.mTransactionPool.acquire();
+ return mDividerController.mTransactionPool.acquire();
}
void releaseTransaction(SurfaceControl.Transaction t) {
- mDivider.mTransactionPool.release(t);
+ mDividerController.mTransactionPool.release(t);
}
@Override
@@ -140,7 +140,7 @@
t.apply();
releaseTransaction(t);
- mDivider.onTaskVanished();
+ mDividerController.onTaskVanished();
}
}
}
@@ -150,7 +150,7 @@
if (taskInfo.displayId != DEFAULT_DISPLAY) {
return;
}
- mDivider.getHandler().post(() -> handleTaskInfoChanged(taskInfo));
+ mDividerController.post(() -> handleTaskInfoChanged(taskInfo));
}
/**
@@ -169,7 +169,7 @@
}
final boolean secondaryImpliedMinimize = mSecondary.topActivityType == ACTIVITY_TYPE_HOME
|| (mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS
- && mDivider.isHomeStackResizable());
+ && mDividerController.isHomeStackResizable());
final boolean primaryWasEmpty = mPrimary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
final boolean secondaryWasEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
if (info.token.asBinder() == mPrimary.token.asBinder()) {
@@ -181,7 +181,7 @@
final boolean secondaryIsEmpty = mSecondary.topActivityType == ACTIVITY_TYPE_UNDEFINED;
final boolean secondaryImpliesMinimize = mSecondary.topActivityType == ACTIVITY_TYPE_HOME
|| (mSecondary.topActivityType == ACTIVITY_TYPE_RECENTS
- && mDivider.isHomeStackResizable());
+ && mDividerController.isHomeStackResizable());
if (DEBUG) {
Log.d(TAG, "onTaskInfoChanged " + mPrimary + " " + mSecondary);
}
@@ -197,14 +197,14 @@
Log.d(TAG, " at-least one split empty " + mPrimary.topActivityType
+ " " + mSecondary.topActivityType);
}
- if (mDivider.isDividerVisible()) {
+ if (mDividerController.isDividerVisible()) {
// Was in split-mode, which means we are leaving split, so continue that.
// This happens when the stack in the primary-split is dismissed.
if (DEBUG) {
Log.d(TAG, " was in split, so this means leave it "
+ mPrimary.topActivityType + " " + mSecondary.topActivityType);
}
- mDivider.startDismissSplit();
+ mDividerController.startDismissSplit();
} else if (!primaryIsEmpty && primaryWasEmpty && secondaryWasEmpty) {
// Wasn't in split-mode (both were empty), but now that the primary split is
// populated, we should fully enter split by moving everything else into secondary.
@@ -213,15 +213,15 @@
if (DEBUG) {
Log.d(TAG, " was not in split, but primary is populated, so enter it");
}
- mDivider.startEnterSplit();
+ mDividerController.startEnterSplit();
}
} else if (secondaryImpliesMinimize) {
// Both splits are populated but the secondary split has a home/recents stack on top,
// so enter minimized mode.
- mDivider.ensureMinimizedSplit();
+ mDividerController.ensureMinimizedSplit();
} else {
// Both splits are populated by normal activities, so make sure we aren't minimized.
- mDivider.ensureNormalSplit();
+ mDividerController.ensureNormalSplit();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
index 6812f62..f2500e5 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/SyncTransactionQueue.java
@@ -33,7 +33,7 @@
* Helper for serializing sync-transactions and corresponding callbacks.
*/
class SyncTransactionQueue {
- private static final boolean DEBUG = Divider.DEBUG;
+ private static final boolean DEBUG = DividerController.DEBUG;
private static final String TAG = "SyncTransactionQueue";
// Just a little longer than the sync-engine timeout of 5s
diff --git a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
index 2b36812..82b10bd 100644
--- a/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/stackdivider/WindowManagerProxy.java
@@ -50,7 +50,7 @@
/**
* Proxy to simplify calls into window manager/activity manager
*/
-public class WindowManagerProxy {
+class WindowManagerProxy {
private static final String TAG = "WindowManagerProxy";
private static final int[] HOME_AND_RECENTS = {ACTIVITY_TYPE_HOME, ACTIVITY_TYPE_RECENTS};
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index aba9e10..e1ff8724 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -15,7 +15,6 @@
*/
package com.android.systemui.statusbar.notification;
-import static android.service.notification.NotificationListenerService.REASON_CANCEL;
import static android.service.notification.NotificationListenerService.REASON_ERROR;
import static com.android.systemui.statusbar.notification.collection.NotifCollection.REASON_UNKNOWN;
@@ -24,14 +23,11 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
-import android.content.Context;
import android.os.RemoteException;
-import android.os.ServiceManager;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.NotificationListenerService.RankingMap;
-import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -41,7 +37,6 @@
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dumpable;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationListener;
@@ -54,11 +49,11 @@
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.dagger.NotificationsModule;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.util.Assert;
import com.android.systemui.util.leak.LeakDetector;
@@ -147,8 +142,6 @@
private final NotificationRankingManager mRankingManager;
private final FeatureFlags mFeatureFlags;
private final ForegroundServiceDismissalFeatureController mFgsFeatureController;
- private final HeadsUpManager mHeadsUpManager;
- private final StatusBarStateController mStatusBarStateController;
private NotificationPresenter mPresenter;
private RankingMap mLatestRankingMap;
@@ -213,8 +206,7 @@
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
ForegroundServiceDismissalFeatureController fgsFeatureController,
- HeadsUpManager headsUpManager,
- StatusBarStateController statusBarStateController
+ IStatusBarService statusBarService
) {
mLogger = logger;
mGroupManager = groupManager;
@@ -225,11 +217,7 @@
mRemoteInputManagerLazy = notificationRemoteInputManagerLazy;
mLeakDetector = leakDetector;
mFgsFeatureController = fgsFeatureController;
- mHeadsUpManager = headsUpManager;
- mStatusBarStateController = statusBarStateController;
-
- mStatusBarService = IStatusBarService.Stub.asInterface(
- ServiceManager.checkService(Context.STATUS_BAR_SERVICE));
+ mStatusBarService = statusBarService;
}
/** Once called, the NEM will start processing notification events from system server. */
@@ -284,16 +272,23 @@
}
/**
- * Requests a notification to be removed.
+ * User requests a notification to be removed.
*
* @param n the notification to remove.
* @param reason why it is being removed e.g. {@link NotificationListenerService#REASON_CANCEL},
* or 0 if unknown.
*/
- public void performRemoveNotification(StatusBarNotification n, int reason) {
- final NotificationVisibility nv = obtainVisibility(n.getKey());
+ public void performRemoveNotification(
+ StatusBarNotification n,
+ @NonNull DismissedByUserStats stats,
+ int reason
+ ) {
removeNotificationInternal(
- n.getKey(), null, nv, false /* forceRemove */, true /* removedByUser */,
+ n.getKey(),
+ null,
+ stats.notificationVisibility,
+ false /* forceRemove */,
+ stats,
reason);
}
@@ -337,7 +332,11 @@
*/
private void handleInflationException(StatusBarNotification n, Exception e) {
removeNotificationInternal(
- n.getKey(), null, null, true /* forceRemove */, false /* removedByUser */,
+ n.getKey(),
+ null,
+ null,
+ true /* forceRemove */,
+ null /* dismissedByUserStats */,
REASON_ERROR);
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onInflationError(n, e);
@@ -435,19 +434,28 @@
reapplyFilterAndSort("addVisibleNotification");
}
-
- public void removeNotification(String key, RankingMap ranking,
- int reason) {
- removeNotificationInternal(key, ranking, obtainVisibility(key), false /* forceRemove */,
- false /* removedByUser */, reason);
+ @VisibleForTesting
+ protected void removeNotification(String key, RankingMap ranking, int reason) {
+ removeNotificationInternal(
+ key,
+ ranking,
+ obtainVisibility(key),
+ false /* forceRemove */,
+ null /* dismissedByUserStats */,
+ reason);
}
+ /**
+ * Internally remove a notification because system server has reported the notification
+ * should be removed OR the user has manually dismissed the notification
+ * @param dismissedByUserStats non-null if the user manually dismissed the notification
+ */
private void removeNotificationInternal(
String key,
@Nullable RankingMap ranking,
@Nullable NotificationVisibility visibility,
boolean forceRemove,
- boolean removedByUser,
+ DismissedByUserStats dismissedByUserStats,
int reason) {
final NotificationEntry entry = getActiveNotificationUnfiltered(key);
@@ -512,11 +520,11 @@
handleGroupSummaryRemoved(key);
removeVisibleNotification(key);
updateNotifications("removeNotificationInternal");
- removedByUser |= entryDismissed;
+ final boolean removedByUser = dismissedByUserStats != null;
mLogger.logNotifRemoved(entry.getKey(), removedByUser);
if (removedByUser && visibility != null) {
- sendNotificationRemovalToServer(entry.getKey(), entry.getSbn(), visibility);
+ sendNotificationRemovalToServer(entry.getSbn(), dismissedByUserStats);
}
for (NotificationEntryListener listener : mNotificationEntryListeners) {
listener.onEntryRemoved(entry, visibility, removedByUser, reason);
@@ -534,30 +542,18 @@
}
private void sendNotificationRemovalToServer(
- String key,
StatusBarNotification notification,
- NotificationVisibility nv) {
- final String pkg = notification.getPackageName();
- final String tag = notification.getTag();
- final int id = notification.getId();
- final int userId = notification.getUser().getIdentifier();
+ DismissedByUserStats dismissedByUserStats) {
try {
- int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (mHeadsUpManager.isAlerting(key)) {
- dismissalSurface = NotificationStats.DISMISSAL_PEEK;
- } else if (mStatusBarStateController.isDozing()) {
- dismissalSurface = NotificationStats.DISMISSAL_AOD;
- }
- int dismissalSentiment = NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
mStatusBarService.onNotificationClear(
- pkg,
- tag,
- id,
- userId,
+ notification.getPackageName(),
+ notification.getTag(),
+ notification.getId(),
+ notification.getUser().getIdentifier(),
notification.getKey(),
- dismissalSurface,
- dismissalSentiment,
- nv);
+ dismissedByUserStats.dismissalSurface,
+ dismissedByUserStats.dismissalSentiment,
+ dismissedByUserStats.notificationVisibility);
} catch (RemoteException ex) {
// system process is dead if we're here.
}
@@ -641,11 +637,7 @@
// Construct the expanded view.
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- mNotificationRowBinderLazy.get()
- .inflateViews(
- entry,
- () -> performRemoveNotification(notification, REASON_CANCEL),
- mInflationCallback);
+ mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
}
mPendingNotifications.put(key, entry);
@@ -675,7 +667,7 @@
final String key = notification.getKey();
abortExistingInflation(key, "updateNotification");
- NotificationEntry entry = getActiveNotificationUnfiltered(key);
+ final NotificationEntry entry = getActiveNotificationUnfiltered(key);
if (entry == null) {
return;
}
@@ -701,11 +693,7 @@
}
if (!mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- mNotificationRowBinderLazy.get()
- .inflateViews(
- entry,
- () -> performRemoveNotification(notification, REASON_CANCEL),
- mInflationCallback);
+ mNotificationRowBinderLazy.get().inflateViews(entry, mInflationCallback);
}
updateNotifications("updateNotificationInternal");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
index d081e11..aaf5c4d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifInflaterImpl.java
@@ -16,17 +16,10 @@
package com.android.systemui.statusbar.notification.collection;
-import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-
-import android.service.notification.NotificationStats;
-
import com.android.internal.statusbar.IStatusBarService;
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.statusbar.notification.InflationException;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinderImpl;
-import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.NotifInflationErrorManager;
import com.android.systemui.statusbar.notification.row.NotificationContentInflater;
@@ -81,7 +74,6 @@
try {
requireBinder().inflateViews(
entry,
- getDismissCallback(entry),
wrapInflationCallback(callback));
} catch (InflationException e) {
mNotifErrorManager.setInflationError(entry, e);
@@ -93,30 +85,6 @@
entry.abortTask();
}
- private Runnable getDismissCallback(NotificationEntry entry) {
- return new Runnable() {
- @Override
- public void run() {
- int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- /*
- * TODO: determine dismissal surface (ie: shade / headsup / aod)
- * see {@link NotificationLogger#logNotificationClear}
- */
- mNotifCollection.dismissNotification(
- entry,
- new DismissedByUserStats(
- dismissalSurface,
- DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(entry.getKey(),
- entry.getRanking().getRank(),
- mNotifPipeline.getShadeListCount(),
- true,
- NotificationLogger.getNotificationLocation(entry))
- ));
- }
- };
- }
-
private NotificationContentInflater.InflationCallback wrapInflationCallback(
InflationCallback callback) {
return new NotificationContentInflater.InflationCallback() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
index 92426e5..08462c1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/BubbleCoordinator.java
@@ -16,10 +16,6 @@
package com.android.systemui.statusbar.notification.collection.coordinator;
-import static android.service.notification.NotificationStats.DISMISSAL_OTHER;
-import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
-
-import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
@@ -27,7 +23,6 @@
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifFilter;
import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifDismissInterceptor;
-import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import java.util.HashSet;
import java.util.Set;
@@ -101,7 +96,6 @@
@Override
public boolean shouldInterceptDismissal(NotificationEntry entry) {
- // TODO: b/149041810 add support for intercepting app-cancelled bubble notifications
// for experimental bubbles
if (mBubbleController.handleDismissalInterception(entry)) {
mInterceptedDismissalEntries.add(entry.getKey());
@@ -121,17 +115,21 @@
private final BubbleController.NotifCallback mNotifCallback =
new BubbleController.NotifCallback() {
@Override
- public void removeNotification(NotificationEntry entry, int reason) {
+ public void removeNotification(
+ NotificationEntry entry,
+ DismissedByUserStats dismissedByUserStats,
+ int reason
+ ) {
if (isInterceptingDismissal(entry)) {
mInterceptedDismissalEntries.remove(entry.getKey());
mOnEndDismissInterception.onEndDismissInterception(mDismissInterceptor, entry,
- createDismissedByUserStats(entry));
+ dismissedByUserStats);
} else if (mNotifPipeline.getAllNotifs().contains(entry)) {
// Bubbles are hiding the notifications from the shade, but the bubble was
// deleted; therefore, the notification should be cancelled as if it were a user
// dismissal (this won't re-enter handleInterceptDimissal because Bubbles
// will have already marked it as no longer a bubble)
- mNotifCollection.dismissNotification(entry, createDismissedByUserStats(entry));
+ mNotifCollection.dismissNotification(entry, dismissedByUserStats);
}
}
@@ -149,16 +147,4 @@
private boolean isInterceptingDismissal(NotificationEntry entry) {
return mInterceptedDismissalEntries.contains(entry.getKey());
}
-
- private DismissedByUserStats createDismissedByUserStats(NotificationEntry entry) {
- return new DismissedByUserStats(
- DISMISSAL_OTHER,
- DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(entry.getKey(),
- entry.getRanking().getRank(),
- mNotifPipeline.getShadeListCount(),
- true, // was visible as a bubble
- NotificationLogger.getNotificationLocation(entry))
- );
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
index 710b137..1215ade 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinder.java
@@ -38,7 +38,6 @@
*/
void inflateViews(
NotificationEntry entry,
- Runnable onDismissRunnable,
NotificationRowContentBinder.InflationCallback callback)
throws InflationException;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
index 85a3bc9..8849824 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/NotificationRowBinderImpl.java
@@ -32,7 +32,6 @@
import com.android.systemui.statusbar.notification.NotificationClicker;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.icon.IconManager;
-import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController;
import com.android.systemui.statusbar.notification.row.NotifBindPipeline;
@@ -59,7 +58,6 @@
private final NotificationLockscreenUserManager mNotificationLockscreenUserManager;
private final NotifBindPipeline mNotifBindPipeline;
private final RowContentBindStage mRowContentBindStage;
- private final NotificationInterruptStateProvider mNotificationInterruptStateProvider;
private final Provider<RowInflaterTask> mRowInflaterTaskProvider;
private final ExpandableNotificationRowComponent.Builder
mExpandableNotificationRowComponentBuilder;
@@ -79,7 +77,6 @@
NotificationLockscreenUserManager notificationLockscreenUserManager,
NotifBindPipeline notifBindPipeline,
RowContentBindStage rowContentBindStage,
- NotificationInterruptStateProvider notificationInterruptionStateProvider,
Provider<RowInflaterTask> rowInflaterTaskProvider,
ExpandableNotificationRowComponent.Builder expandableNotificationRowComponentBuilder,
IconManager iconManager,
@@ -90,7 +87,6 @@
mMessagingUtil = notificationMessagingUtil;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mNotificationLockscreenUserManager = notificationLockscreenUserManager;
- mNotificationInterruptStateProvider = notificationInterruptionStateProvider;
mRowInflaterTaskProvider = rowInflaterTaskProvider;
mExpandableNotificationRowComponentBuilder = expandableNotificationRowComponentBuilder;
mIconManager = iconManager;
@@ -120,7 +116,6 @@
@Override
public void inflateViews(
NotificationEntry entry,
- Runnable onDismissRunnable,
NotificationRowContentBinder.InflationCallback callback)
throws InflationException {
ViewGroup parent = mListContainer.getViewParentForNotification(entry);
@@ -131,7 +126,6 @@
row.reset();
updateRow(entry, row);
inflateContentViews(entry, row, callback);
- entry.getRowController().setOnDismissRunnable(onDismissRunnable);
} else {
mIconManager.createIcons(entry);
mRowInflaterTaskProvider.get().inflate(mContext, parent, entry,
@@ -141,7 +135,6 @@
mExpandableNotificationRowComponentBuilder
.expandableNotificationRow(row)
.notificationEntry(entry)
- .onDismissRunnable(onDismissRunnable)
.onExpandClickListener(mPresenter)
.build();
ExpandableNotificationRowController rowController =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnDismissCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnDismissCallbackImpl.java
new file mode 100644
index 0000000..36adf9b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/inflation/OnDismissCallbackImpl.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.inflation;
+
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationStats;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
+import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.OnDismissCallback;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+/**
+ * Callback used when a user:
+ * 1. Manually dismisses a notification {@see ExpandableNotificationRow}.
+ * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}.
+ * {@see StatusBarNotificationActivityStarter}
+ */
+public class OnDismissCallbackImpl implements OnDismissCallback {
+ private final NotifPipeline mNotifPipeline;
+ private final NotifCollection mNotifCollection;
+ private final HeadsUpManager mHeadsUpManager;
+ private final StatusBarStateController mStatusBarStateController;
+
+ public OnDismissCallbackImpl(
+ NotifPipeline notifPipeline,
+ NotifCollection notifCollection,
+ HeadsUpManager headsUpManager,
+ StatusBarStateController statusBarStateController
+ ) {
+ mNotifPipeline = notifPipeline;
+ mNotifCollection = notifCollection;
+ mHeadsUpManager = headsUpManager;
+ mStatusBarStateController = statusBarStateController;
+ }
+
+ @Override
+ public void onDismiss(
+ NotificationEntry entry,
+ @NotificationListenerService.NotificationCancelReason int cancellationReason
+ ) {
+ int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+ if (mHeadsUpManager.isAlerting(entry.getKey())) {
+ dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+ } else if (mStatusBarStateController.isDozing()) {
+ dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ }
+
+ mNotifCollection.dismissNotification(
+ entry,
+ new DismissedByUserStats(
+ dismissalSurface,
+ DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(
+ entry.getKey(),
+ entry.getRanking().getRank(),
+ mNotifPipeline.getShadeListCount(),
+ true,
+ NotificationLogger.getNotificationLocation(entry)))
+ );
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnDismissCallbackImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnDismissCallbackImpl.java
new file mode 100644
index 0000000..94ffa8f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/legacy/OnDismissCallbackImpl.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.collection.legacy;
+
+import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
+
+import android.service.notification.NotificationListenerService;
+import android.service.notification.NotificationStats;
+
+import com.android.internal.statusbar.NotificationVisibility;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.statusbar.notification.NotificationEntryManager;
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
+import com.android.systemui.statusbar.notification.logging.NotificationLogger;
+import com.android.systemui.statusbar.notification.row.OnDismissCallback;
+import com.android.systemui.statusbar.policy.HeadsUpManager;
+
+/**
+ * Callback used when a user:
+ * 1. Manually dismisses a notification {@see ExpandableNotificationRow}.
+ * 2. Clicks on a notification with flag {@link android.app.Notification#FLAG_AUTO_CANCEL}.
+ * {@see StatusBarNotificationActivityStarter}
+ */
+public class OnDismissCallbackImpl implements OnDismissCallback {
+ private final NotificationEntryManager mNotificationEntryManager;
+ private final HeadsUpManager mHeadsUpManager;
+ private final StatusBarStateController mStatusBarStateController;
+
+ public OnDismissCallbackImpl(
+ NotificationEntryManager notificationEntryManager,
+ HeadsUpManager headsUpManager,
+ StatusBarStateController statusBarStateController
+ ) {
+ mNotificationEntryManager = notificationEntryManager;
+ mHeadsUpManager = headsUpManager;
+ mStatusBarStateController = statusBarStateController;
+ }
+
+ @Override
+ public void onDismiss(
+ NotificationEntry entry,
+ @NotificationListenerService.NotificationCancelReason int cancellationReason
+ ) {
+ int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
+ if (mHeadsUpManager.isAlerting(entry.getKey())) {
+ dismissalSurface = NotificationStats.DISMISSAL_PEEK;
+ } else if (mStatusBarStateController.isDozing()) {
+ dismissalSurface = NotificationStats.DISMISSAL_AOD;
+ }
+
+ mNotificationEntryManager.performRemoveNotification(
+ entry.getSbn(),
+ new DismissedByUserStats(
+ dismissalSurface,
+ DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(
+ entry.getKey(),
+ entry.getRanking().getRank(),
+ mNotificationEntryManager.getActiveNotificationsCount(),
+ true,
+ NotificationLogger.getNotificationLocation(entry))),
+ cancellationReason
+ );
+ }
+}
+
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index ee471c3..046dc3c8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -25,6 +25,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.R;
import com.android.systemui.bubbles.BubbleController;
import com.android.systemui.dagger.qualifiers.Background;
@@ -40,11 +41,13 @@
import com.android.systemui.statusbar.notification.NotificationEntryManager;
import com.android.systemui.statusbar.notification.NotificationEntryManagerLogger;
import com.android.systemui.statusbar.notification.VisualStabilityManager;
+import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifInflaterImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotifInflater;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.inflation.OnDismissCallbackImpl;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.init.NotificationsController;
@@ -58,6 +61,7 @@
import com.android.systemui.statusbar.notification.row.ChannelEditorDialogController;
import com.android.systemui.statusbar.notification.row.NotificationBlockingHelperManager;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
+import com.android.systemui.statusbar.notification.row.OnDismissCallback;
import com.android.systemui.statusbar.notification.row.PriorityOnboardingDialogController;
import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -92,8 +96,7 @@
Lazy<NotificationRemoteInputManager> notificationRemoteInputManagerLazy,
LeakDetector leakDetector,
ForegroundServiceDismissalFeatureController fgsFeatureController,
- HeadsUpManager headsUpManager,
- StatusBarStateController statusBarStateController) {
+ IStatusBarService statusBarService) {
return new NotificationEntryManager(
logger,
groupManager,
@@ -104,8 +107,7 @@
notificationRemoteInputManagerLazy,
leakDetector,
fgsFeatureController,
- headsUpManager,
- statusBarStateController);
+ statusBarService);
}
/** Provides an instance of {@link NotificationGutsManager} */
@@ -219,6 +221,28 @@
return featureFlags.isNewNotifPipelineRenderingEnabled() ? pipeline.get() : entryManager;
}
+ /**
+ * Provide a dismissal callback that's triggered when a user manually dismissed a notification
+ * from the notification shade or it gets auto-cancelled by click.
+ */
+ @Provides
+ @Singleton
+ static OnDismissCallback provideOnDismissCallback(
+ FeatureFlags featureFlags,
+ HeadsUpManager headsUpManager,
+ StatusBarStateController statusBarStateController,
+ Lazy<NotifPipeline> pipeline,
+ Lazy<NotifCollection> notifCollection,
+ NotificationEntryManager entryManager) {
+ return featureFlags.isNewNotifPipelineRenderingEnabled()
+ ? new OnDismissCallbackImpl(
+ pipeline.get(), notifCollection.get(), headsUpManager,
+ statusBarStateController)
+ : new com.android.systemui.statusbar.notification.collection
+ .legacy.OnDismissCallbackImpl(
+ entryManager, headsUpManager, statusBarStateController);
+ }
+
/** */
@Binds
NotificationInterruptStateProvider bindNotificationInterruptStateProvider(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 7585740..22d0357 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.notification.row;
+import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+
import static com.android.systemui.statusbar.notification.ActivityLaunchAnimator.ExpandAnimationParameters;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
import static com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_PUBLIC;
@@ -321,7 +323,7 @@
private View mGroupParentWhenDismissed;
private boolean mShelfIconVisible;
private boolean mAboveShelf;
- private Runnable mOnDismissRunnable;
+ private OnDismissCallback mOnDismissCallback;
private boolean mIsLowPriority;
private boolean mIsColorized;
private boolean mUseIncreasedCollapsedHeight;
@@ -1444,10 +1446,8 @@
}
dismiss(fromAccessibility);
if (mEntry.isClearable()) {
- // TODO: beverlyt, log dismissal
- // TODO: track dismiss sentiment
- if (mOnDismissRunnable != null) {
- mOnDismissRunnable.run();
+ if (mOnDismissCallback != null) {
+ mOnDismissCallback.onDismiss(mEntry, REASON_CANCEL);
}
}
}
@@ -1464,8 +1464,8 @@
return mIsBlockingHelperShowing && mNotificationTranslationFinished;
}
- void setOnDismissRunnable(Runnable onDismissRunnable) {
- mOnDismissRunnable = onDismissRunnable;
+ void setOnDismissCallback(OnDismissCallback onDismissCallback) {
+ mOnDismissCallback = onDismissCallback;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index c983935..86a3271 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -30,7 +30,6 @@
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.dagger.AppName;
-import com.android.systemui.statusbar.notification.row.dagger.DismissRunnable;
import com.android.systemui.statusbar.notification.row.dagger.NotificationKey;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowScope;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -66,7 +65,7 @@
private final ExpandableNotificationRow.CoordinateOnClickListener mOnAppOpsClickListener;
private final ExpandableNotificationRow.CoordinateOnClickListener mOnFeedbackClickListener;
private final NotificationGutsManager mNotificationGutsManager;
- private Runnable mOnDismissRunnable;
+ private final OnDismissCallback mOnDismissCallback;
private final FalsingManager mFalsingManager;
private final boolean mAllowLongPress;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
@@ -84,7 +83,7 @@
StatusBarStateController statusBarStateController,
NotificationGutsManager notificationGutsManager,
@Named(ALLOW_NOTIFICATION_LONG_PRESS_NAME) boolean allowLongPress,
- @DismissRunnable Runnable onDismissRunnable, FalsingManager falsingManager,
+ OnDismissCallback onDismissCallback, FalsingManager falsingManager,
PeopleNotificationIdentifier peopleNotificationIdentifier) {
mView = view;
mActivatableNotificationViewController = activatableNotificationViewController;
@@ -101,7 +100,7 @@
mOnExpandClickListener = onExpandClickListener;
mStatusBarStateController = statusBarStateController;
mNotificationGutsManager = notificationGutsManager;
- mOnDismissRunnable = onDismissRunnable;
+ mOnDismissCallback = onDismissCallback;
mOnAppOpsClickListener = mNotificationGutsManager::openGuts;
mOnFeedbackClickListener = mNotificationGutsManager::openGuts;
mAllowLongPress = allowLongPress;
@@ -130,7 +129,7 @@
mStatusBarStateController,
mPeopleNotificationIdentifier
);
- mView.setOnDismissRunnable(mOnDismissRunnable);
+ mView.setOnDismissCallback(mOnDismissCallback);
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (mAllowLongPress) {
mView.setLongPressListener((v, x, y, item) -> {
@@ -163,10 +162,4 @@
private void logNotificationExpansion(String key, boolean userAction, boolean expanded) {
mNotificationLogger.onExpansionChanged(key, userAction, expanded);
}
-
- /** */
- public void setOnDismissRunnable(Runnable onDismissRunnable) {
- mOnDismissRunnable = onDismissRunnable;
- mView.setOnDismissRunnable(onDismissRunnable);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnDismissCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnDismissCallback.java
new file mode 100644
index 0000000..f1aed89
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/OnDismissCallback.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.notification.row;
+
+import android.service.notification.NotificationListenerService;
+
+import com.android.systemui.statusbar.notification.collection.NotificationEntry;
+
+/**
+ * Callback when a user clicks on an auto-cancelled notification or manually swipes to dismiss the
+ * notification.
+ */
+public interface OnDismissCallback {
+
+ /**
+ * Handle a user interaction that triggers a notification dismissal.
+ */
+ void onDismiss(
+ NotificationEntry entry,
+ @NotificationListenerService.NotificationCancelReason int cancellationReason);
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
index 321656d..28ddf59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/dagger/ExpandableNotificationRowComponent.java
@@ -54,8 +54,6 @@
@BindsInstance
Builder notificationEntry(NotificationEntry entry);
@BindsInstance
- Builder onDismissRunnable(@DismissRunnable Runnable runnable);
- @BindsInstance
Builder onExpandClickListener(ExpandableNotificationRow.OnExpandClickListener presenter);
ExpandableNotificationRowComponent build();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index 9caf0f7..66a541a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -11,7 +11,7 @@
* 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
+ * limitations under the License.
*/
package com.android.systemui.statusbar.notification.stack;
@@ -6581,40 +6581,25 @@
} else {
final List<Pair<NotificationEntry, DismissedByUserStats>>
entriesWithRowsDismissedFromShade = new ArrayList<>();
- final List<DismissedByUserStats> dismissalUserStats = new ArrayList<>();
final int numVisibleEntries = mNotifPipeline.getShadeListCount();
for (int i = 0; i < viewsToRemove.size(); i++) {
final NotificationEntry entry = viewsToRemove.get(i).getEntry();
- final DismissedByUserStats stats =
- new DismissedByUserStats(
- DISMISSAL_SHADE,
- DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(
- entry.getKey(),
- entry.getRanking().getRank(),
- numVisibleEntries,
- true,
- NotificationLogger.getNotificationLocation(entry)));
entriesWithRowsDismissedFromShade.add(
- new Pair<NotificationEntry, DismissedByUserStats>(entry, stats));
+ new Pair<NotificationEntry, DismissedByUserStats>(
+ entry,
+ getDismissedByUserStats(entry, numVisibleEntries)));
}
mNotifCollection.dismissNotifications(entriesWithRowsDismissedFromShade);
}
} else {
for (ExpandableNotificationRow rowToRemove : viewsToRemove) {
if (canChildBeDismissed(rowToRemove)) {
- if (selectedRows == ROWS_ALL) {
- // TODO: This is a listener method; we shouldn't be calling it. Can we just
- // call performRemoveNotification as below?
- mEntryManager.removeNotification(
- rowToRemove.getEntry().getKey(),
- null /* ranking */,
- NotificationListenerService.REASON_CANCEL_ALL);
- } else {
- mEntryManager.performRemoveNotification(
- rowToRemove.getEntry().getSbn(),
- NotificationListenerService.REASON_CANCEL_ALL);
- }
+ mEntryManager.performRemoveNotification(
+ rowToRemove.getEntry().getSbn(),
+ getDismissedByUserStats(
+ rowToRemove.getEntry(),
+ mEntryManager.getActiveNotificationsCount()),
+ NotificationListenerService.REASON_CANCEL_ALL);
} else {
rowToRemove.resetTranslation();
}
@@ -6628,6 +6613,21 @@
}
}
+ private DismissedByUserStats getDismissedByUserStats(
+ NotificationEntry entry,
+ int numVisibleEntries
+ ) {
+ return new DismissedByUserStats(
+ DISMISSAL_SHADE,
+ DISMISS_SENTIMENT_NEUTRAL,
+ NotificationVisibility.obtain(
+ entry.getKey(),
+ entry.getRanking().getRank(),
+ numVisibleEntries,
+ true,
+ NotificationLogger.getNotificationLocation(entry)));
+ }
+
// ---------------------- DragDownHelper.OnDragDownListener ------------------------------------
@ShadeViewRefactor(RefactorComponent.INPUT)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
index 3ae84ec..b89cb21 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconController.java
@@ -79,15 +79,16 @@
public void removeIcon(String slot, int tag);
public void removeAllIconsForSlot(String slot);
- public static final String ICON_BLACKLIST = "icon_blacklist";
+ // TODO: See if we can rename this tunable name.
+ String ICON_HIDE_LIST = "icon_blacklist";
- /** Reads the default blacklist from config value unless blacklistStr is provided. */
- static ArraySet<String> getIconBlacklist(Context context, String blackListStr) {
+ /** Reads the default hide list from config value unless hideListStr is provided. */
+ static ArraySet<String> getIconHideList(Context context, String hideListStr) {
ArraySet<String> ret = new ArraySet<>();
- String[] blacklist = blackListStr == null
+ String[] hideList = hideListStr == null
? context.getResources().getStringArray(R.array.config_statusBarIconBlackList)
- : blackListStr.split(",");
- for (String slot : blacklist) {
+ : hideListStr.split(",");
+ for (String slot : hideList) {
if (!TextUtils.isEmpty(slot)) {
ret.add(slot);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index d0e8067..21e1d31 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -59,7 +59,7 @@
private static final String TAG = "StatusBarIconController";
private final ArrayList<IconManager> mIconGroups = new ArrayList<>();
- private final ArraySet<String> mIconBlacklist = new ArraySet<>();
+ private final ArraySet<String> mIconHideList = new ArraySet<>();
// Points to light or dark context depending on the... context?
private Context mContext;
@@ -79,7 +79,7 @@
loadDimens();
commandQueue.addCallback(this);
- Dependency.get(TunerService.class).addTunable(this, ICON_BLACKLIST);
+ Dependency.get(TunerService.class).addTunable(this, ICON_HIDE_LIST);
}
@Override
@@ -89,12 +89,12 @@
for (int i = 0; i < allSlots.size(); i++) {
Slot slot = allSlots.get(i);
List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder();
- boolean blocked = mIconBlacklist.contains(slot.getName());
+ boolean hidden = mIconHideList.contains(slot.getName());
for (StatusBarIconHolder holder : holders) {
int tag = holder.getTag();
int viewIndex = getViewIndex(getSlotIndex(slot.getName()), holder.getTag());
- group.onIconAdded(viewIndex, slot.getName(), blocked, holder);
+ group.onIconAdded(viewIndex, slot.getName(), hidden, holder);
}
}
}
@@ -107,11 +107,11 @@
@Override
public void onTuningChanged(String key, String newValue) {
- if (!ICON_BLACKLIST.equals(key)) {
+ if (!ICON_HIDE_LIST.equals(key)) {
return;
}
- mIconBlacklist.clear();
- mIconBlacklist.addAll(StatusBarIconController.getIconBlacklist(mContext, newValue));
+ mIconHideList.clear();
+ mIconHideList.addAll(StatusBarIconController.getIconHideList(mContext, newValue));
ArrayList<Slot> currentSlots = getSlots();
ArrayMap<Slot, List<StatusBarIconHolder>> slotsToReAdd = new ArrayMap<>();
@@ -142,9 +142,9 @@
private void addSystemIcon(int index, StatusBarIconHolder holder) {
String slot = getSlotName(index);
int viewIndex = getViewIndex(index, holder.getTag());
- boolean blocked = mIconBlacklist.contains(slot);
+ boolean hidden = mIconHideList.contains(slot);
- mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, blocked, holder));
+ mIconGroups.forEach(l -> l.onIconAdded(viewIndex, slot, hidden, holder));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
index 7bcfb46..4de6484 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarter.java
@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.phone;
import static android.service.notification.NotificationListenerService.REASON_CLICK;
-import static android.service.notification.NotificationStats.DISMISS_SENTIMENT_NEUTRAL;
import static com.android.systemui.statusbar.phone.StatusBar.getActivityOptions;
@@ -37,7 +36,6 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.service.dreams.IDreamManager;
-import android.service.notification.NotificationStats;
import android.service.notification.StatusBarNotification;
import android.text.TextUtils;
import android.util.EventLog;
@@ -71,11 +69,11 @@
import com.android.systemui.statusbar.notification.collection.NotifCollection;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
+import com.android.systemui.statusbar.notification.row.OnDismissCallback;
import com.android.systemui.statusbar.policy.HeadsUpUtil;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -128,6 +126,7 @@
private final NotificationPresenter mPresenter;
private final NotificationPanelViewController mNotificationPanel;
private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ private final OnDismissCallback mOnDismissCallback;
private boolean mIsCollapsingToShowActivityOverLockscreen;
@@ -162,6 +161,7 @@
FeatureFlags featureFlags,
MetricsLogger metricsLogger,
StatusBarNotificationActivityStarterLogger logger,
+ OnDismissCallback onDismissCallback,
StatusBar statusBar,
NotificationPresenter presenter,
@@ -197,6 +197,7 @@
mFeatureFlags = featureFlags;
mMetricsLogger = metricsLogger;
mLogger = logger;
+ mOnDismissCallback = onDismissCallback;
// TODO: use KeyguardStateController#isOccluded to remove this dependency
mStatusBar = statusBar;
@@ -574,13 +575,14 @@
private void removeNotification(NotificationEntry entry) {
// We have to post it to the UI thread for synchronization
mMainThreadHandler.post(() -> {
- Runnable removeRunnable = createRemoveRunnable(entry);
if (mPresenter.isCollapsing()) {
// To avoid lags we're only performing the remove
// after the shade was collapsed
- mShadeController.addPostCollapseAction(removeRunnable);
+ mShadeController.addPostCollapseAction(
+ () -> mOnDismissCallback.onDismiss(entry, REASON_CLICK)
+ );
} else {
- removeRunnable.run();
+ mOnDismissCallback.onDismiss(entry, REASON_CLICK);
}
});
}
@@ -595,43 +597,6 @@
}
}
- private Runnable createRemoveRunnable(NotificationEntry entry) {
- if (mFeatureFlags.isNewNotifPipelineRenderingEnabled()) {
- return new Runnable() {
- @Override
- public void run() {
- // see NotificationLogger#logNotificationClear
- int dismissalSurface = NotificationStats.DISMISSAL_SHADE;
- if (mHeadsUpManager.isAlerting(entry.getKey())) {
- dismissalSurface = NotificationStats.DISMISSAL_PEEK;
- } else if (mNotificationPanel.hasPulsingNotifications()) {
- dismissalSurface = NotificationStats.DISMISSAL_AOD;
- }
-
- mNotifCollection.dismissNotification(
- entry,
- new DismissedByUserStats(
- dismissalSurface,
- DISMISS_SENTIMENT_NEUTRAL,
- NotificationVisibility.obtain(
- entry.getKey(),
- entry.getRanking().getRank(),
- mNotifPipeline.getShadeListCount(),
- true,
- NotificationLogger.getNotificationLocation(entry))
- ));
- }
- };
- } else {
- return new Runnable() {
- @Override
- public void run() {
- mEntryManager.performRemoveNotification(entry.getSbn(), REASON_CLICK);
- }
- };
- }
- }
-
/**
* Public builder for {@link StatusBarNotificationActivityStarter}.
*/
@@ -667,6 +632,7 @@
private final FeatureFlags mFeatureFlags;
private final MetricsLogger mMetricsLogger;
private final StatusBarNotificationActivityStarterLogger mLogger;
+ private final OnDismissCallback mOnDismissCallback;
private StatusBar mStatusBar;
private NotificationPresenter mNotificationPresenter;
@@ -704,7 +670,8 @@
FeatureFlags featureFlags,
MetricsLogger metricsLogger,
- StatusBarNotificationActivityStarterLogger logger) {
+ StatusBarNotificationActivityStarterLogger logger,
+ OnDismissCallback onDismissCallback) {
mContext = context;
mCommandQueue = commandQueue;
@@ -736,6 +703,7 @@
mFeatureFlags = featureFlags;
mMetricsLogger = metricsLogger;
mLogger = logger;
+ mOnDismissCallback = onDismissCallback;
}
/** Sets the status bar to use as {@link StatusBar}. */
@@ -793,6 +761,7 @@
mFeatureFlags,
mMetricsLogger,
mLogger,
+ mOnDismissCallback,
mStatusBar,
mNotificationPresenter,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 690d573..7eefaf2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -52,12 +52,12 @@
private final SecurityController mSecurityController;
private final Handler mHandler = Handler.getMain();
- private boolean mBlockAirplane;
- private boolean mBlockMobile;
- private boolean mBlockWifi;
- private boolean mBlockEthernet;
+ private boolean mHideAirplane;
+ private boolean mHideMobile;
+ private boolean mHideWifi;
+ private boolean mHideEthernet;
private boolean mActivityEnabled;
- private boolean mForceBlockWifi;
+ private boolean mForceHideWifi;
// Track as little state as possible, and only for padding purposes
private boolean mIsAirplaneMode = false;
@@ -80,7 +80,7 @@
mNetworkController = Dependency.get(NetworkController.class);
mSecurityController = Dependency.get(SecurityController.class);
- Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
mNetworkController.addCallback(this);
mSecurityController.addCallback(this);
}
@@ -114,21 +114,21 @@
@Override
public void onTuningChanged(String key, String newValue) {
- if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+ if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
return;
}
- ArraySet<String> blockList = StatusBarIconController.getIconBlacklist(mContext, newValue);
- boolean blockAirplane = blockList.contains(mSlotAirplane);
- boolean blockMobile = blockList.contains(mSlotMobile);
- boolean blockWifi = blockList.contains(mSlotWifi);
- boolean blockEthernet = blockList.contains(mSlotEthernet);
+ ArraySet<String> hideList = StatusBarIconController.getIconHideList(mContext, newValue);
+ boolean hideAirplane = hideList.contains(mSlotAirplane);
+ boolean hideMobile = hideList.contains(mSlotMobile);
+ boolean hideWifi = hideList.contains(mSlotWifi);
+ boolean hideEthernet = hideList.contains(mSlotEthernet);
- if (blockAirplane != mBlockAirplane || blockMobile != mBlockMobile
- || blockEthernet != mBlockEthernet || blockWifi != mBlockWifi) {
- mBlockAirplane = blockAirplane;
- mBlockMobile = blockMobile;
- mBlockEthernet = blockEthernet;
- mBlockWifi = blockWifi || mForceBlockWifi;
+ if (hideAirplane != mHideAirplane || hideMobile != mHideMobile
+ || hideEthernet != mHideEthernet || hideWifi != mHideWifi) {
+ mHideAirplane = hideAirplane;
+ mHideMobile = hideMobile;
+ mHideEthernet = hideEthernet;
+ mHideWifi = hideWifi || mForceHideWifi;
// Re-register to get new callbacks.
mNetworkController.removeCallback(this);
mNetworkController.addCallback(this);
@@ -140,7 +140,7 @@
boolean activityIn, boolean activityOut, String description, boolean isTransient,
String statusLabel) {
- boolean visible = statusIcon.visible && !mBlockWifi;
+ boolean visible = statusIcon.visible && !mHideWifi;
boolean in = activityIn && mActivityEnabled && visible;
boolean out = activityOut && mActivityEnabled && visible;
@@ -189,7 +189,7 @@
// Visibility of the data type indicator changed
boolean typeChanged = statusType != state.typeId && (statusType == 0 || state.typeId == 0);
- state.visible = statusIcon.visible && !mBlockMobile;
+ state.visible = statusIcon.visible && !mHideMobile;
state.strengthId = statusIcon.icon;
state.typeId = statusType;
state.contentDescription = statusIcon.contentDescription;
@@ -270,7 +270,7 @@
@Override
public void setEthernetIndicators(IconState state) {
- boolean visible = state.visible && !mBlockEthernet;
+ boolean visible = state.visible && !mHideEthernet;
int resId = state.icon;
String description = state.contentDescription;
@@ -284,7 +284,7 @@
@Override
public void setIsAirplaneMode(IconState icon) {
- mIsAirplaneMode = icon.visible && !mBlockAirplane;
+ mIsAirplaneMode = icon.visible && !mHideAirplane;
int resId = icon.icon;
String description = icon.contentDescription;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
index 673549a..e5a4679 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BatteryController.java
@@ -79,6 +79,13 @@
default void setReverseState(boolean isReverse) {}
/**
+ * Returns {@code true} if extreme battery saver is on.
+ */
+ default boolean isExtremeSaverOn() {
+ return false;
+ }
+
+ /**
* A listener that will be notified whenever a change in battery level or power save mode has
* occurred.
*/
@@ -92,6 +99,9 @@
default void onReverseChanged(boolean isReverse, int level, String name) {
}
+
+ default void onExtremeBatterySaverChanged(boolean isExtreme) {
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
index 9560195..120a0e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Clock.java
@@ -191,7 +191,7 @@
mBroadcastDispatcher.registerReceiverWithHandler(mIntentReceiver, filter,
Dependency.get(Dependency.TIME_TICK_HANDLER), UserHandle.ALL);
Dependency.get(TunerService.class).addTunable(this, CLOCK_SECONDS,
- StatusBarIconController.ICON_BLACKLIST);
+ StatusBarIconController.ICON_HIDE_LIST);
mCommandQueue.addCallback(this);
if (mShowDark) {
Dependency.get(DarkIconDispatcher.class).addDarkReceiver(this);
@@ -305,8 +305,8 @@
if (CLOCK_SECONDS.equals(key)) {
mShowSeconds = TunerService.parseIntegerSwitch(newValue, false);
updateShowSeconds();
- } else {
- setClockVisibleByUser(!StatusBarIconController.getIconBlacklist(getContext(), newValue)
+ } else if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
+ setClockVisibleByUser(!StatusBarIconController.getIconHideList(getContext(), newValue)
.contains("clock"));
updateClockVisibility();
}
@@ -404,7 +404,7 @@
mContentDescriptionFormat = new SimpleDateFormat(format);
/*
* Search for an unquoted "a" in the format string, so we can
- * add dummy characters around it to let us find it again after
+ * add marker characters around it to let us find it again after
* formatting and change its size.
*/
if (mAmPmStyle != AM_PM_STYLE_NORMAL) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
index 66372c3..b71aafd 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
@@ -38,7 +38,7 @@
private final String mBattery;
private boolean mBatteryEnabled;
private boolean mHasPercentage;
- private ArraySet<String> mBlacklist;
+ private ArraySet<String> mHideList;
private boolean mHasSetValue;
public BatteryPreference(Context context, AttributeSet attrs) {
@@ -52,7 +52,7 @@
super.onAttached();
mHasPercentage = Settings.System.getInt(getContext().getContentResolver(),
SHOW_BATTERY_PERCENT, 0) != 0;
- Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
}
@Override
@@ -63,9 +63,9 @@
@Override
public void onTuningChanged(String key, String newValue) {
- if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
- mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
- mBatteryEnabled = !mBlacklist.contains(mBattery);
+ if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
+ mHideList = StatusBarIconController.getIconHideList(getContext(), newValue);
+ mBatteryEnabled = !mHideList.contains(mBattery);
}
if (!mHasSetValue) {
// Because of the complicated tri-state it can end up looping and setting state back to
@@ -88,12 +88,12 @@
MetricsLogger.action(getContext(), MetricsEvent.TUNER_BATTERY_PERCENTAGE, v);
Settings.System.putInt(getContext().getContentResolver(), SHOW_BATTERY_PERCENT, v ? 1 : 0);
if (DISABLED.equals(value)) {
- mBlacklist.add(mBattery);
+ mHideList.add(mBattery);
} else {
- mBlacklist.remove(mBattery);
+ mHideList.remove(mBattery);
}
- Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_BLACKLIST,
- TextUtils.join(",", mBlacklist));
+ Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_HIDE_LIST,
+ TextUtils.join(",", mHideList));
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
index f7d0c9f..c92d7bb 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
@@ -33,7 +33,7 @@
private final String mClock;
private boolean mClockEnabled;
private boolean mHasSeconds;
- private ArraySet<String> mBlacklist;
+ private ArraySet<String> mHideList;
private boolean mHasSetValue;
private boolean mReceivedSeconds;
private boolean mReceivedClock;
@@ -47,7 +47,7 @@
@Override
public void onAttached() {
super.onAttached();
- Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST,
+ Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST,
Clock.CLOCK_SECONDS);
}
@@ -59,10 +59,10 @@
@Override
public void onTuningChanged(String key, String newValue) {
- if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+ if (StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
mReceivedClock = true;
- mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
- mClockEnabled = !mBlacklist.contains(mClock);
+ mHideList = StatusBarIconController.getIconHideList(getContext(), newValue);
+ mClockEnabled = !mHideList.contains(mClock);
} else if (Clock.CLOCK_SECONDS.equals(key)) {
mReceivedSeconds = true;
mHasSeconds = newValue != null && Integer.parseInt(newValue) != 0;
@@ -87,12 +87,12 @@
Dependency.get(TunerService.class).setValue(Clock.CLOCK_SECONDS, SECONDS.equals(value) ? 1
: 0);
if (DISABLED.equals(value)) {
- mBlacklist.add(mClock);
+ mHideList.add(mClock);
} else {
- mBlacklist.remove(mClock);
+ mHideList.remove(mClock);
}
- Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_BLACKLIST,
- TextUtils.join(",", mBlacklist));
+ Dependency.get(TunerService.class).setValue(StatusBarIconController.ICON_HIDE_LIST,
+ TextUtils.join(",", mHideList));
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
index de8ccfa..cc0050b 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/StatusBarSwitch.java
@@ -34,7 +34,7 @@
public class StatusBarSwitch extends SwitchPreference implements Tunable {
- private Set<String> mBlacklist;
+ private Set<String> mHideList;
public StatusBarSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -43,7 +43,7 @@
@Override
public void onAttached() {
super.onAttached();
- Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
+ Dependency.get(TunerService.class).addTunable(this, StatusBarIconController.ICON_HIDE_LIST);
}
@Override
@@ -54,35 +54,35 @@
@Override
public void onTuningChanged(String key, String newValue) {
- if (!StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+ if (!StatusBarIconController.ICON_HIDE_LIST.equals(key)) {
return;
}
- mBlacklist = StatusBarIconController.getIconBlacklist(getContext(), newValue);
- setChecked(!mBlacklist.contains(getKey()));
+ mHideList = StatusBarIconController.getIconHideList(getContext(), newValue);
+ setChecked(!mHideList.contains(getKey()));
}
@Override
protected boolean persistBoolean(boolean value) {
if (!value) {
- // If not enabled add to blacklist.
- if (!mBlacklist.contains(getKey())) {
+ // If not enabled add to hideList.
+ if (!mHideList.contains(getKey())) {
MetricsLogger.action(getContext(), MetricsEvent.TUNER_STATUS_BAR_DISABLE,
getKey());
- mBlacklist.add(getKey());
- setList(mBlacklist);
+ mHideList.add(getKey());
+ setList(mHideList);
}
} else {
- if (mBlacklist.remove(getKey())) {
+ if (mHideList.remove(getKey())) {
MetricsLogger.action(getContext(), MetricsEvent.TUNER_STATUS_BAR_ENABLE, getKey());
- setList(mBlacklist);
+ setList(mHideList);
}
}
return true;
}
- private void setList(Set<String> blacklist) {
+ private void setList(Set<String> hideList) {
ContentResolver contentResolver = getContext().getContentResolver();
- Settings.Secure.putStringForUser(contentResolver, StatusBarIconController.ICON_BLACKLIST,
- TextUtils.join(",", blacklist), ActivityManager.getCurrentUser());
+ Settings.Secure.putStringForUser(contentResolver, StatusBarIconController.ICON_HIDE_LIST,
+ TextUtils.join(",", hideList), ActivityManager.getCurrentUser());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
index 9ad2aa2..644f758 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerServiceImpl.java
@@ -60,7 +60,7 @@
// Things that use the tunable infrastructure but are now real user settings and
// shouldn't be reset with tuner settings.
- private static final String[] RESET_BLACKLIST = new String[] {
+ private static final String[] RESET_EXCEPTION_LIST = new String[] {
QSTileHost.TILES_SETTING,
Settings.Secure.DOZE_ALWAYS_ON,
Settings.Secure.MEDIA_CONTROLS_RESUME
@@ -116,17 +116,17 @@
private void upgradeTuner(int oldVersion, int newVersion, Handler mainHandler) {
if (oldVersion < 1) {
- String blacklistStr = getValue(StatusBarIconController.ICON_BLACKLIST);
- if (blacklistStr != null) {
- ArraySet<String> iconBlacklist =
- StatusBarIconController.getIconBlacklist(mContext, blacklistStr);
+ String hideListStr = getValue(StatusBarIconController.ICON_HIDE_LIST);
+ if (hideListStr != null) {
+ ArraySet<String> iconHideList =
+ StatusBarIconController.getIconHideList(mContext, hideListStr);
- iconBlacklist.add("rotate");
- iconBlacklist.add("headset");
+ iconHideList.add("rotate");
+ iconHideList.add("headset");
Settings.Secure.putStringForUser(mContentResolver,
- StatusBarIconController.ICON_BLACKLIST,
- TextUtils.join(",", iconBlacklist), mCurrentUser);
+ StatusBarIconController.ICON_HIDE_LIST,
+ TextUtils.join(",", iconHideList), mCurrentUser);
}
}
if (oldVersion < 2) {
@@ -251,7 +251,7 @@
mContext.sendBroadcast(intent);
for (String key : mTunableLookup.keySet()) {
- if (ArrayUtils.contains(RESET_BLACKLIST, key)) {
+ if (ArrayUtils.contains(RESET_EXCEPTION_LIST, key)) {
continue;
}
Settings.Secure.putStringForUser(mContentResolver, key, null, user);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
index 15828b4..b758953 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleControllerTest.java
@@ -321,7 +321,7 @@
Bubble b = mBubbleData.getOverflowBubbleWithKey(mRow.getEntry().getKey());
assertThat(mBubbleData.getOverflowBubbles()).isEqualTo(ImmutableList.of(b));
verify(mNotificationEntryManager, never()).performRemoveNotification(
- eq(mRow.getEntry().getSbn()), anyInt());
+ eq(mRow.getEntry().getSbn()), any(), anyInt());
assertThat(mRow.getEntry().isBubble()).isFalse();
Bubble b2 = mBubbleData.getBubbleInStackWithKey(mRow2.getEntry().getKey());
@@ -352,7 +352,7 @@
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_NOTIF_CANCEL);
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- eq(mRow.getEntry().getSbn()), anyInt());
+ eq(mRow.getEntry().getSbn()), any(), anyInt());
assertThat(mBubbleData.getOverflowBubbles()).isEmpty();
assertFalse(mRow.getEntry().isBubble());
}
@@ -365,7 +365,7 @@
mBubbleController.removeBubble(
mRow.getEntry().getKey(), BubbleController.DISMISS_USER_CHANGED);
verify(mNotificationEntryManager, never()).performRemoveNotification(
- eq(mRow.getEntry().getSbn()), anyInt());
+ eq(mRow.getEntry().getSbn()), any(), anyInt());
assertFalse(mBubbleController.hasBubbles());
assertFalse(mSysUiStateBubblesExpanded);
assertTrue(mRow.getEntry().isBubble());
@@ -873,7 +873,7 @@
mRow2.getEntry().getKey(), BubbleController.DISMISS_USER_GESTURE);
// Overflow max of 1 is reached; mRow is oldest, so it gets removed
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- mRow.getEntry().getSbn(), REASON_CANCEL);
+ eq(mRow.getEntry().getSbn()), any(), eq(REASON_CANCEL));
assertEquals(mBubbleData.getBubbles().size(), 1);
assertEquals(mBubbleData.getOverflowBubbles().size(), 1);
}
@@ -985,11 +985,13 @@
// THEN only the NON-bubble children are dismissed
List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- childrenRows.get(0).getEntry().getSbn(), REASON_GROUP_SUMMARY_CANCELED);
+ eq(childrenRows.get(0).getEntry().getSbn()), any(),
+ eq(REASON_GROUP_SUMMARY_CANCELED));
verify(mNotificationEntryManager, times(1)).performRemoveNotification(
- childrenRows.get(1).getEntry().getSbn(), REASON_GROUP_SUMMARY_CANCELED);
+ eq(childrenRows.get(1).getEntry().getSbn()), any(),
+ eq(REASON_GROUP_SUMMARY_CANCELED));
verify(mNotificationEntryManager, never()).performRemoveNotification(
- eq(groupedBubble.getEntry().getSbn()), anyInt());
+ eq(groupedBubble.getEntry().getSbn()), any(), anyInt());
// THEN the bubble child is suppressed from the shade
assertTrue(mBubbleController.isBubbleNotificationSuppressedFromShade(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
index 686a094..43bf191 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/bubbles/NewNotifPipelineBubbleControllerTest.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -301,7 +302,7 @@
assertTrue(mBubbleData.hasOverflowBubbleWithKey(mRow.getEntry().getKey()));
// We don't remove the notification since the bubble is still in overflow.
- verify(mNotifCallback, never()).removeNotification(eq(mRow.getEntry()), anyInt());
+ verify(mNotifCallback, never()).removeNotification(eq(mRow.getEntry()), any(), anyInt());
assertFalse(mBubbleController.hasBubbles());
}
@@ -325,7 +326,8 @@
// Since the notif is dismissed and not in overflow, once the bubble is removed,
// removeNotification gets called to really remove the notif
- verify(mNotifCallback, times(1)).removeNotification(eq(mRow.getEntry()), anyInt());
+ verify(mNotifCallback, times(1)).removeNotification(eq(mRow.getEntry()),
+ any(), anyInt());
assertFalse(mBubbleController.hasBubbles());
}
@@ -831,10 +833,11 @@
// THEN only the NON-bubble children are dismissed
List<ExpandableNotificationRow> childrenRows = groupSummary.getAttachedChildren();
verify(mNotifCallback, times(1)).removeNotification(
- childrenRows.get(0).getEntry(), REASON_GROUP_SUMMARY_CANCELED);
+ eq(childrenRows.get(0).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
verify(mNotifCallback, times(1)).removeNotification(
- childrenRows.get(1).getEntry(), REASON_GROUP_SUMMARY_CANCELED);
- verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()), anyInt());
+ eq(childrenRows.get(1).getEntry()), any(), eq(REASON_GROUP_SUMMARY_CANCELED));
+ verify(mNotifCallback, never()).removeNotification(eq(groupedBubble.getEntry()),
+ any(), anyInt());
// THEN the bubble child still exists as a bubble and is suppressed from the shade
assertTrue(mBubbleData.hasBubbleInStackWithKey(groupedBubble.getEntry().getKey()));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
index 7155460..7d8728e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/SeekBarObserverTest.kt
@@ -94,11 +94,11 @@
@Test
fun seekBarProgress() {
- // WHEN seek bar progress is about half
+ // WHEN part of the track has been played
val data = SeekBarViewModel.Progress(true, true, 3000, 120000)
observer.onChanged(data)
- // THEN seek bar is visible
- assertThat(seekBarView.progress).isEqualTo(100)
+ // THEN seek bar shows the progress
+ assertThat(seekBarView.progress).isEqualTo(3000)
assertThat(seekBarView.max).isEqualTo(120000)
assertThat(elapsedTimeView.getText()).isEqualTo("00:03")
assertThat(totalTimeView.getText()).isEqualTo("02:00")
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
index 583d069..73164b5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedAnimationControllerTest.java
@@ -44,6 +44,7 @@
private static final int TEST_BOUNDS_HEIGHT = 1000;
OneHandedAnimationController mOneHandedAnimationController;
+ OneHandedTutorialHandler mTutorialHandler;
@Mock
private SurfaceControl mMockLeash;
@@ -52,6 +53,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
+ mTutorialHandler = new OneHandedTutorialHandler(mContext);
mOneHandedAnimationController = new OneHandedAnimationController(
new OneHandedSurfaceTransactionHelper(mContext));
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 3231b28..a989cd1f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -61,6 +61,7 @@
DisplayAreaInfo mDisplayAreaInfo;
Display mDisplay;
OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer;
+ OneHandedTutorialHandler mTutorialHandler;
OneHandedAnimationController.OneHandedTransitionAnimator mFakeAnimator;
WindowContainerToken mToken;
SurfaceControl mLeash;
@@ -97,14 +98,15 @@
mMockSurfaceTransactionHelper);
when(mMockAnimator.isRunning()).thenReturn(true);
when(mMockAnimator.setDuration(anyInt())).thenReturn(mFakeAnimator);
- when(mMockAnimator.setOneHandedAnimationCallback(any())).thenReturn(mFakeAnimator);
+ when(mMockAnimator.setOneHandedAnimationCallbacks(any())).thenReturn(mFakeAnimator);
when(mMockAnimator.setTransitionDirection(anyInt())).thenReturn(mFakeAnimator);
when(mMockLeash.getWidth()).thenReturn(DISPLAY_WIDTH);
when(mMockLeash.getHeight()).thenReturn(DISPLAY_HEIGHT);
mDisplayAreaOrganizer = new OneHandedDisplayAreaOrganizer(mContext,
mMockDisplayController,
- mMockAnimationController);
+ mMockAnimationController,
+ mTutorialHandler);
mUpdateHandler = mDisplayAreaOrganizer.getUpdateHandler();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
index 3b284b14..694f51b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedGestureHandlerTest.java
@@ -48,6 +48,7 @@
public class OneHandedGestureHandlerTest extends OneHandedTestCase {
Instrumentation mInstrumentation;
OneHandedTouchHandler mTouchHandler;
+ OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
OneHandedManagerImpl mOneHandedManagerImpl;
@Mock
@@ -62,13 +63,15 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mInstrumentation = InstrumentationRegistry.getInstrumentation();
- mTouchHandler = Mockito.spy(new OneHandedTouchHandler());
+ mTouchHandler = new OneHandedTouchHandler();
+ mTutorialHandler = new OneHandedTutorialHandler(mContext);
mGestureHandler = Mockito.spy(new OneHandedGestureHandler(
mContext, mMockDisplayController, mMockNavigationModeController));
mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(),
mMockDisplayController,
mMockDisplayAreaOrganizer,
mTouchHandler,
+ mTutorialHandler,
mGestureHandler,
mMockSysUiState);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
index 55bec54..3418ebf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedManagerImplTest.java
@@ -57,6 +57,8 @@
@Mock
OneHandedTouchHandler mMockTouchHandler;
@Mock
+ OneHandedTutorialHandler mMockTutorialHandler;
+ @Mock
OneHandedGestureHandler mMockGestureHandler;
@Mock
SysUiState mMockSysUiState;
@@ -69,6 +71,7 @@
mMockDisplayController,
mMockDisplayAreaOrganizer,
mMockTouchHandler,
+ mMockTutorialHandler,
mMockGestureHandler,
mMockSysUiState);
mTimeoutHandler = Mockito.spy(OneHandedTimeoutHandler.get());
@@ -84,7 +87,7 @@
final OneHandedAnimationController animationController = new OneHandedAnimationController(
transactionHelper);
OneHandedDisplayAreaOrganizer displayAreaOrganizer = new OneHandedDisplayAreaOrganizer(
- mContext, mMockDisplayController, animationController);
+ mContext, mMockDisplayController, animationController, mMockTutorialHandler);
assertThat(displayAreaOrganizer.isInOneHanded()).isFalse();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
index 3a4ba6a..fdb28d3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTouchHandlerTest.java
@@ -46,6 +46,7 @@
public class OneHandedTouchHandlerTest extends OneHandedTestCase {
Instrumentation mInstrumentation;
OneHandedTouchHandler mTouchHandler;
+ OneHandedTutorialHandler mTutorialHandler;
OneHandedGestureHandler mGestureHandler;
OneHandedManagerImpl mOneHandedManagerImpl;
@Mock
@@ -68,13 +69,15 @@
mMockDisplayController,
mMockDisplayAreaOrganizer,
mTouchHandler,
+ mTutorialHandler,
mGestureHandler,
mMockSysUiState);
}
@Test
public void testOneHandedManager_registerForDisplayAreaOrganizer() {
- verify(mMockDisplayAreaOrganizer, times(1)).registerTransitionCallback(mTouchHandler);
+ verify(mMockDisplayAreaOrganizer, times(1))
+ .registerTransitionCallback(mTouchHandler);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
new file mode 100644
index 0000000..f4aa00e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/onehanded/OneHandedTutorialHandlerTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.onehanded;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import com.android.systemui.model.SysUiState;
+import com.android.systemui.statusbar.phone.NavigationModeController;
+import com.android.wm.shell.common.DisplayController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class OneHandedTutorialHandlerTest extends OneHandedTestCase {
+ Instrumentation mInstrumentation;
+ OneHandedTouchHandler mTouchHandler;
+ OneHandedTutorialHandler mTutorialHandler;
+ OneHandedGestureHandler mGestureHandler;
+ OneHandedManagerImpl mOneHandedManagerImpl;
+ @Mock
+ DisplayController mMockDisplayController;
+ @Mock
+ NavigationModeController mMockNavigationModeController;
+ @Mock
+ OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer;
+ @Mock
+ SysUiState mMockSysUiState;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mTouchHandler = new OneHandedTouchHandler();
+ mTutorialHandler = Mockito.spy(new OneHandedTutorialHandler(mContext));
+ mGestureHandler = new OneHandedGestureHandler(mContext, mMockDisplayController,
+ mMockNavigationModeController);
+ mOneHandedManagerImpl = new OneHandedManagerImpl(mInstrumentation.getContext(),
+ mMockDisplayController,
+ mMockDisplayAreaOrganizer,
+ mTouchHandler,
+ mTutorialHandler,
+ mGestureHandler,
+ mMockSysUiState);
+ }
+
+ @Test
+ public void testOneHandedManager_registerForDisplayAreaOrganizer() {
+ verify(mMockDisplayAreaOrganizer, times(1))
+ .registerTransitionCallback(mTutorialHandler);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
index 8a49326..d4718e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationEntryManagerTest.java
@@ -57,11 +57,11 @@
import androidx.annotation.NonNull;
import androidx.test.filters.SmallTest;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLifetimeExtender;
import com.android.systemui.statusbar.NotificationMediaManager;
@@ -75,6 +75,7 @@
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.NotificationRankingManager;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
+import com.android.systemui.statusbar.notification.collection.notifcollection.DismissedByUserStats;
import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -202,8 +203,7 @@
() -> mRemoteInputManager,
mLeakDetector,
mock(ForegroundServiceDismissalFeatureController.class),
- mock(HeadsUpManager.class),
- mock(StatusBarStateController.class)
+ mock(IStatusBarService.class)
);
mEntryManager.setUpWithPresenter(mPresenter);
mEntryManager.addNotificationEntryListener(mEntryListener);
@@ -294,7 +294,8 @@
assertTrue(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
// WHEN the uninflated entry is removed
- mEntryManager.performRemoveNotification(mSbn, UNDEFINED_DISMISS_REASON);
+ mEntryManager.performRemoveNotification(mSbn, mock(DismissedByUserStats.class),
+ UNDEFINED_DISMISS_REASON);
// THEN the entry is still removed from the allNotifications list
assertFalse(entriesContainKey(mEntryManager.getAllNotifs(), mSbn.getKey()));
@@ -470,7 +471,8 @@
@Test
public void testPerformRemoveNotification_removedEntry() {
mEntryManager.removeNotification(mSbn.getKey(), null, 0);
- mEntryManager.performRemoveNotification(mSbn, REASON_CANCEL);
+ mEntryManager.performRemoveNotification(mSbn, mock(DismissedByUserStats.class),
+ REASON_CANCEL);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index 601df2c..a90af87 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -40,6 +40,7 @@
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import androidx.test.filters.SmallTest;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
@@ -185,8 +186,7 @@
() -> mRemoteInputManager,
mLeakDetector,
mock(ForegroundServiceDismissalFeatureController.class),
- mock(HeadsUpManager.class),
- mock(StatusBarStateController.class)
+ mock(IStatusBarService.class)
);
NotifRemoteViewCache cache = new NotifRemoteViewCacheImpl(mEntryManager);
@@ -252,7 +252,6 @@
mLockscreenUserManager,
pipeline,
mRowContentBindStage,
- mNotificationInterruptionStateProvider,
RowInflaterTask::new,
mExpandableNotificationRowComponentBuilder,
new IconManager(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index 6d411333..ddac2ec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -49,13 +49,13 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.logging.testing.UiEventLoggerFake;
+import com.android.internal.statusbar.IStatusBarService;
import com.android.systemui.ExpandHelper;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.media.KeyguardMediaController;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.EmptyShadeView;
import com.android.systemui.statusbar.FeatureFlags;
import com.android.systemui.statusbar.NotificationLockscreenUserManager;
@@ -93,7 +93,6 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.ShadeController;
import com.android.systemui.statusbar.phone.StatusBar;
-import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.ZenModeController;
import com.android.systemui.util.leak.LeakDetector;
@@ -193,8 +192,7 @@
() -> mRemoteInputManager,
mock(LeakDetector.class),
mock(ForegroundServiceDismissalFeatureController.class),
- mock(HeadsUpManager.class),
- mock(StatusBarStateController.class)
+ mock(IStatusBarService.class)
);
mEntryManager.setUpWithPresenter(mock(NotificationPresenter.class));
when(mFeatureFlags.isNewNotifPipelineRenderingEnabled()).thenReturn(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
index acdb2c5..3306734 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterTest.java
@@ -73,6 +73,7 @@
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
+import com.android.systemui.statusbar.notification.row.OnDismissCallback;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
@@ -131,6 +132,8 @@
@Mock
private Intent mContentIntentInner;
@Mock
+ private OnDismissCallback mOnDismissCallback;
+ @Mock
private NotificationActivityStarter mNotificationActivityStarter;
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -207,7 +210,8 @@
mFeatureFlags,
mock(MetricsLogger.class),
- mock(StatusBarNotificationActivityStarterLogger.class))
+ mock(StatusBarNotificationActivityStarterLogger.class),
+ mOnDismissCallback)
.setStatusBar(mStatusBar)
.setNotificationPresenter(mock(NotificationPresenter.class))
.setNotificationPanelViewController(mock(NotificationPanelViewController.class))
@@ -266,8 +270,8 @@
verify(mClickNotifier).onNotificationClick(
eq(sbn.getKey()), any(NotificationVisibility.class));
- // Notification is removed due to FLAG_AUTO_CANCEL
- verify(mEntryManager).performRemoveNotification(eq(sbn), eq(REASON_CLICK));
+ // Notification calls dismiss callback to remove notification due to FLAG_AUTO_CANCEL
+ verify(mOnDismissCallback).onDismiss(mNotificationRow.getEntry(), REASON_CLICK);
}
@Test
@@ -296,7 +300,7 @@
verifyZeroInteractions(mContentIntent);
// Notification should not be cancelled.
- verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
+ verify(mOnDismissCallback, never()).onDismiss(eq(mNotificationRow.getEntry()), anyInt());
}
@Test
@@ -326,7 +330,7 @@
verifyZeroInteractions(mContentIntent);
// Notification should not be cancelled.
- verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
+ verify(mEntryManager, never()).performRemoveNotification(eq(sbn), any(), anyInt());
}
@Test
@@ -358,6 +362,6 @@
verifyNoMoreInteractions(mContentIntent);
// Notification should not be cancelled.
- verify(mEntryManager, never()).performRemoveNotification(eq(sbn), anyInt());
+ verify(mEntryManager, never()).performRemoveNotification(eq(sbn), any(), anyInt());
}
}
diff --git a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
index 103151d..7ee607c 100644
--- a/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
+++ b/services/appprediction/java/com/android/server/appprediction/AppPredictionPerUserService.java
@@ -219,7 +219,7 @@
if (connected) {
synchronized (mLock) {
if (mZombie) {
- // Sanity check - shouldn't happen
+ // Validation check - shouldn't happen
if (mRemoteService == null) {
Slog.w(TAG, "Cannot resurrect sessions because remote service is null");
return;
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index 5a9320f..b0755ac 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -161,8 +161,9 @@
@Override
public void onFailure(int requestId, CharSequence message) {
+ String errorMessage = message == null ? "" : String.valueOf(message);
fillRequest.completeExceptionally(
- new RuntimeException(String.valueOf(message)));
+ new RuntimeException(errorMessage));
}
});
return fillRequest;
diff --git a/services/backup/java/com/android/server/backup/UserBackupManagerService.java b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
index 2de26b7..3ab81cb 100644
--- a/services/backup/java/com/android/server/backup/UserBackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/UserBackupManagerService.java
@@ -2307,7 +2307,7 @@
public void enqueueFullBackup(String packageName, long lastBackedUp) {
FullBackupEntry newEntry = new FullBackupEntry(packageName, lastBackedUp);
synchronized (mQueueLock) {
- // First, sanity check that we aren't adding a duplicate. Slow but
+ // First, check that we aren't adding a duplicate. Slow but
// straightforward; we'll have at most on the order of a few hundred
// items in this list.
dequeueFullBackupLocked(packageName);
diff --git a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
index 82bed3b..e42d3bd 100644
--- a/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
+++ b/services/backup/java/com/android/server/backup/restore/FullRestoreEngine.java
@@ -379,7 +379,7 @@
}
}
- // Sanity check: make sure we never give data to the wrong app. This
+ // Make sure we never give data to the wrong app. This
// should never happen but a little paranoia here won't go amiss.
if (okay && !pkg.equals(mAgentPackage)) {
Slog.e(TAG, "Restoring data for " + pkg
diff --git a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
index 1629215..ee05c2b 100644
--- a/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
+++ b/services/backup/java/com/android/server/backup/utils/BackupEligibilityRules.java
@@ -37,7 +37,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.backup.IBackupTransport;
import com.android.internal.util.ArrayUtils;
-import com.android.server.LocalServices;
import com.android.server.backup.transport.TransportClient;
import com.google.android.collect.Sets;
@@ -49,8 +48,8 @@
*/
public class BackupEligibilityRules {
private static final boolean DEBUG = false;
- // Whitelist of system packages that are eligible for backup in non-system users.
- private static final Set<String> systemPackagesWhitelistedForAllUsers =
+ // List of system packages that are eligible for backup in non-system users.
+ private static final Set<String> systemPackagesAllowedForAllUsers =
Sets.newArraySet(PACKAGE_MANAGER_SENTINEL, PLATFORM_PACKAGE_NAME);
private final PackageManager mPackageManager;
@@ -97,9 +96,10 @@
// 2. they run as a system-level uid
if (UserHandle.isCore(app.uid)) {
- // and the backup is happening for non-system user on a non-whitelisted package.
+ // and the backup is happening for a non-system user on a package that is not explicitly
+ // allowed.
if (mUserId != UserHandle.USER_SYSTEM
- && !systemPackagesWhitelistedForAllUsers.contains(app.packageName)) {
+ && !systemPackagesAllowedForAllUsers.contains(app.packageName)) {
return false;
}
diff --git a/services/core/java/com/android/server/ServiceWatcher.java b/services/core/java/com/android/server/ServiceWatcher.java
index e5d0021..0038dc2 100644
--- a/services/core/java/com/android/server/ServiceWatcher.java
+++ b/services/core/java/com/android/server/ServiceWatcher.java
@@ -80,10 +80,16 @@
default void onError() {}
}
+ /** Function to run on binder interface when first bound. */
+ public interface OnBindRunner {
+ /** Called to run client code with the binder. */
+ void run(IBinder binder, ComponentName service) throws RemoteException;
+ }
+
/**
* Information on the service ServiceWatcher has selected as the best option for binding.
*/
- public static final class ServiceInfo implements Comparable<ServiceInfo> {
+ private static final class ServiceInfo implements Comparable<ServiceInfo> {
public static final ServiceInfo NONE = new ServiceInfo(Integer.MIN_VALUE, null,
UserHandle.USER_NULL, false);
@@ -179,25 +185,25 @@
private final Handler mHandler;
private final Intent mIntent;
- @Nullable private final BinderRunner mOnBind;
+ @Nullable private final OnBindRunner mOnBind;
@Nullable private final Runnable mOnUnbind;
// read/write from handler thread only
private int mCurrentUserId;
// write from handler thread only, read anywhere
- private volatile ServiceInfo mServiceInfo;
+ private volatile ServiceInfo mTargetService;
private volatile IBinder mBinder;
public ServiceWatcher(Context context, String action,
- @Nullable BinderRunner onBind, @Nullable Runnable onUnbind,
+ @Nullable OnBindRunner onBind, @Nullable Runnable onUnbind,
@BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
this(context, FgThread.getHandler(), action, onBind, onUnbind, enableOverlayResId,
nonOverlayPackageResId);
}
public ServiceWatcher(Context context, Handler handler, String action,
- @Nullable BinderRunner onBind, @Nullable Runnable onUnbind,
+ @Nullable OnBindRunner onBind, @Nullable Runnable onUnbind,
@BoolRes int enableOverlayResId, @StringRes int nonOverlayPackageResId) {
mContext = context;
mHandler = handler;
@@ -214,7 +220,7 @@
mCurrentUserId = UserHandle.USER_NULL;
- mServiceInfo = ServiceInfo.NONE;
+ mTargetService = ServiceInfo.NONE;
mBinder = null;
}
@@ -304,7 +310,7 @@
}
}
- if (forceRebind || !bestServiceInfo.equals(mServiceInfo)) {
+ if (forceRebind || !bestServiceInfo.equals(mTargetService)) {
rebind(bestServiceInfo);
}
}
@@ -312,32 +318,32 @@
private void rebind(ServiceInfo newServiceInfo) {
Preconditions.checkState(Looper.myLooper() == mHandler.getLooper());
- if (!mServiceInfo.equals(ServiceInfo.NONE)) {
+ if (!mTargetService.equals(ServiceInfo.NONE)) {
if (D) {
- Log.i(TAG, "[" + mIntent.getAction() + "] unbinding from " + mServiceInfo);
+ Log.i(TAG, "[" + mIntent.getAction() + "] unbinding from " + mTargetService);
}
mContext.unbindService(this);
- onServiceDisconnected(mServiceInfo.component);
- mServiceInfo = ServiceInfo.NONE;
+ onServiceDisconnected(mTargetService.component);
+ mTargetService = ServiceInfo.NONE;
}
- mServiceInfo = newServiceInfo;
- if (mServiceInfo.equals(ServiceInfo.NONE)) {
+ mTargetService = newServiceInfo;
+ if (mTargetService.equals(ServiceInfo.NONE)) {
return;
}
- Preconditions.checkState(mServiceInfo.component != null);
+ Preconditions.checkState(mTargetService.component != null);
if (D) {
- Log.i(TAG, getLogPrefix() + " binding to " + mServiceInfo);
+ Log.i(TAG, getLogPrefix() + " binding to " + mTargetService);
}
- Intent bindIntent = new Intent(mIntent).setComponent(mServiceInfo.component);
+ Intent bindIntent = new Intent(mIntent).setComponent(mTargetService.component);
if (!mContext.bindServiceAsUser(bindIntent, this,
BIND_AUTO_CREATE | BIND_NOT_FOREGROUND | BIND_NOT_VISIBLE,
- mHandler, UserHandle.of(mServiceInfo.userId))) {
- mServiceInfo = ServiceInfo.NONE;
+ mHandler, UserHandle.of(mTargetService.userId))) {
+ mTargetService = ServiceInfo.NONE;
Log.e(TAG, getLogPrefix() + " unexpected bind failure - retrying later");
mHandler.postDelayed(() -> onBestServiceChanged(false), RETRY_DELAY_MS);
}
@@ -355,11 +361,11 @@
mBinder = binder;
if (mOnBind != null) {
try {
- mOnBind.run(binder);
+ mOnBind.run(binder, component);
} catch (RuntimeException | RemoteException e) {
// binders may propagate some specific non-RemoteExceptions from the other side
// through the binder as well - we cannot allow those to crash the system server
- Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
+ Log.e(TAG, getLogPrefix() + " exception running on " + component, e);
}
}
}
@@ -406,7 +412,7 @@
void onPackageChanged(String packageName) {
// force a rebind if the changed package was the currently connected package
- onBestServiceChanged(packageName.equals(mServiceInfo.getPackageName()));
+ onBestServiceChanged(packageName.equals(mTargetService.getPackageName()));
}
/**
@@ -425,7 +431,7 @@
} catch (RuntimeException | RemoteException e) {
// binders may propagate some specific non-RemoteExceptions from the other side
// through the binder as well - we cannot allow those to crash the system server
- Log.e(TAG, getLogPrefix() + " exception running on " + mServiceInfo, e);
+ Log.e(TAG, getLogPrefix() + " exception running on " + mTargetService, e);
runner.onError();
}
});
@@ -437,14 +443,14 @@
@Override
public String toString() {
- return mServiceInfo.toString();
+ return mTargetService.toString();
}
/**
* Dump for debugging.
*/
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- pw.println("service=" + mServiceInfo);
+ pw.println("target service=" + mTargetService);
pw.println("connected=" + (mBinder != null));
}
}
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 33a92e6..1680963 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -2559,12 +2559,12 @@
private int getAllowMode(Intent service, @Nullable String callingPackage) {
if (callingPackage == null || service.getComponent() == null) {
- return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+ return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL;
}
if (callingPackage.equals(service.getComponent().getPackageName())) {
- return ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
+ return ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL;
} else {
- return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+ return ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL;
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 43e3a04..d9fde0f 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -16,8 +16,6 @@
package com.android.server.am;
-import static android.os.Process.THREAD_PRIORITY_FOREGROUND;
-
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_COMPACTION;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FREEZER;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
@@ -256,7 +254,7 @@
ProcessDependencies processDependencies) {
mAm = am;
mCachedAppOptimizerThread = new ServiceThread("CachedAppOptimizerThread",
- THREAD_PRIORITY_FOREGROUND, true);
+ Process.THREAD_GROUP_SYSTEM, true);
mProcStateThrottle = new HashSet<>();
mProcessDependencies = processDependencies;
mTestCallback = callback;
@@ -280,8 +278,6 @@
updateProcStateThrottle();
updateUseFreezer();
}
- Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
- Process.THREAD_GROUP_SYSTEM);
}
/**
@@ -411,12 +407,15 @@
mUseCompaction = DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_USE_COMPACTION, DEFAULT_USE_COMPACTION);
- if (mUseCompaction) {
+ if (mUseCompaction && mCompactionHandler == null) {
if (!mCachedAppOptimizerThread.isAlive()) {
mCachedAppOptimizerThread.start();
}
mCompactionHandler = new MemCompactionHandler();
+
+ Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
+ Process.THREAD_GROUP_SYSTEM);
}
}
@@ -470,13 +469,16 @@
mUseFreezer = isFreezerSupported();
}
- if (mUseFreezer) {
+ if (mUseFreezer && mFreezeHandler == null) {
Slog.d(TAG_AM, "Freezer enabled");
if (!mCachedAppOptimizerThread.isAlive()) {
mCachedAppOptimizerThread.start();
}
mFreezeHandler = new FreezeHandler();
+
+ Process.setThreadGroupAndCpuset(mCachedAppOptimizerThread.getThreadId(),
+ Process.THREAD_GROUP_SYSTEM);
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 0658e81..19b671e 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -23,10 +23,11 @@
import static android.app.ActivityManager.USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
import static android.app.ActivityManager.USER_OP_IS_CURRENT;
import static android.app.ActivityManager.USER_OP_SUCCESS;
-import static android.app.ActivityManagerInternal.ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL;
+import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL;
import static android.app.ActivityManagerInternal.ALLOW_FULL_ONLY;
import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
-import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL_IN_PROFILE_OR_FULL;
import static android.os.Process.SHELL_UID;
import static android.os.Process.SYSTEM_UID;
@@ -1909,11 +1910,12 @@
callingUid, -1, true) != PackageManager.PERMISSION_GRANTED) {
// If the caller does not have either permission, they are always doomed.
allow = false;
- } else if (allowMode == ALLOW_NON_FULL) {
+ } else if (allowMode == ALLOW_NON_FULL
+ || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL) {
// We are blanket allowing non-full access, you lucky caller!
allow = true;
- } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE
- || allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ } else if (allowMode == ALLOW_NON_FULL_IN_PROFILE_OR_FULL
+ || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL) {
// We may or may not allow this depending on whether the two users are
// in the same profile.
allow = isSameProfileGroup;
@@ -1940,12 +1942,15 @@
builder.append("; this requires ");
builder.append(INTERACT_ACROSS_USERS_FULL);
if (allowMode != ALLOW_FULL_ONLY) {
- if (allowMode == ALLOW_NON_FULL || isSameProfileGroup) {
+ if (allowMode == ALLOW_NON_FULL
+ || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL
+ || isSameProfileGroup) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_USERS);
}
if (isSameProfileGroup
- && allowMode == ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ && (allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL
+ || allowMode == ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL)) {
builder.append(" or ");
builder.append(INTERACT_ACROSS_PROFILES);
}
@@ -1972,7 +1977,8 @@
private boolean canInteractWithAcrossProfilesPermission(
int allowMode, boolean isSameProfileGroup, int callingPid, int callingUid,
String callingPackage) {
- if (allowMode != ALLOW_ALL_PROFILE_PERMISSIONS_IN_PROFILE) {
+ if (allowMode != ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_FULL
+ && allowMode != ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL) {
return false;
}
if (!isSameProfileGroup) {
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index e6480fc..74f3daf 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -19,6 +19,7 @@
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_CAMERA;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_LOCATION;
import static android.app.ActivityManager.PROCESS_CAPABILITY_FOREGROUND_MICROPHONE;
+import static android.app.ActivityManagerInternal.ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL;
import static android.app.AppOpsManager.CALL_BACK_ON_SWITCHED_OP;
import static android.app.AppOpsManager.FILTER_BY_ATTRIBUTION_TAG;
import static android.app.AppOpsManager.FILTER_BY_OP_NAMES;
@@ -128,6 +129,7 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AtomicFile;
+import android.util.EventLog;
import android.util.KeyValueListParser;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -161,6 +163,7 @@
import com.android.server.LockGuard;
import com.android.server.SystemServerInitThreadPool;
import com.android.server.SystemServiceManager;
+import com.android.server.am.ActivityManagerService;
import com.android.server.pm.PackageList;
import com.android.server.pm.parsing.pkg.AndroidPackage;
@@ -2197,8 +2200,11 @@
+ " by uid " + Binder.getCallingUid());
}
+ int userId = UserHandle.getUserId(uid);
+
enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingOp(code);
+ verifyIncomingUser(userId);
code = AppOpsManager.opToSwitch(code);
if (permissionPolicyCallback == null) {
@@ -2443,8 +2449,12 @@
private void setMode(int code, int uid, @NonNull String packageName, int mode,
@Nullable IAppOpsCallback permissionPolicyCallback) {
enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
+
+ int userId = UserHandle.getUserId(uid);
+
verifyIncomingOp(code);
- verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+ verifyIncomingUser(userId);
+ verifyIncomingPackage(packageName, userId);
ArraySet<ModeCallback> repCbs = null;
code = AppOpsManager.opToSwitch(code);
@@ -2857,8 +2867,11 @@
private int checkOperationImpl(int code, int uid, String packageName,
boolean raw) {
+ int userId = UserHandle.getUserId(uid);
+
verifyIncomingOp(code);
- verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+ verifyIncomingUser(userId);
+ verifyIncomingPackage(packageName, userId);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -2977,10 +2990,15 @@
String proxiedAttributionTag, int proxyUid, String proxyPackageName,
String proxyAttributionTag, boolean shouldCollectAsyncNotedOp, String message,
boolean shouldCollectMessage) {
+ int proxiedUserId = UserHandle.getUserId(proxiedUid);
+ int proxyUserId = UserHandle.getUserId(proxyUid);
+
verifyIncomingUid(proxyUid);
verifyIncomingOp(code);
- verifyIncomingPackage(proxiedPackageName, UserHandle.getUserId(proxiedUid));
- verifyIncomingPackage(proxyPackageName, UserHandle.getUserId(proxyUid));
+ verifyIncomingUser(proxiedUserId);
+ verifyIncomingUser(proxyUserId);
+ verifyIncomingPackage(proxiedPackageName, proxiedUserId);
+ verifyIncomingPackage(proxyPackageName, proxyUserId);
String resolveProxyPackageName = resolvePackageName(proxyUid, proxyPackageName);
if (resolveProxyPackageName == null) {
@@ -3030,9 +3048,12 @@
private int noteOperationImpl(int code, int uid, @Nullable String packageName,
@Nullable String attributionTag, boolean shouldCollectAsyncNotedOp,
@Nullable String message, boolean shouldCollectMessage) {
+ int userId = UserHandle.getUserId(uid);
+
verifyIncomingUid(uid);
verifyIncomingOp(code);
- verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+ verifyIncomingUser(userId);
+ verifyIncomingPackage(packageName, userId);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -3409,9 +3430,12 @@
public int startOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag, boolean startIfModeDefault, boolean shouldCollectAsyncNotedOp,
String message, boolean shouldCollectMessage) {
+ int userId = UserHandle.getUserId(uid);
+
verifyIncomingUid(uid);
verifyIncomingOp(code);
- verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+ verifyIncomingUser(userId);
+ verifyIncomingPackage(packageName, userId);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -3491,9 +3515,12 @@
@Override
public void finishOperation(IBinder clientId, int code, int uid, String packageName,
String attributionTag) {
+ int userId = UserHandle.getUserId(uid);
+
verifyIncomingUid(uid);
verifyIncomingOp(code);
- verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+ verifyIncomingUser(userId);
+ verifyIncomingPackage(packageName, userId);
String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
@@ -3722,6 +3749,33 @@
}
}
+ private void verifyIncomingUser(@UserIdInt int userId) {
+ int callingUid = Binder.getCallingUid();
+ int callingUserId = UserHandle.getUserId(callingUid);
+ int callingPid = Binder.getCallingPid();
+
+ if (callingUserId != userId) {
+ // Prevent endless loop between when checking appops inside of handleIncomingUser
+ if (Binder.getCallingPid() == ActivityManagerService.MY_PID) {
+ return;
+ }
+ long token = Binder.clearCallingIdentity();
+ try {
+ try {
+ LocalServices.getService(ActivityManagerInternal.class).handleIncomingUser(
+ callingPid, callingUid, userId, /* allowAll */ false,
+ ALLOW_ACROSS_PROFILES_IN_PROFILE_OR_NON_FULL, "appop operation", null);
+ } catch (Exception e) {
+ EventLog.writeEvent(0x534e4554, "153996875", "appop", userId);
+
+ throw e;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+ }
+
private @Nullable UidState getUidStateLocked(int uid, boolean edit) {
UidState uidState = mUidStates.get(uid);
if (uidState == null) {
@@ -5801,8 +5855,11 @@
return false;
}
}
+ int userId = UserHandle.getUserId(uid);
+
verifyIncomingOp(code);
- verifyIncomingPackage(packageName, UserHandle.getUserId(uid));
+ verifyIncomingUser(userId);
+ verifyIncomingPackage(packageName, userId);
final String resolvedPackageName = resolvePackageName(uid, packageName);
if (resolvedPackageName == null) {
diff --git a/services/core/java/com/android/server/appop/TEST_MAPPING b/services/core/java/com/android/server/appop/TEST_MAPPING
index 84de25c..a3e1b7a 100644
--- a/services/core/java/com/android/server/appop/TEST_MAPPING
+++ b/services/core/java/com/android/server/appop/TEST_MAPPING
@@ -7,6 +7,9 @@
"name": "CtsAppOps2TestCases"
},
{
+ "name": "CtsAppOpHostTestCases"
+ },
+ {
"name": "FrameworksServicesTests",
"options": [
{
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index 7051e4a..e128d99 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -108,7 +108,8 @@
private final PowerManager mPowerManager;
private final Object mLock;
@GuardedBy("mLock")
- private IAttentionService mService;
+ @VisibleForTesting
+ protected IAttentionService mService;
@GuardedBy("mLock")
private AttentionCheckCacheBuffer mAttentionCheckCacheBuffer;
@GuardedBy("mLock")
@@ -158,7 +159,8 @@
}
/** Resolves and sets up the attention service if it had not been done yet. */
- private boolean isServiceAvailable() {
+ @VisibleForTesting
+ protected boolean isServiceAvailable() {
if (mComponentName == null) {
mComponentName = resolveAttentionService(mContext);
}
@@ -168,12 +170,12 @@
/**
* Returns {@code true} if attention service is supported on this device.
*/
- private boolean isAttentionServiceSupported() {
+ @VisibleForTesting
+ protected boolean isAttentionServiceSupported() {
return isServiceEnabled() && isServiceConfigured(mContext);
}
- @VisibleForTesting
- protected boolean isServiceEnabled() {
+ private boolean isServiceEnabled() {
return DeviceConfig.getBoolean(NAMESPACE_ATTENTION_MANAGER_SERVICE, KEY_SERVICE_ENABLED,
DEFAULT_SERVICE_ENABLED);
}
diff --git a/services/core/java/com/android/server/display/ColorFade.java b/services/core/java/com/android/server/display/ColorFade.java
index ec2b0c0..ad3cd67 100644
--- a/services/core/java/com/android/server/display/ColorFade.java
+++ b/services/core/java/com/android/server/display/ColorFade.java
@@ -491,7 +491,14 @@
mIsWideColor = SurfaceControl.getActiveColorMode(token)
== Display.COLOR_MODE_DISPLAY_P3;
- SurfaceControl.screenshot(token, s);
+ SurfaceControl.ScreenshotHardwareBuffer screenshotBuffer =
+ mDisplayManagerInternal.systemScreenshot(mDisplayId);
+ s.attachAndQueueBufferWithColorSpace(screenshotBuffer.getHardwareBuffer(),
+ screenshotBuffer.getColorSpace());
+
+ if (screenshotBuffer.containsSecureLayers()) {
+ mTransaction.setSecure(mSurfaceControl, true).apply();
+ }
st.updateTexImage();
st.getTransformMatrix(mTexMatrix);
} finally {
@@ -586,7 +593,6 @@
}
if (mSurfaceControl == null) {
- Transaction t = new Transaction();
try {
final SurfaceControl.Builder builder = new SurfaceControl.Builder(mSurfaceSession)
.setName("ColorFade")
@@ -602,15 +608,16 @@
return false;
}
- t.setLayerStack(mSurfaceControl, mDisplayLayerStack);
- t.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ mTransaction.setLayerStack(mSurfaceControl, mDisplayLayerStack);
+ mTransaction.setWindowCrop(mSurfaceControl, mDisplayWidth, mDisplayHeight);
+ mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
+ mDisplayId, mSurfaceControl);
+ mSurfaceLayout.onDisplayTransaction(mTransaction);
+ mTransaction.apply();
+
mSurface = new Surface();
mSurface.copyFrom(mSurfaceControl);
- mSurfaceLayout = new NaturalSurfaceLayout(mDisplayManagerInternal,
- mDisplayId, mSurfaceControl);
- mSurfaceLayout.onDisplayTransaction(t);
- t.apply();
}
return true;
}
@@ -652,7 +659,7 @@
if (mSurfaceControl != null) {
mSurfaceLayout.dispose();
mSurfaceLayout = null;
- new Transaction().remove(mSurfaceControl).apply();
+ mTransaction.remove(mSurfaceControl).apply();
mSurface.release();
mSurfaceControl = null;
mSurfaceVisible = false;
diff --git a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
index e29471b..eed1aac 100644
--- a/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
+++ b/services/core/java/com/android/server/location/HardwareActivityRecognitionProxy.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
import android.hardware.location.ActivityRecognitionHardware;
import android.hardware.location.IActivityRecognitionHardwareClient;
@@ -77,7 +78,7 @@
return mServiceWatcher.register();
}
- private void onBind(IBinder binder) throws RemoteException {
+ private void onBind(IBinder binder, ComponentName service) throws RemoteException {
String descriptor = binder.getInterfaceDescriptor();
if (IActivityRecognitionHardwareWatcher.class.getCanonicalName().equals(descriptor)) {
diff --git a/services/core/java/com/android/server/location/LocationProviderProxy.java b/services/core/java/com/android/server/location/LocationProviderProxy.java
index 2bcee2f..d778d24 100644
--- a/services/core/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/core/java/com/android/server/location/LocationProviderProxy.java
@@ -19,6 +19,7 @@
import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
import android.annotation.Nullable;
+import android.content.ComponentName;
import android.content.Context;
import android.location.Location;
import android.location.util.identity.CallerIdentity;
@@ -32,10 +33,12 @@
import com.android.internal.location.ILocationProviderManager;
import com.android.internal.location.ProviderProperties;
import com.android.internal.location.ProviderRequest;
+import com.android.internal.util.ArrayUtils;
import com.android.server.ServiceWatcher;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.util.Objects;
/**
* Proxy for ILocationProvider implementations.
@@ -65,6 +68,8 @@
@GuardedBy("mLock")
Proxy mProxy;
+ @GuardedBy("mLock")
+ @Nullable ComponentName mService;
private volatile ProviderRequest mRequest;
@@ -86,11 +91,12 @@
return mServiceWatcher.register();
}
- private void onBind(IBinder binder) throws RemoteException {
+ private void onBind(IBinder binder, ComponentName service) throws RemoteException {
ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
synchronized (mLock) {
mProxy = new Proxy();
+ mService = service;
provider.setLocationProviderManager(mProxy);
ProviderRequest request = mRequest;
@@ -103,6 +109,7 @@
private void onUnbind() {
synchronized (mLock) {
mProxy = null;
+ mService = null;
setState(State.EMPTY_STATE);
}
}
@@ -111,16 +118,16 @@
public void onSetRequest(ProviderRequest request) {
mRequest = request;
mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- service.setRequest(request, request.workSource);
+ ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
+ provider.setRequest(request, request.workSource);
});
}
@Override
public void onExtraCommand(int uid, int pid, String command, Bundle extras) {
mServiceWatcher.runOnBinder(binder -> {
- ILocationProvider service = ILocationProvider.Stub.asInterface(binder);
- service.sendExtraCommand(command, extras);
+ ILocationProvider provider = ILocationProvider.Stub.asInterface(binder);
+ provider.sendExtraCommand(command, extras);
});
}
@@ -129,12 +136,14 @@
mServiceWatcher.dump(fd, pw, args);
}
- private static String guessPackageName(Context context, int uid) {
+ private static String guessPackageName(Context context, int uid, String packageName) {
String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
if (packageNames == null || packageNames.length == 0) {
// illegal state exception will propagate back through binders
throw new IllegalStateException(
"location provider from uid " + uid + " has no package information");
+ } else if (ArrayUtils.contains(packageNames, packageName)) {
+ return packageName;
} else {
return packageNames[0];
}
@@ -154,7 +163,8 @@
CallerIdentity identity;
if (packageName == null) {
- packageName = guessPackageName(mContext, Binder.getCallingUid());
+ packageName = guessPackageName(mContext, Binder.getCallingUid(),
+ Objects.requireNonNull(mService).getPackageName());
// unsafe is ok since the package is coming direct from the package manager here
identity = CallerIdentity.fromBinderUnsafe(packageName, attributionTag);
} else {
@@ -175,7 +185,8 @@
// if no identity is set yet, set it now
if (getIdentity() == null) {
- String packageName = guessPackageName(mContext, Binder.getCallingUid());
+ String packageName = guessPackageName(mContext, Binder.getCallingUid(),
+ Objects.requireNonNull(mService).getPackageName());
// unsafe is ok since the package is coming direct from the package manager here
setIdentity(CallerIdentity.fromBinderUnsafe(packageName, null));
}
diff --git a/services/core/java/com/android/server/location/geofence/GeofenceProxy.java b/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
index 2218079..686a66b 100644
--- a/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
+++ b/services/core/java/com/android/server/location/geofence/GeofenceProxy.java
@@ -63,7 +63,7 @@
private GeofenceProxy(Context context, IGpsGeofenceHardware gpsGeofence) {
mGpsGeofenceHardware = Objects.requireNonNull(gpsGeofence);
mServiceWatcher = new ServiceWatcher(context, SERVICE_ACTION,
- this::updateGeofenceHardware, null,
+ (binder, service) -> updateGeofenceHardware(binder), null,
com.android.internal.R.bool.config_enableGeofenceOverlay,
com.android.internal.R.string.config_geofenceProviderPackageName);
diff --git a/services/core/java/com/android/server/os/TEST_MAPPING b/services/core/java/com/android/server/os/TEST_MAPPING
index a837fb4..d937af1 100644
--- a/services/core/java/com/android/server/os/TEST_MAPPING
+++ b/services/core/java/com/android/server/os/TEST_MAPPING
@@ -1,6 +1,14 @@
{
"presubmit": [
- // TODO(159590499) add BugreportManagerTestCases
+ {
+ "file_patterns": ["Bugreport[^/]*\\.java"],
+ "name": "BugreportManagerTestCases",
+ "options": [
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ },
{
"file_patterns": ["Bugreport[^/]*\\.java"],
"name": "CtsBugreportTestCases",
diff --git a/services/core/java/com/android/server/pm/BackgroundDexOptService.java b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
index 5415967..d48570f 100644
--- a/services/core/java/com/android/server/pm/BackgroundDexOptService.java
+++ b/services/core/java/com/android/server/pm/BackgroundDexOptService.java
@@ -337,6 +337,7 @@
private int idleOptimizePackages(PackageManagerService pm, ArraySet<String> pkgs,
long lowStorageThreshold) {
ArraySet<String> updatedPackages = new ArraySet<>();
+ ArraySet<String> updatedPackagesDueToSecondaryDex = new ArraySet<>();
try {
final boolean supportSecondaryDex = supportSecondaryDex();
@@ -391,11 +392,14 @@
}
int secondaryResult = optimizePackages(pm, pkgs, lowStorageThreshold,
- /*isForPrimaryDex*/ false, updatedPackages);
+ /*isForPrimaryDex*/ false, updatedPackagesDueToSecondaryDex);
return secondaryResult;
} finally {
// Always let the pinner service know about changes.
notifyPinService(updatedPackages);
+ // Only notify IORap the primary dex opt, because we don't want to
+ // invalidate traces unnecessary due to b/161633001 and that it's
+ // better to have a trace than no trace at all.
notifyPackagesUpdated(updatedPackages);
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index 92da005..41735da 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -1746,7 +1746,7 @@
List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
try {
- installNonStaged(childSessions);
+ verifyNonStaged(childSessions);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
@@ -1755,10 +1755,51 @@
}
}
+ private void verifyNonStaged(List<PackageInstallerSession> childSessions)
+ throws PackageManagerException {
+ final PackageManagerService.ActiveInstallSession verifyingSession =
+ makeSessionActiveForVerification();
+ if (verifyingSession == null) {
+ return;
+ }
+ if (isMultiPackage()) {
+ List<PackageManagerService.ActiveInstallSession> verifyingChildSessions =
+ new ArrayList<>(childSessions.size());
+ boolean success = true;
+ PackageManagerException failure = null;
+ for (int i = 0; i < childSessions.size(); ++i) {
+ final PackageInstallerSession session = childSessions.get(i);
+ try {
+ final PackageManagerService.ActiveInstallSession verifyingChildSession =
+ session.makeSessionActiveForVerification();
+ if (verifyingChildSession != null) {
+ verifyingChildSessions.add(verifyingChildSession);
+ }
+ } catch (PackageManagerException e) {
+ failure = e;
+ success = false;
+ }
+ }
+ if (!success) {
+ final IntentSender statusReceiver;
+ synchronized (mLock) {
+ statusReceiver = mRemoteStatusReceiver;
+ }
+ sendOnPackageInstalled(mContext, statusReceiver, sessionId,
+ isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
+ failure.error, failure.getLocalizedMessage(), null);
+ return;
+ }
+ mPm.verifyStage(verifyingSession, verifyingChildSessions);
+ } else {
+ mPm.verifyStage(verifyingSession);
+ }
+ }
+
private void installNonStaged(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
final PackageManagerService.ActiveInstallSession installingSession =
- makeSessionActive();
+ makeSessionActiveForInstall();
if (installingSession == null) {
return;
}
@@ -1771,7 +1812,7 @@
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.ActiveInstallSession installingChildSession =
- session.makeSessionActive();
+ session.makeSessionActiveForInstall();
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
}
@@ -1797,11 +1838,11 @@
}
/**
- * Stages this session for install and returns a
+ * Stages this session for verification and returns a
* {@link PackageManagerService.ActiveInstallSession} representing this new staged state or null
- * in case permissions need to be requested before install can proceed.
+ * in case permissions need to be requested before verification can proceed.
*/
- private PackageManagerService.ActiveInstallSession makeSessionActive()
+ private PackageManagerService.ActiveInstallSession makeSessionActiveForVerification()
throws PackageManagerException {
assertNotLocked("makeSessionActive");
@@ -1820,6 +1861,7 @@
}
}
+ // TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
if (!params.isMultiPackage && needToAskForPermissions()) {
// User needs to confirm installation;
// give installer an intent they can use to involve
@@ -1841,12 +1883,12 @@
}
synchronized (mLock) {
- return makeSessionActiveLocked();
+ return makeSessionActiveForVerificationLocked();
}
}
@GuardedBy("mLock")
- private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
+ private PackageManagerService.ActiveInstallSession makeSessionActiveForVerificationLocked()
throws PackageManagerException {
if (!params.isMultiPackage) {
Objects.requireNonNull(mPackageName);
@@ -1910,6 +1952,80 @@
extractNativeLibraries(stageDir, params.abiOverride, mayInheritNativeLibs());
}
+ final IPackageInstallObserver2 localObserver;
+ if (!hasParentSessionId()) {
+ // Avoid attaching this observer to child session since they won't use it.
+ localObserver = new IPackageInstallObserver2.Stub() {
+ @Override
+ public void onUserActionRequired(Intent intent) {
+ throw new IllegalStateException();
+ }
+
+ @Override
+ public void onPackageInstalled(String basePackageName, int returnCode, String msg,
+ Bundle extras) {
+ if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ onVerificationComplete();
+ } else {
+ destroyInternal();
+ dispatchSessionFinished(returnCode, msg, extras);
+ }
+ }
+ };
+ } else {
+ localObserver = null;
+ }
+
+ final UserHandle user;
+ if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
+ user = UserHandle.ALL;
+ } else {
+ user = new UserHandle(userId);
+ }
+
+ mRelinquished = true;
+ // TODO(159331446): create VerificationParams directly by passing information that is
+ // required for verification only
+ return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
+ localObserver, sessionId, params, mInstallerUid, mInstallSource, user,
+ mSigningDetails);
+ }
+
+ private void onVerificationComplete() {
+ if ((params.installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
+ destroyInternal();
+ dispatchSessionFinished(PackageManager.INSTALL_SUCCEEDED, "Dry run", new Bundle());
+ return;
+ }
+
+ List<PackageInstallerSession> childSessions = getChildSessionsNotLocked();
+ try {
+ installNonStaged(childSessions);
+ } catch (PackageManagerException e) {
+ final String completeMsg = ExceptionUtils.getCompleteMessage(e);
+ Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
+ destroyInternal();
+ dispatchSessionFinished(e.error, completeMsg, null);
+ }
+ }
+
+ /**
+ * Stages this session for install and returns a
+ * {@link PackageManagerService.ActiveInstallSession} representing this new staged state.
+ */
+ private PackageManagerService.ActiveInstallSession makeSessionActiveForInstall()
+ throws PackageManagerException {
+ synchronized (mLock) {
+ if (mDestroyed) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
+ }
+ if (!mSealed) {
+ throw new PackageManagerException(
+ INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
+ }
+ }
+
// We've reached point of no return; call into PMS to install the stage.
// Regardless of success or failure we always destroy session.
final IPackageInstallObserver2 localObserver = new IPackageInstallObserver2.Stub() {
@@ -1926,34 +2042,6 @@
}
};
- // An observer through which PMS returns the result of verification
- // TODO(samiul): We are temporarily assigning two observer to ActiveInstallSession. One for
- // installation and one for verification. This will be fixed within next few CLs.
- final IPackageInstallObserver2 sessionVerificationObserver;
- if (!hasParentSessionId()) {
- // Avoid attaching this observer to child session since they won't use it.
- sessionVerificationObserver = new IPackageInstallObserver2.Stub() {
- @Override
- public void onUserActionRequired(Intent intent) {
- throw new IllegalStateException();
- }
-
- @Override
- public void onPackageInstalled(String basePackageName, int returnCode, String msg,
- Bundle extras) {
- if (returnCode == PackageManager.INSTALL_SUCCEEDED) {
- // TODO(samiul): In future, packages will not be installed immediately after
- // verification. Package verification will return control back to here,
- // and we will have call into PMS again to install package.
- //
- // For now, this is a no op.
- }
- }
- };
- } else {
- sessionVerificationObserver = null;
- }
-
final UserHandle user;
if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
@@ -1961,10 +2049,11 @@
user = new UserHandle(userId);
}
- mRelinquished = true;
- return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir, localObserver,
- sessionVerificationObserver, sessionId, params, mInstallerUid, mInstallSource, user,
- mSigningDetails);
+ synchronized (mLock) {
+ return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
+ localObserver, sessionId, params, mInstallerUid, mInstallSource, user,
+ mSigningDetails);
+ }
}
private static void maybeRenameFile(File from, File to) throws PackageManagerException {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 73db48a..6782fc9 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1159,7 +1159,7 @@
final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();
/** List of packages waiting for rollback to be enabled. */
- final SparseArray<InstallParams> mPendingEnableRollback = new SparseArray<>();
+ final SparseArray<VerificationParams> mPendingEnableRollback = new SparseArray<>();
final PackageInstallerService mInstallerService;
@@ -1833,7 +1833,7 @@
if ((state != null) && !state.isVerificationComplete()
&& !state.timeoutExtended()) {
- final InstallParams params = state.getInstallParams();
+ final VerificationParams params = state.getVerificationParams();
final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
Slog.i(TAG, "Verification timed out for " + originUri);
@@ -1874,7 +1874,7 @@
final PackageVerificationState state = mPendingVerification.get(verificationId);
if (state != null && !state.isIntegrityVerificationComplete()) {
- final InstallParams params = state.getInstallParams();
+ final VerificationParams params = state.getVerificationParams();
final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
Slog.i(TAG, "Integrity verification timed out for " + originUri);
@@ -1919,7 +1919,7 @@
state.setVerifierResponse(response.callerUid, response.code);
if (state.isVerificationComplete()) {
- final InstallParams params = state.getInstallParams();
+ final VerificationParams params = state.getVerificationParams();
final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
if (state.isInstallAllowed()) {
@@ -1953,7 +1953,7 @@
}
final int response = (Integer) msg.obj;
- final InstallParams params = state.getInstallParams();
+ final VerificationParams params = state.getVerificationParams();
final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
state.setIntegrityVerificationResult(response);
@@ -2037,7 +2037,8 @@
case ENABLE_ROLLBACK_STATUS: {
final int enableRollbackToken = msg.arg1;
final int enableRollbackCode = msg.arg2;
- InstallParams params = mPendingEnableRollback.get(enableRollbackToken);
+ final VerificationParams params =
+ mPendingEnableRollback.get(enableRollbackToken);
if (params == null) {
Slog.w(TAG, "Invalid rollback enabled token "
+ enableRollbackToken + " received");
@@ -2061,7 +2062,8 @@
case ENABLE_ROLLBACK_TIMEOUT: {
final int enableRollbackToken = msg.arg1;
final int sessionId = msg.arg2;
- final InstallParams params = mPendingEnableRollback.get(enableRollbackToken);
+ final VerificationParams params =
+ mPendingEnableRollback.get(enableRollbackToken);
if (params != null) {
final Uri originUri = Uri.fromFile(params.origin.resolvedFile);
@@ -12886,6 +12888,22 @@
mHandler.sendMessage(msg);
}
+ void verifyStage(ActiveInstallSession activeInstallSession) {
+ final VerificationParams params = new VerificationParams(activeInstallSession);
+ mHandler.post(()-> {
+ params.startCopy();
+ });
+ }
+
+ void verifyStage(ActiveInstallSession parent, List<ActiveInstallSession> children)
+ throws PackageManagerException {
+ final MultiPackageVerificationParams params =
+ new MultiPackageVerificationParams(UserHandle.ALL, parent, children);
+ mHandler.post(()-> {
+ params.startCopy();
+ });
+ }
+
private void sendPackageAddedForUser(String packageName, PackageSetting pkgSetting,
int userId, int dataLoaderType) {
final boolean isSystem = isSystemApp(pkgSetting) || isUpdatedSystemApp(pkgSetting);
@@ -14746,13 +14764,8 @@
* committed together.
*/
class MultiPackageInstallParams extends HandlerParams {
- private final IPackageInstallObserver2 mVerificationObserver;
- @NonNull
private final ArrayList<InstallParams> mChildParams;
- // TODO(samiul): mCurrentState will relocated to a install-specific class in future
- @NonNull
private final Map<InstallArgs, Integer> mCurrentState;
- private final Map<InstallParams, Integer> mVerificationState;
MultiPackageInstallParams(
@NonNull UserHandle user,
@@ -14770,8 +14783,6 @@
this.mChildParams.add(childParams);
}
this.mCurrentState = new ArrayMap<>(mChildParams.size());
- this.mVerificationState = new ArrayMap<>(mChildParams.size());
- mVerificationObserver = parent.getVerificationObserver();
}
@Override
@@ -14788,7 +14799,6 @@
}
}
- // TODO(samiul): this method will relocated to a install-specific class in future
void tryProcessInstallRequest(InstallArgs args, int currentStatus) {
mCurrentState.put(args, currentStatus);
if (mCurrentState.size() != mChildParams.size()) {
@@ -14812,84 +14822,48 @@
completeStatus == PackageManager.INSTALL_SUCCEEDED,
installRequests);
}
-
- void trySendVerificationCompleteNotification(InstallParams child, int currentStatus) {
- mVerificationState.put(child, currentStatus);
- if (mVerificationState.size() != mChildParams.size()) {
- return;
- }
- int completeStatus = PackageManager.INSTALL_SUCCEEDED;
- for (Integer status : mVerificationState.values()) {
- if (status == PackageManager.INSTALL_UNKNOWN) {
- return;
- } else if (status != PackageManager.INSTALL_SUCCEEDED) {
- completeStatus = status;
- break;
- }
- }
- try {
- mVerificationObserver.onPackageInstalled(null, completeStatus,
- "Package Verification Result", new Bundle());
- } catch (RemoteException e) {
- Slog.i(TAG, "Observer no longer exists.");
- }
- }
}
class InstallParams extends HandlerParams {
// TODO: see if we can collapse this into ActiveInstallSession
-
final OriginInfo origin;
final MoveInfo move;
- final IPackageInstallObserver2 mInstallObserver;
- private final IPackageInstallObserver2 mVerificationObserver;
+ final IPackageInstallObserver2 observer;
int installFlags;
@NonNull final InstallSource installSource;
final String volumeUuid;
- private boolean mWaitForVerificationToComplete;
- private boolean mWaitForIntegrityVerificationToComplete;
- private boolean mWaitForEnableRollbackToComplete;
int mRet;
final String packageAbiOverride;
final String[] grantedRuntimePermissions;
final List<String> whitelistedRestrictedPermissions;
final int autoRevokePermissionsMode;
- final VerificationInfo verificationInfo;
final PackageParser.SigningDetails signingDetails;
final int installReason;
- @Nullable
- MultiPackageInstallParams mParentInstallParams;
- final long requiredInstalledVersionCode;
+ @Nullable MultiPackageInstallParams mParentInstallParams;
final boolean forceQueryableOverride;
final int mDataLoaderType;
- final int mSessionId;
- InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 installObserver,
+ InstallParams(OriginInfo origin, MoveInfo move, IPackageInstallObserver2 observer,
int installFlags, InstallSource installSource, String volumeUuid,
- VerificationInfo verificationInfo, UserHandle user, String packageAbiOverride,
+ UserHandle user, String packageAbiOverride,
String[] grantedPermissions, List<String> whitelistedRestrictedPermissions,
int autoRevokePermissionsMode,
- SigningDetails signingDetails, int installReason,
- long requiredInstalledVersionCode, int dataLoaderType) {
+ SigningDetails signingDetails, int installReason, int dataLoaderType) {
super(user);
this.origin = origin;
this.move = move;
- this.mInstallObserver = installObserver;
- this.mVerificationObserver = null;
+ this.observer = observer;
this.installFlags = installFlags;
this.installSource = Preconditions.checkNotNull(installSource);
this.volumeUuid = volumeUuid;
- this.verificationInfo = verificationInfo;
this.packageAbiOverride = packageAbiOverride;
this.grantedRuntimePermissions = grantedPermissions;
this.whitelistedRestrictedPermissions = whitelistedRestrictedPermissions;
this.autoRevokePermissionsMode = autoRevokePermissionsMode;
this.signingDetails = signingDetails;
this.installReason = installReason;
- this.requiredInstalledVersionCode = requiredInstalledVersionCode;
this.forceQueryableOverride = false;
this.mDataLoaderType = dataLoaderType;
- this.mSessionId = -1;
}
InstallParams(ActiveInstallSession activeInstallSession) {
@@ -14902,19 +14876,13 @@
Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
}
}
- verificationInfo = new VerificationInfo(
- sessionParams.originatingUri,
- sessionParams.referrerUri,
- sessionParams.originatingUid,
- activeInstallSession.getInstallerUid());
origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir());
move = null;
installReason = fixUpInstallReason(
activeInstallSession.getInstallSource().installerPackageName,
activeInstallSession.getInstallerUid(),
sessionParams.installReason);
- mInstallObserver = activeInstallSession.getInstallObserver();
- mVerificationObserver = activeInstallSession.getVerificationObserver();
+ observer = activeInstallSession.getObserver();
installFlags = sessionParams.installFlags;
installSource = activeInstallSession.getInstallSource();
volumeUuid = sessionParams.volumeUuid;
@@ -14923,11 +14891,9 @@
whitelistedRestrictedPermissions = sessionParams.whitelistedRestrictedPermissions;
autoRevokePermissionsMode = sessionParams.autoRevokePermissionsMode;
signingDetails = activeInstallSession.getSigningDetails();
- requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
forceQueryableOverride = sessionParams.forceQueryableOverride;
mDataLoaderType = (sessionParams.dataLoaderParams != null)
? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
- mSessionId = activeInstallSession.getSessionId();
}
@Override
@@ -15074,13 +15040,154 @@
public void handleStartCopy() {
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
origin.resolvedPath, installFlags, packageAbiOverride);
+ mRet = overrideInstallLocation(pkgLite);
+ }
+
+ @Override
+ void handleReturnCode() {
+ processPendingInstall();
+ }
+
+ private void processPendingInstall() {
+ InstallArgs args = createInstallArgs(this);
+ if (mRet == PackageManager.INSTALL_SUCCEEDED) {
+ mRet = args.copyApk();
+ }
+ if (mParentInstallParams != null) {
+ mParentInstallParams.tryProcessInstallRequest(args, mRet);
+ } else {
+ PackageInstalledInfo res = createPackageInstalledInfo(mRet);
+ processInstallRequestsAsync(
+ res.returnCode == PackageManager.INSTALL_SUCCEEDED,
+ Collections.singletonList(new InstallRequest(args, res)));
+
+ }
+ }
+ }
+
+ /**
+ * Container for a multi-package install which refers to all install sessions and args being
+ * committed together.
+ */
+ class MultiPackageVerificationParams extends HandlerParams {
+ private final IPackageInstallObserver2 mObserver;
+ private final ArrayList<VerificationParams> mChildParams;
+ private final Map<VerificationParams, Integer> mVerificationState;
+
+ MultiPackageVerificationParams(
+ @NonNull UserHandle user,
+ @NonNull ActiveInstallSession parent,
+ @NonNull List<ActiveInstallSession> activeInstallSessions)
+ throws PackageManagerException {
+ super(user);
+ if (activeInstallSessions.size() == 0) {
+ throw new PackageManagerException("No child sessions found!");
+ }
+ mChildParams = new ArrayList<>(activeInstallSessions.size());
+ for (int i = 0; i < activeInstallSessions.size(); i++) {
+ final VerificationParams childParams =
+ new VerificationParams(activeInstallSessions.get(i));
+ childParams.mParentVerificationParams = this;
+ this.mChildParams.add(childParams);
+ }
+ this.mVerificationState = new ArrayMap<>(mChildParams.size());
+ mObserver = parent.getObserver();
+ }
+
+ @Override
+ void handleStartCopy() {
+ for (VerificationParams params : mChildParams) {
+ params.handleStartCopy();
+ }
+ }
+
+ @Override
+ void handleReturnCode() {
+ for (VerificationParams params : mChildParams) {
+ params.handleReturnCode();
+ }
+ }
+
+ void trySendVerificationCompleteNotification(VerificationParams child, int currentStatus) {
+ mVerificationState.put(child, currentStatus);
+ if (mVerificationState.size() != mChildParams.size()) {
+ return;
+ }
+ int completeStatus = PackageManager.INSTALL_SUCCEEDED;
+ for (Integer status : mVerificationState.values()) {
+ if (status == PackageManager.INSTALL_UNKNOWN) {
+ return;
+ } else if (status != PackageManager.INSTALL_SUCCEEDED) {
+ completeStatus = status;
+ break;
+ }
+ }
+ try {
+ mObserver.onPackageInstalled(null, completeStatus,
+ "Package Verification Result", new Bundle());
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Observer no longer exists.");
+ }
+ }
+ }
+
+ class VerificationParams extends HandlerParams {
+ final OriginInfo origin;
+ final IPackageInstallObserver2 observer;
+ final int installFlags;
+ @NonNull final InstallSource installSource;
+ final String packageAbiOverride;
+ final VerificationInfo verificationInfo;
+ final PackageParser.SigningDetails signingDetails;
+ @Nullable MultiPackageVerificationParams mParentVerificationParams;
+ final long requiredInstalledVersionCode;
+ final int mDataLoaderType;
+ final int mSessionId;
+
+ private boolean mWaitForVerificationToComplete;
+ private boolean mWaitForIntegrityVerificationToComplete;
+ private boolean mWaitForEnableRollbackToComplete;
+ private int mRet;
+
+ VerificationParams(ActiveInstallSession activeInstallSession) {
+ super(activeInstallSession.getUser());
+ final PackageInstaller.SessionParams sessionParams =
+ activeInstallSession.getSessionParams();
+ if (DEBUG_INSTANT) {
+ if ((sessionParams.installFlags
+ & PackageManager.INSTALL_INSTANT_APP) != 0) {
+ Slog.d(TAG, "Ephemeral install of " + activeInstallSession.getPackageName());
+ }
+ }
+ verificationInfo = new VerificationInfo(
+ sessionParams.originatingUri,
+ sessionParams.referrerUri,
+ sessionParams.originatingUid,
+ activeInstallSession.getInstallerUid());
+ origin = OriginInfo.fromStagedFile(activeInstallSession.getStagedDir());
+ observer = activeInstallSession.getObserver();
+ installFlags = sessionParams.installFlags;
+ installSource = activeInstallSession.getInstallSource();
+ packageAbiOverride = sessionParams.abiOverride;
+ signingDetails = activeInstallSession.getSigningDetails();
+ requiredInstalledVersionCode = sessionParams.requiredInstalledVersionCode;
+ mDataLoaderType = (sessionParams.dataLoaderParams != null)
+ ? sessionParams.dataLoaderParams.getType() : DataLoaderType.NONE;
+ mSessionId = activeInstallSession.getSessionId();
+ }
+
+ @Override
+ public String toString() {
+ return "InstallParams{" + Integer.toHexString(System.identityHashCode(this))
+ + " file=" + origin.file + "}";
+ }
+
+ public void handleStartCopy() {
+ PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
+ origin.resolvedPath, installFlags, packageAbiOverride);
mRet = verifyReplacingVersionCode(pkgLite);
- if (mRet == INSTALL_SUCCEEDED) {
- mRet = overrideInstallLocation(pkgLite);
- }
-
// Perform package verification and enable rollback (unless we are simply moving the
// package).
if (mRet == INSTALL_SUCCEEDED && !origin.existing) {
@@ -15242,7 +15349,7 @@
final long idleDuration = getVerificationTimeout();
idleController.addPowerSaveTempWhitelistAppDirect(Process.myUid(),
- idleDuration,
+ idleDuration,
false, "integrity component");
final BroadcastOptions options = BroadcastOptions.makeBasic();
options.setTemporaryAppWhitelistDuration(idleDuration);
@@ -15476,44 +15583,16 @@
|| mWaitForEnableRollbackToComplete) {
return;
}
-
- if ((installFlags & PackageManager.INSTALL_DRY_RUN) != 0) {
- try {
- mInstallObserver.onPackageInstalled(null, mRet, "Dry run", new Bundle());
- } catch (RemoteException e) {
- Slog.i(TAG, "Observer no longer exists.");
- }
- return;
- }
sendVerificationCompleteNotification();
-
- // TODO(samiul): In future return once verification is complete
- processPendingInstall();
- }
-
- private void processPendingInstall() {
- InstallArgs args = createInstallArgs(this);
- if (mRet == PackageManager.INSTALL_SUCCEEDED) {
- mRet = args.copyApk();
- }
- if (mParentInstallParams != null) {
- mParentInstallParams.tryProcessInstallRequest(args, mRet);
- } else {
- PackageInstalledInfo res = createPackageInstalledInfo(mRet);
- processInstallRequestsAsync(
- res.returnCode == PackageManager.INSTALL_SUCCEEDED,
- Collections.singletonList(new InstallRequest(args, res)));
-
- }
}
private void sendVerificationCompleteNotification() {
- if (mParentInstallParams != null) {
- mParentInstallParams.trySendVerificationCompleteNotification(this, mRet);
+ if (mParentVerificationParams != null) {
+ mParentVerificationParams.trySendVerificationCompleteNotification(this, mRet);
} else {
try {
- mVerificationObserver.onPackageInstalled(null, mRet,
- "Package Verification Result", new Bundle());
+ observer.onPackageInstalled(null, mRet, "Package Verification Result",
+ new Bundle());
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
@@ -15597,7 +15676,7 @@
/** New install */
InstallArgs(InstallParams params) {
- this(params.origin, params.move, params.mInstallObserver, params.installFlags,
+ this(params.origin, params.move, params.observer, params.installFlags,
params.installSource, params.volumeUuid,
params.getUser(), null /*instructionSets*/, params.packageAbiOverride,
params.grantedRuntimePermissions, params.whitelistedRestrictedPermissions,
@@ -23377,12 +23456,11 @@
final Message msg = mHandler.obtainMessage(INIT_COPY);
final OriginInfo origin = OriginInfo.fromExistingFile(codeFile);
final InstallParams params = new InstallParams(origin, move, installObserver, installFlags,
- installSource, volumeUuid, null /*verificationInfo*/, user,
+ installSource, volumeUuid, user,
packageAbiOverride, null /*grantedPermissions*/,
null /*whitelistedRestrictedPermissions*/, MODE_DEFAULT /* autoRevokePermissions */,
PackageParser.SigningDetails.UNKNOWN,
- PackageManager.INSTALL_REASON_UNKNOWN, PackageManager.VERSION_CODE_HIGHEST,
- DataLoaderType.NONE);
+ PackageManager.INSTALL_REASON_UNKNOWN, DataLoaderType.NONE);
params.setTraceMethod("movePackage").setTraceCookie(System.identityHashCode(params));
msg.obj = params;
@@ -25629,13 +25707,12 @@
return mSettings.mPackages.get(packageName).getMimeGroup(mimeGroup);
}
+ // TODO(samiul): Get rid of this class. The callers can create InstallParams and
+ // VerificationParams directly.
static class ActiveInstallSession {
private final String mPackageName;
private final File mStagedDir;
- private final IPackageInstallObserver2 mInstallObserver;
- // TODO(samiul): We are temporarily assigning two observer to ActiveInstallSession. One for
- // installation and one for verification. This will be fixed within next few CLs.
- private final IPackageInstallObserver2 mVerificationObserver;
+ private final IPackageInstallObserver2 mObserver;
private final int mSessionId;
private final PackageInstaller.SessionParams mSessionParams;
private final int mInstallerUid;
@@ -25643,15 +25720,12 @@
private final UserHandle mUser;
private final SigningDetails mSigningDetails;
- ActiveInstallSession(String packageName, File stagedDir,
- IPackageInstallObserver2 installObserver,
- IPackageInstallObserver2 verificationObserver,
+ ActiveInstallSession(String packageName, File stagedDir, IPackageInstallObserver2 observer,
int sessionId, PackageInstaller.SessionParams sessionParams, int installerUid,
InstallSource installSource, UserHandle user, SigningDetails signingDetails) {
mPackageName = packageName;
mStagedDir = stagedDir;
- mInstallObserver = installObserver;
- mVerificationObserver = verificationObserver;
+ mObserver = observer;
mSessionId = sessionId;
mSessionParams = sessionParams;
mInstallerUid = installerUid;
@@ -25668,12 +25742,8 @@
return mStagedDir;
}
- public IPackageInstallObserver2 getInstallObserver() {
- return mInstallObserver;
- }
-
- public IPackageInstallObserver2 getVerificationObserver() {
- return mVerificationObserver;
+ public IPackageInstallObserver2 getObserver() {
+ return mObserver;
}
public int getSessionId() {
diff --git a/services/core/java/com/android/server/pm/PackageVerificationState.java b/services/core/java/com/android/server/pm/PackageVerificationState.java
index ea7af90..cb9c2e9 100644
--- a/services/core/java/com/android/server/pm/PackageVerificationState.java
+++ b/services/core/java/com/android/server/pm/PackageVerificationState.java
@@ -19,7 +19,7 @@
import android.content.pm.PackageManager;
import android.util.SparseBooleanArray;
-import com.android.server.pm.PackageManagerService.InstallParams;
+import com.android.server.pm.PackageManagerService.VerificationParams;
/**
* Tracks the package verification state for a particular package. Each package verification has a
@@ -28,7 +28,7 @@
* sufficient verifiers, then package verification is considered complete.
*/
class PackageVerificationState {
- private final InstallParams mParams;
+ private final VerificationParams mParams;
private final SparseBooleanArray mSufficientVerifierUids;
@@ -50,13 +50,13 @@
* Create a new package verification state where {@code requiredVerifierUid} is the user ID for
* the package that must reply affirmative before things can continue.
*/
- PackageVerificationState(InstallParams params) {
+ PackageVerificationState(VerificationParams params) {
mParams = params;
mSufficientVerifierUids = new SparseBooleanArray();
mExtendedTimeout = false;
}
- InstallParams getInstallParams() {
+ VerificationParams getVerificationParams() {
return mParams;
}
diff --git a/services/core/java/com/android/server/pm/StagingManager.java b/services/core/java/com/android/server/pm/StagingManager.java
index 616e5d1..89bdb3e 100644
--- a/services/core/java/com/android/server/pm/StagingManager.java
+++ b/services/core/java/com/android/server/pm/StagingManager.java
@@ -804,6 +804,7 @@
params.isStaged = false;
if (preReboot) {
params.installFlags &= ~PackageManager.INSTALL_ENABLE_ROLLBACK;
+ params.installFlags |= PackageManager.INSTALL_DRY_RUN;
}
final int apkParentSessionId = mPi.createSession(
params, session.getInstallerPackageName(), session.getInstallerAttributionTag(),
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 5c5942da..6e0efb0 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -137,6 +137,7 @@
import com.android.server.ServiceThread;
import com.android.server.SystemConfig;
import com.android.server.Watchdog;
+import com.android.server.am.ActivityManagerService;
import com.android.server.pm.ApexManager;
import com.android.server.pm.PackageManagerServiceUtils;
import com.android.server.pm.PackageSetting;
@@ -921,6 +922,16 @@
}
final int uid = UserHandle.getUid(userId, pkg.getUid());
+
+ try {
+ enforceCrossUserOrProfilePermission(Binder.getCallingUid(), UserHandle.getUserId(uid),
+ false, false, "checkPermissionInternal");
+ } catch (Exception e) {
+ EventLog.writeEvent(0x534e4554, "153996875", "checkPermission", uid);
+
+ throw e;
+ }
+
final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
pkg.getPackageName());
if (ps == null) {
@@ -1928,10 +1939,8 @@
}
}
if (doGrant && packageName != null) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultBrowser(packageName,
- userId);
- }
+ mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultBrowser(packageName,
+ userId);
}
return true;
}
@@ -1941,10 +1950,8 @@
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils
.enforceSystemOrPhoneCaller("grantPermissionsToEnabledCarrierApps", callingUid);
- synchronized (mLock) {
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId));
- }
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .grantDefaultPermissionsToEnabledCarrierApps(packageNames, userId));
}
@Override
@@ -1952,10 +1959,8 @@
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
"grantDefaultPermissionsToEnabledImsServices", callingUid);
- synchronized (mLock) {
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToEnabledImsServices(packageNames, userId));
- }
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .grantDefaultPermissionsToEnabledImsServices(packageNames, userId));
}
@Override
@@ -1964,11 +1969,8 @@
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
"grantDefaultPermissionsToEnabledTelephonyDataServices", callingUid);
- synchronized (mLock) {
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToEnabledTelephonyDataServices(
- packageNames, userId));
- }
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .grantDefaultPermissionsToEnabledTelephonyDataServices(packageNames, userId));
}
@Override
@@ -1977,11 +1979,8 @@
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils.enforceSystemOrPhoneCaller(
"revokeDefaultPermissionsFromDisabledTelephonyDataServices", callingUid);
- synchronized (mLock) {
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .revokeDefaultPermissionsFromDisabledTelephonyDataServices(
- packageNames, userId));
- }
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .revokeDefaultPermissionsFromDisabledTelephonyDataServices(packageNames, userId));
}
@Override
@@ -1989,10 +1988,8 @@
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils
.enforceSystemOrPhoneCaller("grantDefaultPermissionsToActiveLuiApp", callingUid);
- synchronized (mLock) {
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToActiveLuiApp(packageName, userId));
- }
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .grantDefaultPermissionsToActiveLuiApp(packageName, userId));
}
@Override
@@ -2000,10 +1997,8 @@
final int callingUid = Binder.getCallingUid();
PackageManagerServiceUtils
.enforceSystemOrPhoneCaller("revokeDefaultPermissionsFromLuiApps", callingUid);
- synchronized (mLock) {
- Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
- .revokeDefaultPermissionsFromLuiApps(packageNames, userId));
- }
+ Binder.withCleanCallingIdentity(() -> mDefaultPermissionGrantPolicy
+ .revokeDefaultPermissionsFromLuiApps(packageNames, userId));
}
@Override
@@ -4404,7 +4399,7 @@
}
final int callingUserId = UserHandle.getUserId(callingUid);
if (hasCrossUserPermission(
- callingUid, callingUserId, userId, requireFullPermission,
+ Binder.getCallingPid(), callingUid, callingUserId, userId, requireFullPermission,
requirePermissionWhenSameUser)) {
return;
}
@@ -4431,37 +4426,54 @@
private void enforceCrossUserOrProfilePermission(int callingUid, int userId,
boolean requireFullPermission, boolean checkShell,
String message) {
+ int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getUserId(callingUid);
+
if (userId < 0) {
throw new IllegalArgumentException("Invalid userId " + userId);
}
- if (checkShell) {
- PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
- UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
- }
- final int callingUserId = UserHandle.getUserId(callingUid);
- if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
- /*requirePermissionWhenSameUser= */ false)) {
+
+ if (callingUserId == userId) {
return;
}
- final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
- if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
- mContext,
- android.Manifest.permission.INTERACT_ACROSS_PROFILES,
- PermissionChecker.PID_UNKNOWN,
- callingUid,
- mPackageManagerInt.getPackage(callingUid).getPackageName())
- == PermissionChecker.PERMISSION_GRANTED) {
+
+ // Prevent endless loop between when checking permission while checking a permission
+ if (callingPid == ActivityManagerService.MY_PID) {
return;
}
- String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
- message, requireFullPermission, isSameProfileGroup);
- Slog.w(TAG, errorMessage);
- throw new SecurityException(errorMessage);
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ if (checkShell) {
+ PackageManagerServiceUtils.enforceShellRestriction(mUserManagerInt,
+ UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
+ }
+ if (hasCrossUserPermission(callingPid, callingUid, callingUserId, userId,
+ requireFullPermission, /*requirePermissionWhenSameUser= */ false)) {
+ return;
+ }
+ final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
+ if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
+ mContext,
+ android.Manifest.permission.INTERACT_ACROSS_PROFILES,
+ PermissionChecker.PID_UNKNOWN,
+ callingUid,
+ mPackageManagerInt.getPackage(callingUid).getPackageName())
+ == PermissionChecker.PERMISSION_GRANTED) {
+ return;
+ }
+
+ String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
+ message, requireFullPermission, isSameProfileGroup);
+ Slog.w(TAG, errorMessage);
+ throw new SecurityException(errorMessage);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
- private boolean hasCrossUserPermission(
- int callingUid, int callingUserId, int userId, boolean requireFullPermission,
- boolean requirePermissionWhenSameUser) {
+ private boolean hasCrossUserPermission(int callingPid, int callingUid, int callingUserId,
+ int userId, boolean requireFullPermission, boolean requirePermissionWhenSameUser) {
if (!requirePermissionWhenSameUser && userId == callingUserId) {
return true;
}
@@ -4469,15 +4481,11 @@
return true;
}
if (requireFullPermission) {
- return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL);
+ return mContext.checkPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
}
- return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
- || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS);
- }
-
- private boolean hasPermission(String permission) {
- return mContext.checkCallingOrSelfPermission(permission)
- == PackageManager.PERMISSION_GRANTED;
+ return mContext.checkPermission(android.Manifest.permission.INTERACT_ACROSS_USERS,
+ callingPid, callingUid) == PackageManager.PERMISSION_GRANTED;
}
private boolean isSameProfileGroup(@UserIdInt int callerUserId, @UserIdInt int userId) {
@@ -4889,58 +4897,42 @@
@Override
public void setDialerAppPackagesProvider(PackagesProvider provider) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy.setDialerAppPackagesProvider(provider);
- }
+ mDefaultPermissionGrantPolicy.setDialerAppPackagesProvider(provider);
}
@Override
public void setLocationExtraPackagesProvider(PackagesProvider provider) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy.setLocationExtraPackagesProvider(provider);
- }
+ mDefaultPermissionGrantPolicy.setLocationExtraPackagesProvider(provider);
}
@Override
public void setLocationPackagesProvider(PackagesProvider provider) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy.setLocationPackagesProvider(provider);
- }
+ mDefaultPermissionGrantPolicy.setLocationPackagesProvider(provider);
}
@Override
public void setSimCallManagerPackagesProvider(PackagesProvider provider) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy.setSimCallManagerPackagesProvider(provider);
- }
+ mDefaultPermissionGrantPolicy.setSimCallManagerPackagesProvider(provider);
}
@Override
public void setSmsAppPackagesProvider(PackagesProvider provider) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy.setSmsAppPackagesProvider(provider);
- }
+ mDefaultPermissionGrantPolicy.setSmsAppPackagesProvider(provider);
}
@Override
public void setSyncAdapterPackagesProvider(SyncAdapterPackagesProvider provider) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy.setSyncAdapterPackagesProvider(provider);
- }
+ mDefaultPermissionGrantPolicy.setSyncAdapterPackagesProvider(provider);
}
@Override
public void setUseOpenWifiAppPackagesProvider(PackagesProvider provider) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy.setUseOpenWifiAppPackagesProvider(provider);
- }
+ mDefaultPermissionGrantPolicy.setUseOpenWifiAppPackagesProvider(provider);
}
@Override
public void setVoiceInteractionPackagesProvider(PackagesProvider provider) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy.setVoiceInteractionPackagesProvider(provider);
- }
+ mDefaultPermissionGrantPolicy.setVoiceInteractionPackagesProvider(provider);
}
@Override
@@ -4972,26 +4964,20 @@
@Override
public void grantDefaultPermissionsToDefaultSimCallManager(String packageName, int userId) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToDefaultSimCallManager(packageName, userId);
- }
+ mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultSimCallManager(
+ packageName, userId);
}
@Override
public void grantDefaultPermissionsToDefaultUseOpenWifiApp(String packageName, int userId) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToDefaultUseOpenWifiApp(packageName, userId);
- }
+ mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultUseOpenWifiApp(
+ packageName, userId);
}
@Override
public void grantDefaultPermissionsToDefaultBrowser(String packageName, int userId) {
- synchronized (mLock) {
- mDefaultPermissionGrantPolicy
- .grantDefaultPermissionsToDefaultBrowser(packageName, userId);
- }
+ mDefaultPermissionGrantPolicy.grantDefaultPermissionsToDefaultBrowser(packageName,
+ userId);
}
@Override
diff --git a/services/core/java/com/android/server/pm/permission/TEST_MAPPING b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
index c0d71ac..65dc320 100644
--- a/services/core/java/com/android/server/pm/permission/TEST_MAPPING
+++ b/services/core/java/com/android/server/pm/permission/TEST_MAPPING
@@ -18,14 +18,6 @@
]
},
{
- "name": "CtsAppSecurityHostTestCases",
- "options": [
- {
- "include-filter": "android.appsecurity.cts.AppSecurityTests#rebootWithDuplicatePermission"
- }
- ]
- },
- {
"name": "CtsPermission2TestCases",
"options": [
{
@@ -37,6 +29,17 @@
]
},
{
+ "name": "CtsPermissionHostTestCases"
+ },
+ {
+ "name": "CtsAppSecurityHostTestCases",
+ "options": [
+ {
+ "include-filter": "android.appsecurity.cts.AppSecurityTests#rebootWithDuplicatePermission"
+ }
+ ]
+ },
+ {
"name": "CtsStatsdHostTestCases",
"options": [
{
diff --git a/services/core/java/com/android/server/storage/StorageUserConnection.java b/services/core/java/com/android/server/storage/StorageUserConnection.java
index 361a506..1c29c69 100644
--- a/services/core/java/com/android/server/storage/StorageUserConnection.java
+++ b/services/core/java/com/android/server/storage/StorageUserConnection.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
+import android.os.HandlerThread;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
@@ -64,9 +65,6 @@
private static final String TAG = "StorageUserConnection";
private static final int DEFAULT_REMOTE_TIMEOUT_SECONDS = 20;
- // TODO(b/161702661): Workaround for demo user to have shorter timeout.
- // This allows the DevicePolicyManagerService#enableSystemApp call to succeed without ANR.
- private static final int DEMO_USER_REMOTE_TIMEOUT_SECONDS = 5;
private final Object mLock = new Object();
private final Context mContext;
@@ -75,6 +73,7 @@
private final ActiveConnection mActiveConnection = new ActiveConnection();
private final boolean mIsDemoUser;
@GuardedBy("mLock") private final Map<String, Session> mSessions = new HashMap<>();
+ @GuardedBy("mLock") @Nullable private HandlerThread mHandlerThread;
public StorageUserConnection(Context context, int userId, StorageSessionController controller) {
mContext = Objects.requireNonNull(context);
@@ -82,6 +81,10 @@
mSessionController = controller;
mIsDemoUser = LocalServices.getService(UserManagerInternal.class)
.getUserInfo(userId).isDemo();
+ if (mIsDemoUser) {
+ mHandlerThread = new HandlerThread("StorageUserConnectionThread-" + mUserId);
+ mHandlerThread.start();
+ }
}
/**
@@ -188,6 +191,9 @@
*/
public void close() {
mActiveConnection.close();
+ if (mIsDemoUser) {
+ mHandlerThread.quit();
+ }
}
/** Returns all created sessions. */
@@ -207,8 +213,7 @@
private void waitForLatch(CountDownLatch latch, String reason) throws TimeoutException {
try {
- if (!latch.await(mIsDemoUser ? DEMO_USER_REMOTE_TIMEOUT_SECONDS
- : DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+ if (!latch.await(DEFAULT_REMOTE_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
// TODO(b/140025078): Call ActivityManager ANR API?
Slog.wtf(TAG, "Failed to bind to the ExternalStorageService for user " + mUserId);
throw new TimeoutException("Latch wait for " + reason + " elapsed");
@@ -424,15 +429,32 @@
};
Slog.i(TAG, "Binding to the ExternalStorageService for user " + mUserId);
- if (mContext.bindServiceAsUser(new Intent().setComponent(name), mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
- UserHandle.of(mUserId))) {
- Slog.i(TAG, "Bound to the ExternalStorageService for user " + mUserId);
- return mLatch;
+ if (mIsDemoUser) {
+ // Schedule on a worker thread for demo user to avoid deadlock
+ if (mContext.bindServiceAsUser(new Intent().setComponent(name),
+ mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+ mHandlerThread.getThreadHandler(),
+ UserHandle.of(mUserId))) {
+ Slog.i(TAG, "Bound to the ExternalStorageService for user " + mUserId);
+ return mLatch;
+ } else {
+ mIsConnecting = false;
+ throw new ExternalStorageServiceException(
+ "Failed to bind to the ExternalStorageService for user " + mUserId);
+ }
} else {
- mIsConnecting = false;
- throw new ExternalStorageServiceException(
- "Failed to bind to the ExternalStorageService for user " + mUserId);
+ if (mContext.bindServiceAsUser(new Intent().setComponent(name),
+ mServiceConnection,
+ Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT,
+ UserHandle.of(mUserId))) {
+ Slog.i(TAG, "Bound to the ExternalStorageService for user " + mUserId);
+ return mLatch;
+ } else {
+ mIsConnecting = false;
+ throw new ExternalStorageServiceException(
+ "Failed to bind to the ExternalStorageService for user " + mUserId);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index dd67b3d2..024ef87 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -5919,10 +5919,8 @@
// stack, i.e. the hierarchy of the surfaces is unchanged.
if (inPinnedWindowingMode()) {
return getStack().getSurfaceControl();
- } else if (WindowManagerService.sHierarchicalAnimations) {
- return super.getAnimationLeashParent();
} else {
- return getAppAnimationLayer();
+ return super.getAnimationLeashParent();
}
}
@@ -6010,11 +6008,6 @@
mAnimationBoundsLayer = createAnimationBoundsLayer(t);
// Crop to stack bounds.
- if (!WindowManagerService.sHierarchicalAnimations) {
- // For Hierarchical animation, we don't need to set window crop since the leash
- // surface size has already same as the animating container.
- t.setWindowCrop(mAnimationBoundsLayer, mTmpRect);
- }
t.setLayer(leash, 0);
t.setLayer(mAnimationBoundsLayer, getAnimationLayer());
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index d60d098..5720e9b 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -411,10 +411,6 @@
}
}
- if (!WindowManagerService.sHierarchicalAnimations) {
- return new ArraySet<>(candidates);
- }
-
final ArraySet<ActivityRecord> otherApps = visible ? closingApps : openingApps;
// Ancestors of closing apps while finding animation targets for opening apps, or ancestors
// of opening apps while finding animation targets for closing apps.
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 35bd78c..897b680 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3730,17 +3730,6 @@
}
@Override
- public SurfaceControl getAnimationLeashParent() {
- if (WindowManagerService.sHierarchicalAnimations) {
- return super.getAnimationLeashParent();
- }
- // Currently, only the recents animation will create animation leashes for tasks. In this
- // case, reparent the task to the home animation layer while it is being animated to allow
- // the home activity to reorder the app windows relative to its own.
- return getAppAnimationLayer(ANIMATION_LAYER_HOME);
- }
-
- @Override
void resetSurfacePositionForAnimationLeash(SurfaceControl.Transaction t) {
if (isOrganized()) return;
super.resetSurfacePositionForAnimationLeash(t);
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index f37fe3a..a3a4e40 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -51,7 +51,6 @@
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
import static com.android.server.wm.WindowManagerService.logWithStack;
-import static com.android.server.wm.WindowManagerService.sHierarchicalAnimations;
import static com.android.server.wm.WindowStateAnimator.STACK_CLIP_AFTER_ANIM;
import android.annotation.CallSuper;
@@ -2366,10 +2365,6 @@
final Rect screenBounds = getAnimationBounds(appStackClipMode);
mTmpRect.set(screenBounds);
getAnimationPosition(mTmpPoint);
- if (!sHierarchicalAnimations) {
- // Non-hierarchical animation uses position in global coordinates.
- mTmpPoint.set(mTmpRect.left, mTmpRect.top);
- }
mTmpRect.offsetTo(0, 0);
final RemoteAnimationController controller =
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 0b942ba..9acaa9e 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -395,25 +395,9 @@
// trying to apply a new one.
private static final boolean ALWAYS_KEEP_CURRENT = true;
- /**
- * If set, new app transition framework which supports setting animation on any element
- * in a surface is used.
- * <p>
- * Only set this to non-zero once the new app transition framework is productionalized.
- * </p>
- */
- private static final String HIERARCHICAL_ANIMATIONS_PROPERTY =
- "persist.wm.hierarchical_animations";
-
private static final String DISABLE_TRIPLE_BUFFERING_PROPERTY =
"ro.sf.disable_triple_buffer";
- /**
- * @see #HIERARCHICAL_ANIMATIONS_PROPERTY
- */
- static boolean sHierarchicalAnimations =
- SystemProperties.getBoolean(HIERARCHICAL_ANIMATIONS_PROPERTY, true);
-
static boolean sEnableTripleBuffering = !SystemProperties.getBoolean(
DISABLE_TRIPLE_BUFFERING_PROPERTY, false);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 10523a2..7843663 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -239,9 +239,9 @@
void notifySwitch(nsecs_t when, uint32_t switchValues, uint32_t switchMask,
uint32_t policyFlags) override;
void notifyConfigurationChanged(nsecs_t when) override;
- std::chrono::nanoseconds notifyAnr(const sp<InputApplicationHandle>& inputApplicationHandle,
- const sp<IBinder>& token,
- const std::string& reason) override;
+ std::chrono::nanoseconds notifyAnr(
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token, const std::string& reason) override;
void notifyInputChannelBroken(const sp<IBinder>& token) override;
void notifyFocusChanged(const sp<IBinder>& oldToken, const sp<IBinder>& newToken) override;
bool filterInputEvent(const InputEvent* inputEvent, uint32_t policyFlags) override;
@@ -682,8 +682,8 @@
checkAndClearExceptionFromCallback(env, "notifyConfigurationChanged");
}
-static jobject getInputApplicationHandleObjLocalRef(JNIEnv* env,
- const sp<InputApplicationHandle>& inputApplicationHandle) {
+static jobject getInputApplicationHandleObjLocalRef(
+ JNIEnv* env, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
if (inputApplicationHandle == nullptr) {
return nullptr;
}
@@ -694,8 +694,8 @@
}
std::chrono::nanoseconds NativeInputManager::notifyAnr(
- const sp<InputApplicationHandle>& inputApplicationHandle, const sp<IBinder>& token,
- const std::string& reason) {
+ const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
+ const sp<IBinder>& token, const std::string& reason) {
#if DEBUG_INPUT_DISPATCHER_POLICY
ALOGD("notifyANR");
#endif
@@ -780,8 +780,12 @@
void NativeInputManager::setFocusedApplication(JNIEnv* env, int32_t displayId,
jobject applicationHandleObj) {
- sp<InputApplicationHandle> applicationHandle =
+ if (!applicationHandleObj) {
+ return;
+ }
+ std::shared_ptr<InputApplicationHandle> applicationHandle =
android_view_InputApplicationHandle_getHandle(env, applicationHandleObj);
+ applicationHandle->updateInfo();
mInputManager->getDispatcher()->setFocusedApplication(displayId, applicationHandle);
}
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 0dfdd48..922d715 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -99,6 +99,7 @@
AttentionCheck attentionCheck = new AttentionCheck(mMockAttentionCallbackInternal,
mSpyAttentionManager);
mSpyAttentionManager.mCurrentAttentionCheck = attentionCheck;
+ mSpyAttentionManager.mService = new MockIAttentionService();
}
@Test
@@ -115,6 +116,7 @@
@Test
public void testCheckAttention_returnFalseWhenPowerManagerNotInteract() throws RemoteException {
+ doReturn(true).when(mSpyAttentionManager).isAttentionServiceSupported();
doReturn(false).when(mMockIPowerManager).isInteractive();
AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
assertThat(mSpyAttentionManager.checkAttention(mTimeout, callback)).isFalse();
@@ -122,9 +124,11 @@
@Test
public void testCheckAttention_callOnSuccess() throws RemoteException {
- doReturn(true).when(mSpyAttentionManager).isServiceEnabled();
+ doReturn(true).when(mSpyAttentionManager).isAttentionServiceSupported();
+ doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(true).when(mMockIPowerManager).isInteractive();
doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
+ mSpyAttentionManager.mCurrentAttentionCheck = null;
AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
mSpyAttentionManager.checkAttention(mTimeout, callback);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index d7baf8d..7adcead 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -129,36 +129,6 @@
}
@Test
- public void testGetAnimationTargets_noHierarchicalAnimations() {
- WindowManagerService.sHierarchicalAnimations = false;
-
- // [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (opening, invisible)
- // +- [TaskStack2] - [Task2] - [ActivityRecord2] (closing, visible)
- final Task stack1 = createTaskStackOnDisplay(mDisplayContent);
- final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1);
- activity1.setVisible(false);
- activity1.mVisibleRequested = true;
-
- final Task stack2 = createTaskStackOnDisplay(mDisplayContent);
- final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2);
-
- final ArraySet<ActivityRecord> opening = new ArraySet<>();
- opening.add(activity1);
- final ArraySet<ActivityRecord> closing = new ArraySet<>();
- closing.add(activity2);
-
- // Don't promote when the flag is disabled.
- assertEquals(
- new ArraySet<>(new WindowContainer[]{activity1}),
- AppTransitionController.getAnimationTargets(
- opening, closing, true /* visible */));
- assertEquals(
- new ArraySet<>(new WindowContainer[]{activity2}),
- AppTransitionController.getAnimationTargets(
- opening, closing, false /* visible */));
- }
-
- @Test
public void testGetAnimationTargets_visibilityAlreadyUpdated() {
// [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (opening, visible)
// +- [TaskStack2] - [Task2] - [ActivityRecord2] (closing, invisible)
@@ -177,17 +147,6 @@
// No animation, since visibility of the opening and closing apps are already updated
// outside of AppTransition framework.
- WindowManagerService.sHierarchicalAnimations = false;
- assertEquals(
- new ArraySet<>(),
- AppTransitionController.getAnimationTargets(
- opening, closing, true /* visible */));
- assertEquals(
- new ArraySet<>(),
- AppTransitionController.getAnimationTargets(
- opening, closing, false /* visible */));
-
- WindowManagerService.sHierarchicalAnimations = true;
assertEquals(
new ArraySet<>(),
AppTransitionController.getAnimationTargets(
@@ -221,13 +180,12 @@
// The visibility are already updated, but since forced transition is requested, it will
// be included.
- WindowManagerService.sHierarchicalAnimations = false;
assertEquals(
- new ArraySet<>(new WindowContainer[]{activity1}),
+ new ArraySet<>(new WindowContainer[]{activity1.getStack()}),
AppTransitionController.getAnimationTargets(
opening, closing, true /* visible */));
assertEquals(
- new ArraySet<>(new WindowContainer[]{activity2}),
+ new ArraySet<>(new WindowContainer[]{activity2.getStack()}),
AppTransitionController.getAnimationTargets(
opening, closing, false /* visible */));
}
@@ -247,13 +205,6 @@
// Animate closing apps even if it's not visible when it is exiting before we had a chance
// to play the transition animation.
- WindowManagerService.sHierarchicalAnimations = false;
- assertEquals(
- new ArraySet<>(new WindowContainer[]{activity}),
- AppTransitionController.getAnimationTargets(
- new ArraySet<>(), closing, false /* visible */));
-
- WindowManagerService.sHierarchicalAnimations = true;
assertEquals(
new ArraySet<>(new WindowContainer[]{stack}),
AppTransitionController.getAnimationTargets(
@@ -292,17 +243,6 @@
// Animate opening apps even if it's already visible in case its windows are being replaced.
// Don't animate closing apps if it's already invisible even though its windows are being
// replaced.
- WindowManagerService.sHierarchicalAnimations = false;
- assertEquals(
- new ArraySet<>(new WindowContainer[]{activity1}),
- AppTransitionController.getAnimationTargets(
- opening, closing, true /* visible */));
- assertEquals(
- new ArraySet<>(new WindowContainer[]{}),
- AppTransitionController.getAnimationTargets(
- opening, closing, false /* visible */));
-
- WindowManagerService.sHierarchicalAnimations = true;
assertEquals(
new ArraySet<>(new WindowContainer[]{stack1}),
AppTransitionController.getAnimationTargets(
@@ -315,8 +255,6 @@
@Test
public void testGetAnimationTargets_openingClosingInDifferentTask() {
- WindowManagerService.sHierarchicalAnimations = true;
-
// [DisplayContent] -+- [TaskStack1] - [Task1] -+- [ActivityRecord1] (opening, invisible)
// | +- [ActivityRecord2] (invisible)
// |
@@ -361,8 +299,6 @@
@Test
public void testGetAnimationTargets_openingClosingInSameTask() {
- WindowManagerService.sHierarchicalAnimations = true;
-
// [DisplayContent] - [TaskStack] - [Task] -+- [ActivityRecord1] (opening, invisible)
// +- [ActivityRecord2] (closing, visible)
final Task stack = createTaskStackOnDisplay(mDisplayContent);
@@ -393,8 +329,6 @@
@Test
public void testGetAnimationTargets_animateOnlyTranslucentApp() {
- WindowManagerService.sHierarchicalAnimations = true;
-
// [DisplayContent] -+- [TaskStack1] - [Task1] -+- [ActivityRecord1] (opening, invisible)
// | +- [ActivityRecord2] (visible)
// |
@@ -439,8 +373,6 @@
@Test
public void testGetAnimationTargets_animateTranslucentAndOpaqueApps() {
- WindowManagerService.sHierarchicalAnimations = true;
-
// [DisplayContent] -+- [TaskStack1] - [Task1] -+- [ActivityRecord1] (opening, invisible)
// | +- [ActivityRecord2] (opening, invisible)
// |
@@ -489,8 +421,6 @@
@Test
public void testGetAnimationTargets_stackContainsMultipleTasks() {
- WindowManagerService.sHierarchicalAnimations = true;
-
// [DisplayContent] - [TaskStack] -+- [Task1] - [ActivityRecord1] (opening, invisible)
// +- [Task2] - [ActivityRecord2] (closing, visible)
final Task stack = createTaskStackOnDisplay(mDisplayContent);