Merge "PIN Views - Make a bit smaller for better reachability" into sc-dev
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 6967d81..4c8ab93 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -59,15 +59,6 @@
* constraint on the JobInfo object that you are creating. Otherwise, the builder would throw an
* exception when building. From Android version {@link Build.VERSION_CODES#Q} and onwards, it is
* valid to schedule jobs with no constraints.
- * <p> Prior to Android version {@link Build.VERSION_CODES#S}, jobs could only have a maximum of 100
- * jobs scheduled at a time. Starting with Android version {@link Build.VERSION_CODES#S}, that limit
- * has been increased to 150. Expedited jobs also count towards the limit.
- * <p> In Android version {@link Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum execution time
- * of one minute. Starting with Android version {@link Build.VERSION_CODES#M} and ending with
- * Android version {@link Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes.
- * Starting from Android version {@link Build.VERSION_CODES#S}, jobs will still be stopped after
- * 10 minutes if the system is busy or needs the resources, but if not, jobs may continue running
- * longer than 10 minutes.
*/
public class JobInfo implements Parcelable {
private static String TAG = "JobInfo";
@@ -1471,7 +1462,7 @@
* <ol>
* <li>Run as soon as possible</li>
* <li>Be less restricted during Doze and battery saver</li>
- * <li>Have network access</li>
+ * <li>Have the same network access as foreground services</li>
* <li>Be less likely to be killed than regular jobs</li>
* <li>Be subject to background location throttling</li>
* </ol>
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 361325d..1f4ef04 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -57,6 +57,19 @@
* {@link android.content.Context#getSystemService
* Context.getSystemService(Context.JOB_SCHEDULER_SERVICE)}.
*
+ * <p> Prior to Android version {@link android.os.Build.VERSION_CODES#S}, jobs could only have
+ * a maximum of 100 jobs scheduled at a time. Starting with Android version
+ * {@link android.os.Build.VERSION_CODES#S}, that limit has been increased to 150.
+ * Expedited jobs also count towards the limit.
+ *
+ * <p> In Android version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, jobs had a maximum
+ * execution time of one minute. Starting with Android version
+ * {@link android.os.Build.VERSION_CODES#M} and ending with Android version
+ * {@link android.os.Build.VERSION_CODES#R}, jobs had a maximum execution time of 10 minutes.
+ * Starting from Android version {@link android.os.Build.VERSION_CODES#S}, jobs will still be
+ * stopped after 10 minutes if the system is busy or needs the resources, but if not, jobs
+ * may continue running longer than 10 minutes.
+ *
* <p class="caution"><strong>Note:</strong> Beginning with API 30
* ({@link android.os.Build.VERSION_CODES#R}), JobScheduler will throttle runaway applications.
* Calling {@link #schedule(JobInfo)} and other such methods with very high frequency can have a
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index 61afada..0f3d299 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -74,6 +74,7 @@
/**
* Call this to inform the JobScheduler that the job has finished its work. When the
* system receives this message, it releases the wakelock being held for the job.
+ * This does not need to be called if {@link #onStopJob(JobParameters)} has been called.
* <p>
* You can request that the job be scheduled again by passing {@code true} as
* the <code>wantsReschedule</code> parameter. This will apply back-off policy
@@ -135,6 +136,8 @@
/**
* This method is called if the system has determined that you must stop execution of your job
* even before you've had a chance to call {@link #jobFinished(JobParameters, boolean)}.
+ * Once this method is called, you no longer need to call
+ * {@link #jobFinished(JobParameters, boolean)}.
*
* <p>This will happen if the requirements specified at schedule time are no longer met. For
* example you may have requested WiFi with
@@ -144,8 +147,8 @@
* idle maintenance window. You are solely responsible for the behavior of your application
* upon receipt of this message; your app will likely start to misbehave if you ignore it.
* <p>
- * Once this method returns, the system releases the wakelock that it is holding on
- * behalf of the job.</p>
+ * Once this method returns (or times out), the system releases the wakelock that it is holding
+ * on behalf of the job.</p>
*
* @param params The parameters identifying this job, as supplied to
* the job in the {@link #onStartJob(JobParameters)} callback.
diff --git a/api/Android.bp b/api/Android.bp
index 1fdf177..4baf7c1 100644
--- a/api/Android.bp
+++ b/api/Android.bp
@@ -351,6 +351,7 @@
genrule {
name: "services-system-server-current.txt",
srcs: [
+ ":service-media-s{.system-server.api.txt}",
":service-permission{.system-server.api.txt}",
":non-updatable-system-server-current.txt",
],
@@ -374,6 +375,7 @@
genrule {
name: "services-system-server-removed.txt",
srcs: [
+ ":service-media-s{.system-server.removed-api.txt}",
":service-permission{.system-server.removed-api.txt}",
":non-updatable-system-server-removed.txt",
],
diff --git a/core/api/current.txt b/core/api/current.txt
index 97f9855..4aa38d6 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -52401,17 +52401,6 @@
method @Nullable @WorkerThread public android.view.translation.TranslationResponse translate(@NonNull android.view.translation.TranslationRequest);
}
- public final class UiTranslationManager {
- method public void registerUiTranslationStateCallback(@NonNull java.util.concurrent.Executor, @NonNull android.view.translation.UiTranslationStateCallback);
- method public void unregisterUiTranslationStateCallback(@NonNull android.view.translation.UiTranslationStateCallback);
- }
-
- public interface UiTranslationStateCallback {
- method public void onFinished();
- method public void onPaused();
- method public void onStarted(@NonNull String, @NonNull String);
- }
-
public final class ViewTranslationRequest implements android.os.Parcelable {
method public int describeContents();
method @NonNull public android.view.autofill.AutofillId getAutofillId();
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index d4b3f16..e486fa2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -730,6 +730,11 @@
method public static boolean isTranslucentOrFloating(android.content.res.TypedArray);
field public static final long FORCE_NON_RESIZE_APP = 181136395L; // 0xacbec0bL
field public static final long FORCE_RESIZE_APP = 174042936L; // 0xa5faf38L
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // 0xa5faf64L
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO_LARGE = 180326787L; // 0xabf9183L
+ field public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 1.7777778f;
+ field public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // 0xabf91bdL
+ field public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 1.5f;
field public static final int RESIZE_MODE_RESIZEABLE = 2; // 0x2
}
diff --git a/core/java/android/app/IWallpaperManager.aidl b/core/java/android/app/IWallpaperManager.aidl
index 5402381..e83557c 100644
--- a/core/java/android/app/IWallpaperManager.aidl
+++ b/core/java/android/app/IWallpaperManager.aidl
@@ -167,7 +167,8 @@
* @hide
*/
void removeOnLocalColorsChangedListener(
- in ILocalWallpaperColorConsumer callback, int which, int userId, int displayId);
+ in ILocalWallpaperColorConsumer callback, in List<RectF> area,
+ int which, int userId, int displayId);
/**
* @hide
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 7dbbc54..3ef6757 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -68,6 +68,7 @@
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.Pair;
import android.view.Display;
@@ -318,8 +319,20 @@
private int mCachedWallpaperUserId;
private Bitmap mDefaultWallpaper;
private Handler mMainLooperHandler;
- private ArrayMap<LocalWallpaperColorConsumer, ILocalWallpaperColorConsumer>
- mLocalColorCallbacks = new ArrayMap<>();
+ private ArrayMap<RectF, ArraySet<LocalWallpaperColorConsumer>> mLocalColorAreas =
+ new ArrayMap<>();
+ private ILocalWallpaperColorConsumer mLocalColorCallback =
+ new ILocalWallpaperColorConsumer.Stub() {
+ @Override
+ public void onColorsChanged(RectF area, WallpaperColors colors) {
+ ArraySet<LocalWallpaperColorConsumer> callbacks =
+ mLocalColorAreas.get(area);
+ if (callbacks == null) return;
+ for (LocalWallpaperColorConsumer callback: callbacks) {
+ callback.onColorsChanged(area, colors);
+ }
+ }
+ };
Globals(IWallpaperManager service, Looper looper) {
mService = service;
@@ -361,37 +374,46 @@
}
}
- private ILocalWallpaperColorConsumer wrap(LocalWallpaperColorConsumer callback) {
- ILocalWallpaperColorConsumer callback2 = new ILocalWallpaperColorConsumer.Stub() {
- @Override
- public void onColorsChanged(RectF area, WallpaperColors colors) {
- callback.onColorsChanged(area, colors);
- }
- };
- mLocalColorCallbacks.put(callback, callback2);
- return callback2;
- }
-
public void addOnColorsChangedListener(@NonNull LocalWallpaperColorConsumer callback,
@NonNull List<RectF> regions, int which, int userId, int displayId) {
+ for (RectF area: regions) {
+ ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area);
+ if (callbacks == null) {
+ callbacks = new ArraySet<>();
+ mLocalColorAreas.put(area, callbacks);
+ }
+ callbacks.add(callback);
+ }
try {
- mService.addOnLocalColorsChangedListener(wrap(callback) , regions, which,
+ mService.addOnLocalColorsChangedListener(mLocalColorCallback , regions, which,
userId, displayId);
} catch (RemoteException e) {
// Can't get colors, connection lost.
+ Log.e(TAG, "Can't register for local color updates", e);
}
}
public void removeOnColorsChangedListener(
@NonNull LocalWallpaperColorConsumer callback, int which, int userId,
int displayId) {
- ILocalWallpaperColorConsumer callback2 = mLocalColorCallbacks.remove(callback);
- if (callback2 == null) return;
+ final ArrayList<RectF> removeAreas = new ArrayList<>();
+ for (RectF area : mLocalColorAreas.keySet()) {
+ ArraySet<LocalWallpaperColorConsumer> callbacks = mLocalColorAreas.get(area);
+ if (callbacks == null) continue;
+ callbacks.remove(callback);
+ if (callbacks.size() == 0) {
+ mLocalColorAreas.remove(area);
+ removeAreas.add(area);
+ }
+ }
try {
- mService.removeOnLocalColorsChangedListener(
- callback2, which, userId, displayId);
+ if (removeAreas.size() > 0) {
+ mService.removeOnLocalColorsChangedListener(
+ mLocalColorCallback, removeAreas, which, userId, displayId);
+ }
} catch (RemoteException e) {
// Can't get colors, connection lost.
+ Log.e(TAG, "Can't unregister for local color updates", e);
}
}
diff --git a/core/java/android/app/servertransaction/LaunchActivityItem.java b/core/java/android/app/servertransaction/LaunchActivityItem.java
index 94ab0dd..9f8fcc1 100644
--- a/core/java/android/app/servertransaction/LaunchActivityItem.java
+++ b/core/java/android/app/servertransaction/LaunchActivityItem.java
@@ -253,7 +253,7 @@
return other == null;
}
return other != null && mInfo.flags == other.flags
- && mInfo.maxAspectRatio == other.maxAspectRatio
+ && mInfo.getMaxAspectRatio() == other.getMaxAspectRatio()
&& Objects.equals(mInfo.launchToken, other.launchToken)
&& Objects.equals(mInfo.getComponentName(), other.getComponentName());
}
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index 58f83a7..feb58a30 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -21,6 +21,7 @@
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
+import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Intent;
@@ -254,7 +255,7 @@
* @See {@link android.R.attr#maxAspectRatio}.
* @hide
*/
- public float maxAspectRatio;
+ private float mMaxAspectRatio;
/**
* Value indicating the minimum aspect ratio the activity supports.
@@ -263,7 +264,7 @@
* @See {@link android.R.attr#minAspectRatio}.
* @hide
*/
- public float minAspectRatio;
+ private float mMinAspectRatio;
/**
* Indicates that the activity works well with size changes like display changing size.
@@ -948,6 +949,57 @@
public @interface SizeChangesSupportMode {}
/**
+ * This change id is the gatekeeper for all treatments that force a given min aspect ratio.
+ * Enabling this change will allow the following min aspect ratio treatments to be applied:
+ * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM
+ * OVERRIDE_MIN_ASPECT_RATIO_LARGE
+ *
+ * If OVERRIDE_MIN_ASPECT_RATIO is applied, the min aspect ratio given in the app's
+ * manifest will be overridden to the largest enabled aspect ratio treatment unless the app's
+ * manifest value is higher.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO = 174042980L; // buganizer id
+
+ /**
+ * This change id sets the activity's min aspect ratio to a medium value as defined by
+ * OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE.
+ *
+ * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_MEDIUM = 180326845L; // buganizer id
+
+ /** @hide Medium override aspect ratio, currently 3:2. */
+ @TestApi
+ public static final float OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE = 3 / 2f;
+
+ /**
+ * This change id sets the activity's min aspect ratio to a large value as defined by
+ * OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE.
+ *
+ * This treatment only takes effect if OVERRIDE_MIN_ASPECT_RATIO is also enabled.
+ * @hide
+ */
+ @ChangeId
+ @Overridable
+ @Disabled
+ @TestApi
+ public static final long OVERRIDE_MIN_ASPECT_RATIO_LARGE = 180326787L; // buganizer id
+
+ /** @hide Large override aspect ratio, currently 16:9 */
+ @TestApi
+ public static final float OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE = 16 / 9f;
+
+ /**
* Convert Java change bits to native.
*
* @hide
@@ -1118,8 +1170,8 @@
requestedVrComponent = orig.requestedVrComponent;
rotationAnimation = orig.rotationAnimation;
colorMode = orig.colorMode;
- maxAspectRatio = orig.maxAspectRatio;
- minAspectRatio = orig.minAspectRatio;
+ mMaxAspectRatio = orig.mMaxAspectRatio;
+ mMinAspectRatio = orig.mMinAspectRatio;
supportsSizeChanges = orig.supportsSizeChanges;
attributionTags = orig.attributionTags;
}
@@ -1149,7 +1201,7 @@
* @hide
*/
public boolean hasFixedAspectRatio() {
- return maxAspectRatio != 0 || minAspectRatio != 0;
+ return getMaxAspectRatio() != 0 || getMinAspectRatio() != 0;
}
/**
@@ -1262,6 +1314,58 @@
}
/** @hide */
+ public void setMaxAspectRatio(float maxAspectRatio) {
+ this.mMaxAspectRatio = maxAspectRatio;
+ }
+
+ /** @hide */
+ public float getMaxAspectRatio() {
+ return mMaxAspectRatio;
+ }
+
+ /** @hide */
+ public void setMinAspectRatio(float minAspectRatio) {
+ this.mMinAspectRatio = minAspectRatio;
+ }
+
+ /**
+ * Returns the min aspect ratio of this activity.
+ *
+ * This takes into account the minimum aspect ratio as defined in the app's manifest and
+ * possible overrides as per OVERRIDE_MIN_ASPECT_RATIO.
+ *
+ * In the rare cases where the manifest minimum aspect ratio is required, use
+ * {@code getManifestMinAspectRatio}.
+ * @hide
+ */
+ public float getMinAspectRatio() {
+ if (applicationInfo == null || !CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO,
+ applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ return mMinAspectRatio;
+ }
+
+ if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_LARGE,
+ applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ return Math.max(OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE, mMinAspectRatio);
+ }
+
+ if (CompatChanges.isChangeEnabled(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM,
+ applicationInfo.packageName,
+ UserHandle.getUserHandleForUid(applicationInfo.uid))) {
+ return Math.max(OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE, mMinAspectRatio);
+ }
+
+ return mMinAspectRatio;
+ }
+
+ /** @hide */
+ public float getManifestMinAspectRatio() {
+ return mMinAspectRatio;
+ }
+
+ /** @hide */
@UnsupportedAppUsage
public static boolean isResizeableMode(int mode) {
return mode == RESIZE_MODE_RESIZEABLE
@@ -1360,11 +1464,14 @@
if (requestedVrComponent != null) {
pw.println(prefix + "requestedVrComponent=" + requestedVrComponent);
}
- if (maxAspectRatio != 0) {
- pw.println(prefix + "maxAspectRatio=" + maxAspectRatio);
+ if (getMaxAspectRatio() != 0) {
+ pw.println(prefix + "maxAspectRatio=" + getMaxAspectRatio());
}
- if (minAspectRatio != 0) {
- pw.println(prefix + "minAspectRatio=" + minAspectRatio);
+ if (getMinAspectRatio() != 0) {
+ pw.println(prefix + "minAspectRatio=" + getMinAspectRatio());
+ if (getManifestMinAspectRatio() != getMinAspectRatio()) {
+ pw.println(prefix + "getManifestMinAspectRatio=" + getManifestMinAspectRatio());
+ }
}
if (supportsSizeChanges) {
pw.println(prefix + "supportsSizeChanges=true");
@@ -1420,8 +1527,8 @@
dest.writeString8(requestedVrComponent);
dest.writeInt(rotationAnimation);
dest.writeInt(colorMode);
- dest.writeFloat(maxAspectRatio);
- dest.writeFloat(minAspectRatio);
+ dest.writeFloat(mMaxAspectRatio);
+ dest.writeFloat(mMinAspectRatio);
dest.writeBoolean(supportsSizeChanges);
dest.writeString8Array(attributionTags);
}
@@ -1540,8 +1647,8 @@
requestedVrComponent = source.readString8();
rotationAnimation = source.readInt();
colorMode = source.readInt();
- maxAspectRatio = source.readFloat();
- minAspectRatio = source.readFloat();
+ mMaxAspectRatio = source.readFloat();
+ mMinAspectRatio = source.readFloat();
supportsSizeChanges = source.readBoolean();
attributionTags = source.createString8Array();
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 5e08399..5ff1124 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4890,8 +4890,8 @@
info.maxRecents = target.info.maxRecents;
info.windowLayout = target.info.windowLayout;
info.resizeMode = target.info.resizeMode;
- info.maxAspectRatio = target.info.maxAspectRatio;
- info.minAspectRatio = target.info.minAspectRatio;
+ info.setMaxAspectRatio(target.info.getMaxAspectRatio());
+ info.setMinAspectRatio(target.info.getManifestMinAspectRatio());
info.supportsSizeChanges = target.info.supportsSizeChanges;
info.requestedVrComponent = target.info.requestedVrComponent;
@@ -8157,7 +8157,7 @@
return;
}
- info.maxAspectRatio = maxAspectRatio;
+ info.setMaxAspectRatio(maxAspectRatio);
mHasMaxAspectRatio = true;
}
@@ -8173,7 +8173,7 @@
return;
}
- info.minAspectRatio = minAspectRatio;
+ info.setMinAspectRatio(minAspectRatio);
mHasMinAspectRatio = true;
}
diff --git a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
index b660a00..fdd2c2a 100644
--- a/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
+++ b/core/java/android/content/pm/parsing/PackageInfoWithoutStateUtils.java
@@ -474,9 +474,9 @@
ai.screenOrientation = a.getScreenOrientation();
ai.resizeMode = a.getResizeMode();
Float maxAspectRatio = a.getMaxAspectRatio();
- ai.maxAspectRatio = maxAspectRatio != null ? maxAspectRatio : 0f;
+ ai.setMaxAspectRatio(maxAspectRatio != null ? maxAspectRatio : 0f);
Float minAspectRatio = a.getMinAspectRatio();
- ai.minAspectRatio = minAspectRatio != null ? minAspectRatio : 0f;
+ ai.setMinAspectRatio(minAspectRatio != null ? minAspectRatio : 0f);
ai.supportsSizeChanges = a.getSupportsSizeChanges();
ai.requestedVrComponent = a.getRequestedVrComponent();
ai.rotationAnimation = a.getRotationAnimation();
diff --git a/core/java/android/os/RemoteCallbackList.java b/core/java/android/os/RemoteCallbackList.java
index d89c3d5..df4ade0 100644
--- a/core/java/android/os/RemoteCallbackList.java
+++ b/core/java/android/os/RemoteCallbackList.java
@@ -21,7 +21,6 @@
import android.util.Slog;
import java.io.PrintWriter;
-import java.util.function.BiConsumer;
import java.util.function.Consumer;
/**
@@ -355,23 +354,6 @@
}
/**
- * Performs {@code action} on each callback and associated cookie, calling {@link
- * #beginBroadcast()}/{@link #finishBroadcast()} before/after looping.
- *
- * @hide
- */
- public <C> void broadcast(BiConsumer<E, C> action) {
- int itemCount = beginBroadcast();
- try {
- for (int i = 0; i < itemCount; i++) {
- action.accept(getBroadcastItem(i), (C) getBroadcastCookie(i));
- }
- } finally {
- finishBroadcast();
- }
- }
-
- /**
* Returns the number of registered callbacks. Note that the number of registered
* callbacks may differ from the value returned by {@link #beginBroadcast()} since
* the former returns the number of callbacks registered at the time of the call
diff --git a/core/java/android/view/IScrollCaptureCallbacks.aidl b/core/java/android/view/IScrollCaptureCallbacks.aidl
index 26eaac0..9b35614 100644
--- a/core/java/android/view/IScrollCaptureCallbacks.aidl
+++ b/core/java/android/view/IScrollCaptureCallbacks.aidl
@@ -27,13 +27,6 @@
*/
interface IScrollCaptureCallbacks {
/**
- * Provides the result of WindowManagerService#requestScrollCapture
- *
- * @param response the response which describes the result
- */
- oneway void onScrollCaptureResponse(in ScrollCaptureResponse response);
-
- /**
* Called in reply to IScrollCaptureConnection#startCapture, when the remote end has confirmed
* the request and is ready to begin capturing images.
*/
diff --git a/core/java/android/view/IScrollCaptureConnection.aidl b/core/java/android/view/IScrollCaptureConnection.aidl
index c55e888..3a6b693 100644
--- a/core/java/android/view/IScrollCaptureConnection.aidl
+++ b/core/java/android/view/IScrollCaptureConnection.aidl
@@ -18,6 +18,7 @@
import android.graphics.Rect;
import android.os.ICancellationSignal;
+import android.view.IScrollCaptureCallbacks;
import android.view.Surface;
@@ -31,11 +32,12 @@
/**
* Informs the target that it has been selected for scroll capture.
*
- * @param surface a return channel for image buffers
+ * @param surface used to shuttle image buffers between processes
+ * @param callbacks a return channel for requests
*
- * @return a cancallation signal which is used cancel the request
+ * @return a cancallation signal which is used cancel the start request
*/
- ICancellationSignal startCapture(in Surface surface);
+ ICancellationSignal startCapture(in Surface surface, IScrollCaptureCallbacks callbacks);
/**
* Request the target capture an image within the provided rectangle.
diff --git a/core/java/android/view/IScrollCaptureResponseListener.aidl b/core/java/android/view/IScrollCaptureResponseListener.aidl
new file mode 100644
index 0000000..7220f6c
--- /dev/null
+++ b/core/java/android/view/IScrollCaptureResponseListener.aidl
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+import android.view.ScrollCaptureResponse;
+import android.view.Surface;
+
+/**
+ * Asynchronous callback channel for the initial response to a scroll capture request.
+ *
+ * {@hide}
+ */
+interface IScrollCaptureResponseListener {
+ /**
+ * Provides the initial response to a scroll capture request.
+ *
+ * @param response the response which describes the result
+ */
+ oneway void onScrollCaptureResponse(in ScrollCaptureResponse response);
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index fb012eb..8d59ba0 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -26,7 +26,7 @@
import android.view.DragEvent;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.window.ClientWindowFrames;
@@ -134,5 +134,5 @@
*
* @param callbacks to receive responses
*/
- void requestScrollCapture(in IScrollCaptureCallbacks callbacks);
+ void requestScrollCapture(in IScrollCaptureResponseListener callbacks);
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index b345b2e..a42126f 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -42,7 +42,7 @@
import android.view.IDisplayWindowRotationController;
import android.view.IOnKeyguardExitResult;
import android.view.IPinnedTaskListener;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.RemoteAnimationAdapter;
import android.view.IRotationWatcher;
import android.view.ISystemGestureExclusionListener;
@@ -744,10 +744,10 @@
* @param behindClient token for a window, used to filter the search to windows behind it, or
* {@code null} to accept a window at any zOrder
* @param taskId specifies the id of a task the result must belong to, or -1 to ignore task ids
- * @param callbacks the object to receive replies
+ * @param listener the object to receive the response
*/
void requestScrollCapture(int displayId, IBinder behindClient, int taskId,
- IScrollCaptureCallbacks callbacks);
+ IScrollCaptureResponseListener listener);
/**
* Holds the WM lock for the specified amount of milliseconds.
diff --git a/core/java/android/view/ScrollCaptureConnection.java b/core/java/android/view/ScrollCaptureConnection.java
index 3456e01..a6d786e 100644
--- a/core/java/android/view/ScrollCaptureConnection.java
+++ b/core/java/android/view/ScrollCaptureConnection.java
@@ -32,6 +32,7 @@
import com.android.internal.annotations.VisibleForTesting;
+import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -50,8 +51,9 @@
private final Object mLock = new Object();
private final Rect mScrollBounds;
private final Point mPositionInWindow;
- private final CloseGuard mCloseGuard;
private final Executor mUiThread;
+ private final CloseGuard mCloseGuard = new CloseGuard();
+
private ScrollCaptureCallback mLocal;
private IScrollCaptureCallbacks mRemote;
@@ -60,42 +62,38 @@
private CancellationSignal mCancellation;
- private volatile boolean mStarted;
- private volatile boolean mConnected;
+ private volatile boolean mActive;
/**
* Constructs a ScrollCaptureConnection.
*
+ * @param uiThread an executor for the UI thread of the containing View
* @param selectedTarget the target the client is controlling
- * @param remote the callbacks to reply to system requests
*
* @hide
*/
public ScrollCaptureConnection(
@NonNull Executor uiThread,
- @NonNull ScrollCaptureTarget selectedTarget,
- @NonNull IScrollCaptureCallbacks remote) {
+ @NonNull ScrollCaptureTarget selectedTarget) {
mUiThread = requireNonNull(uiThread, "<uiThread> must non-null");
requireNonNull(selectedTarget, "<selectedTarget> must non-null");
- mRemote = requireNonNull(remote, "<callbacks> must non-null");
mScrollBounds = requireNonNull(Rect.copyOrNull(selectedTarget.getScrollBounds()),
"target.getScrollBounds() must be non-null to construct a client");
-
mLocal = selectedTarget.getCallback();
mPositionInWindow = new Point(selectedTarget.getPositionInWindow());
-
- mCloseGuard = new CloseGuard();
- mCloseGuard.open("close");
- mConnected = true;
}
@BinderThread
@Override
- public ICancellationSignal startCapture(Surface surface) throws RemoteException {
- checkConnected();
+ public ICancellationSignal startCapture(@NonNull Surface surface,
+ @NonNull IScrollCaptureCallbacks remote) throws RemoteException {
+
+ mCloseGuard.open("close");
+
if (!surface.isValid()) {
throw new RemoteException(new IllegalArgumentException("surface must be valid"));
}
+ mRemote = requireNonNull(remote, "<callbacks> must non-null");
ICancellationSignal cancellation = CancellationSignal.createTransport();
mCancellation = CancellationSignal.fromTransport(cancellation);
@@ -110,7 +108,7 @@
@UiThread
private void onStartCaptureCompleted() {
- mStarted = true;
+ mActive = true;
try {
mRemote.onCaptureStarted();
} catch (RemoteException e) {
@@ -119,13 +117,11 @@
}
}
-
@BinderThread
@Override
public ICancellationSignal requestImage(Rect requestRect) throws RemoteException {
Trace.beginSection("requestImage");
- checkConnected();
- checkStarted();
+ checkActive();
ICancellationSignal cancellation = CancellationSignal.createTransport();
mCancellation = CancellationSignal.fromTransport(cancellation);
@@ -152,8 +148,7 @@
@BinderThread
@Override
public ICancellationSignal endCapture() throws RemoteException {
- checkConnected();
- checkStarted();
+ checkActive();
ICancellationSignal cancellation = CancellationSignal.createTransport();
mCancellation = CancellationSignal.fromTransport(cancellation);
@@ -167,64 +162,48 @@
@UiThread
private void onEndCaptureCompleted() {
- synchronized (mLock) {
- mStarted = false;
- try {
+ mActive = false;
+ try {
+ if (mRemote != null) {
mRemote.onCaptureEnded();
- } catch (RemoteException e) {
- Log.w(TAG, "Shutting down due to error: ", e);
- close();
}
+ } catch (RemoteException e) {
+ Log.w(TAG, "Caught exception confirming capture end!", e);
+ } finally {
+ close();
}
}
@BinderThread
@Override
public void close() {
- if (mStarted) {
- Log.w(TAG, "close(): capture is still started?! Ending now.");
-
+ if (mActive) {
+ if (mCancellation != null) {
+ Log.w(TAG, "close(): cancelling pending operation.");
+ mCancellation.cancel();
+ mCancellation = null;
+ }
+ Log.w(TAG, "close(): capture session still active! Ending now.");
// -> UiThread
mUiThread.execute(() -> mLocal.onScrollCaptureEnd(() -> { /* ignore */ }));
- mStarted = false;
+ mActive = false;
}
- disconnect();
+ mActive = false;
+ mSession = null;
+ mRemote = null;
+ mLocal = null;
+ mCloseGuard.close();
+ Reference.reachabilityFence(this);
}
- /**
- * Shuts down this client and releases references to dependent objects. No attempt is made
- * to notify the controller, use with caution!
- */
- private void disconnect() {
+ @VisibleForTesting
+ public boolean isActive() {
+ return mActive;
+ }
+
+ private void checkActive() throws RemoteException {
synchronized (mLock) {
- mSession = null;
- mConnected = false;
- mStarted = false;
- mRemote = null;
- mLocal = null;
- mCloseGuard.close();
- }
- }
-
- public boolean isConnected() {
- return mConnected;
- }
-
- public boolean isStarted() {
- return mStarted;
- }
-
- private synchronized void checkConnected() throws RemoteException {
- synchronized (mLock) {
- if (!mConnected) {
- throw new RemoteException(new IllegalStateException("Not connected"));
- }
- }
- }
-
- private void checkStarted() throws RemoteException {
- synchronized (mLock) {
- if (!mStarted) {
+ if (!mActive) {
throw new RemoteException(new IllegalStateException("Not started!"));
}
}
@@ -233,24 +212,16 @@
/** @return a string representation of the state of this client */
public String toString() {
return "ScrollCaptureConnection{"
- + "connected=" + mConnected
- + ", started=" + mStarted
+ + "active=" + mActive
+ ", session=" + mSession
+ ", remote=" + mRemote
+ ", local=" + mLocal
+ "}";
}
- @VisibleForTesting
- public CancellationSignal getCancellation() {
- return mCancellation;
- }
-
protected void finalize() throws Throwable {
try {
- if (mCloseGuard != null) {
- mCloseGuard.warnIfOpen();
- }
+ mCloseGuard.warnIfOpen();
close();
} finally {
super.finalize();
diff --git a/core/java/android/view/ScrollCaptureResponse.java b/core/java/android/view/ScrollCaptureResponse.java
index 564113e..8808827 100644
--- a/core/java/android/view/ScrollCaptureResponse.java
+++ b/core/java/android/view/ScrollCaptureResponse.java
@@ -20,6 +20,7 @@
import android.annotation.Nullable;
import android.graphics.Rect;
import android.os.Parcelable;
+import android.os.RemoteException;
import com.android.internal.util.DataClass;
@@ -57,11 +58,22 @@
@DataClass.PluralOf("message")
private ArrayList<String> mMessages = new ArrayList<>();
- /** Whether a connection has been returned. */
+ /** Whether an active connection is present. */
public boolean isConnected() {
- return mConnection != null;
+ return mConnection != null && mConnection.asBinder().isBinderAlive();
}
+ /** Closes a connection returned with this response. */
+ public void close() {
+ if (mConnection != null) {
+ try {
+ mConnection.close();
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ mConnection = null;
+ }
+ }
@@ -367,10 +379,10 @@
}
@DataClass.Generated(
- time = 1612282689462L,
+ time = 1614833185795L,
codegenVersion = "1.0.22",
sourceFile = "frameworks/base/core/java/android/view/ScrollCaptureResponse.java",
- inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
+ inputSignatures = "private @android.annotation.NonNull java.lang.String mDescription\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.MaySetToNull android.view.IScrollCaptureConnection mConnection\nprivate @android.annotation.Nullable android.graphics.Rect mWindowBounds\nprivate @android.annotation.Nullable android.graphics.Rect mBoundsInWindow\nprivate @android.annotation.Nullable java.lang.String mWindowTitle\nprivate @android.annotation.NonNull @com.android.internal.util.DataClass.PluralOf(\"message\") java.util.ArrayList<java.lang.String> mMessages\npublic boolean isConnected()\npublic void close()\nclass ScrollCaptureResponse extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genGetters=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 35726c0..d462f58 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -5356,7 +5356,7 @@
updateLocationInParentDisplay(msg.arg1, msg.arg2);
} break;
case MSG_REQUEST_SCROLL_CAPTURE:
- handleScrollCaptureRequest((IScrollCaptureCallbacks) msg.obj);
+ handleScrollCaptureRequest((IScrollCaptureResponseListener) msg.obj);
break;
}
}
@@ -9267,10 +9267,10 @@
/**
* Dispatches a scroll capture request to the view hierarchy on the ui thread.
*
- * @param callbacks for replies
+ * @param listener for the response
*/
- public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
- mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, callbacks).sendToTarget();
+ public void dispatchScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) {
+ mHandler.obtainMessage(MSG_REQUEST_SCROLL_CAPTURE, listener).sendToTarget();
}
/**
@@ -9317,10 +9317,10 @@
* A call to {@link IScrollCaptureCallbacks#onScrollCaptureResponse(ScrollCaptureResponse)}
* will follow.
*
- * @param callbacks to receive responses
+ * @param listener to receive responses
* @see ScrollCaptureTargetSelector
*/
- public void handleScrollCaptureRequest(@NonNull IScrollCaptureCallbacks callbacks) {
+ public void handleScrollCaptureRequest(@NonNull IScrollCaptureResponseListener listener) {
ScrollCaptureSearchResults results =
new ScrollCaptureSearchResults(mContext.getMainExecutor());
@@ -9335,7 +9335,7 @@
getChildVisibleRect(rootView, rect, point);
rootView.dispatchScrollCaptureSearch(rect, point, results::addTarget);
}
- Runnable onComplete = () -> dispatchScrollCaptureSearchResult(callbacks, results);
+ Runnable onComplete = () -> dispatchScrollCaptureSearchResponse(listener, results);
results.setOnCompleteListener(onComplete);
if (!results.isComplete()) {
mHandler.postDelayed(results::finish, getScrollCaptureRequestTimeout());
@@ -9343,8 +9343,8 @@
}
/** Called by {@link #handleScrollCaptureRequest} when a result is returned */
- private void dispatchScrollCaptureSearchResult(
- @NonNull IScrollCaptureCallbacks callbacks,
+ private void dispatchScrollCaptureSearchResponse(
+ @NonNull IScrollCaptureResponseListener listener,
@NonNull ScrollCaptureSearchResults results) {
ScrollCaptureTarget selectedTarget = results.getTopResult();
@@ -9361,7 +9361,7 @@
if (selectedTarget == null) {
response.setDescription("No scrollable targets found in window");
try {
- callbacks.onScrollCaptureResponse(response.build());
+ listener.onScrollCaptureResponse(response.build());
} catch (RemoteException e) {
Log.e(TAG, "Failed to send scroll capture search result", e);
}
@@ -9387,11 +9387,11 @@
// Create a connection and return it to the caller
ScrollCaptureConnection connection = new ScrollCaptureConnection(
- mView.getContext().getMainExecutor(), selectedTarget, callbacks);
+ mView.getContext().getMainExecutor(), selectedTarget);
response.setConnection(connection);
try {
- callbacks.onScrollCaptureResponse(response.build());
+ listener.onScrollCaptureResponse(response.build());
} catch (RemoteException e) {
if (DEBUG_SCROLL_CAPTURE) {
Log.w(TAG, "Failed to send scroll capture search response.", e);
@@ -9691,10 +9691,10 @@
}
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
- viewAncestor.dispatchScrollCaptureRequest(callbacks);
+ viewAncestor.dispatchScrollCaptureRequest(listener);
}
}
}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
index cf5ec8d..c814e5a 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -2619,10 +2619,10 @@
/**
* System request to begin scroll capture.
*
- * @param callbacks to receive responses
+ * @param listener to receive the response
* @hide
*/
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
}
/**
diff --git a/core/java/android/view/translation/ITranslationManager.aidl b/core/java/android/view/translation/ITranslationManager.aidl
index d347f31..7f6c4b4 100644
--- a/core/java/android/view/translation/ITranslationManager.aidl
+++ b/core/java/android/view/translation/ITranslationManager.aidl
@@ -17,7 +17,6 @@
package android.view.translation;
import android.os.IBinder;
-import android.os.IRemoteCallback;
import android.view.autofill.AutofillId;
import android.view.translation.TranslationSpec;
import com.android.internal.os.IResultReceiver;
@@ -41,7 +40,4 @@
void updateUiTranslationStateByTaskId(int state, in TranslationSpec sourceSpec,
in TranslationSpec destSpec, in List<AutofillId> viewIds, int taskId,
int userId);
-
- void registerUiTranslationStateCallback(in IRemoteCallback callback, int userId);
- void unregisterUiTranslationStateCallback(in IRemoteCallback callback, int userId);
}
diff --git a/core/java/android/view/translation/UiTranslationManager.java b/core/java/android/view/translation/UiTranslationManager.java
index 9fba95f..7c73e70 100644
--- a/core/java/android/view/translation/UiTranslationManager.java
+++ b/core/java/android/view/translation/UiTranslationManager.java
@@ -16,36 +16,28 @@
package android.view.translation;
-import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.app.assist.ActivityId;
import android.content.Context;
-import android.os.Binder;
-import android.os.Bundle;
-import android.os.IRemoteCallback;
import android.os.RemoteException;
-import android.util.ArrayMap;
-import android.util.Log;
import android.view.View;
import android.view.autofill.AutofillId;
-import com.android.internal.annotations.GuardedBy;
-
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.Executor;
-// TODO(b/178044703): Describe what UI Translation is.
/**
* The {@link UiTranslationManager} class provides ways for apps to use the ui translation
* function in framework.
+ *
+ * @hide
*/
+@SystemApi
public final class UiTranslationManager {
private static final String TAG = "UiTranslationManager";
@@ -96,14 +88,6 @@
public @interface UiTranslationState {
}
- // Keys for the data transmitted in the internal UI Translation state callback.
- /** @hide */
- public static final String EXTRA_STATE = "state";
- /** @hide */
- public static final String EXTRA_SOURCE_LOCALE = "source_locale";
- /** @hide */
- public static final String EXTRA_TARGET_LOCALE = "target_locale";
-
@NonNull
private final Context mContext;
@@ -127,12 +111,9 @@
* @param destSpec {@link TranslationSpec} for the translated data.
* @param viewIds A list of the {@link View}'s {@link AutofillId} which needs to be translated
* @param taskId the Activity Task id which needs ui translation
- *
- * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
- @SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
int taskId) {
@@ -160,11 +141,8 @@
* @throws IllegalArgumentException if the no {@link View}'s {@link AutofillId} in the list
* @throws NullPointerException the sourceSpec, destSpec, viewIds, activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
- *
- * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
- @SystemApi
public void startTranslation(@NonNull TranslationSpec sourceSpec,
@NonNull TranslationSpec destSpec, @NonNull List<AutofillId> viewIds,
@NonNull ActivityId activityId) {
@@ -193,12 +171,9 @@
* NOTE: Please use {@code finishTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
- *
- * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
- @SystemApi
public void finishTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_FINISHED,
@@ -216,11 +191,8 @@
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
- *
- * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
- @SystemApi
public void finishTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -240,12 +212,9 @@
* NOTE: Please use {@code pauseTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
- *
- * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
- @SystemApi
public void pauseTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_PAUSED,
@@ -263,11 +232,8 @@
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
- *
- * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
- @SystemApi
public void pauseTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -287,12 +253,9 @@
* NOTE: Please use {@code resumeTranslation(ActivityId)} instead.
*
* @param taskId the Activity Task id which needs ui translation
- *
- * @hide
*/
// TODO, hide the APIs
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
- @SystemApi
public void resumeTranslation(int taskId) {
try {
mService.updateUiTranslationStateByTaskId(STATE_UI_TRANSLATION_RESUMED,
@@ -310,11 +273,8 @@
* @param activityId the identifier for the Activity which needs ui translation
* @throws NullPointerException the activityId or
* {@link android.app.assist.ActivityId#getToken()} is {@code null}
- *
- * @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_UI_TRANSLATION)
- @SystemApi
public void resumeTranslation(@NonNull ActivityId activityId) {
try {
Objects.requireNonNull(activityId);
@@ -326,104 +286,4 @@
throw e.rethrowFromSystemServer();
}
}
-
- // TODO(b/178044703): Fix the View API link when it becomes public.
- /**
- * Register for notifications of UI Translation state changes on the foreground activity. This
- * is available to the owning application itself and also the current input method.
- * <p>
- * The application whose UI is being translated can use this to customize the UI Translation
- * behavior in ways that aren't made easy by methods like
- * View#onCreateTranslationRequest().
- * <p>
- * Input methods can use this to offer complementary features to UI Translation; for example,
- * enabling outgoing message translation when the system is translating incoming messages in a
- * communication app.
- *
- * @param callback the callback to register for receiving the state change
- * notifications
- */
- public void registerUiTranslationStateCallback(
- @NonNull @CallbackExecutor Executor executor,
- @NonNull UiTranslationStateCallback callback) {
- Objects.requireNonNull(executor);
- Objects.requireNonNull(callback);
- synchronized (mCallbacks) {
- if (mCallbacks.containsKey(callback)) {
- Log.w(TAG, "registerUiTranslationStateCallback: callback already registered;"
- + " ignoring.");
- return;
- }
- final IRemoteCallback remoteCallback =
- new UiTranslationStateRemoteCallback(executor, callback);
- try {
- mService.registerUiTranslationStateCallback(remoteCallback, mContext.getUserId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- mCallbacks.put(callback, remoteCallback);
- }
- }
-
- /**
- * Unregister {@code callback}.
- *
- * @see #registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)
- */
- public void unregisterUiTranslationStateCallback(@NonNull UiTranslationStateCallback callback) {
- Objects.requireNonNull(callback);
-
- synchronized (mCallbacks) {
- final IRemoteCallback remoteCallback = mCallbacks.get(callback);
- if (remoteCallback == null) {
- Log.w(TAG, "unregisterUiTranslationStateCallback: callback not found; ignoring.");
- return;
- }
- try {
- mService.unregisterUiTranslationStateCallback(remoteCallback, mContext.getUserId());
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- mCallbacks.remove(callback);
- }
- }
-
- @NonNull
- @GuardedBy("mCallbacks")
- private final Map<UiTranslationStateCallback, IRemoteCallback> mCallbacks = new ArrayMap<>();
-
- private static class UiTranslationStateRemoteCallback extends IRemoteCallback.Stub {
- private final Executor mExecutor;
- private final UiTranslationStateCallback mCallback;
-
- UiTranslationStateRemoteCallback(Executor executor,
- UiTranslationStateCallback callback) {
- mExecutor = executor;
- mCallback = callback;
- }
-
- @Override
- public void sendResult(Bundle bundle) {
- Binder.clearCallingIdentity();
- mExecutor.execute(() -> {
- int state = bundle.getInt(EXTRA_STATE);
- switch (state) {
- case STATE_UI_TRANSLATION_STARTED:
- case STATE_UI_TRANSLATION_RESUMED:
- mCallback.onStarted(
- bundle.getString(EXTRA_SOURCE_LOCALE),
- bundle.getString(EXTRA_TARGET_LOCALE));
- break;
- case STATE_UI_TRANSLATION_PAUSED:
- mCallback.onPaused();
- break;
- case STATE_UI_TRANSLATION_FINISHED:
- mCallback.onFinished();
- break;
- default:
- Log.wtf(TAG, "Unexpected translation state:" + state);
- }
- });
- }
- }
}
diff --git a/core/java/android/view/translation/UiTranslationStateCallback.java b/core/java/android/view/translation/UiTranslationStateCallback.java
deleted file mode 100644
index 1946b70..0000000
--- a/core/java/android/view/translation/UiTranslationStateCallback.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view.translation;
-
-import android.annotation.NonNull;
-
-import java.util.concurrent.Executor;
-
-/**
- * Callback for listening to UI Translation state changes. See {@link
- * UiTranslationManager#registerUiTranslationStateCallback(Executor, UiTranslationStateCallback)}.
- */
-public interface UiTranslationStateCallback {
-
- /**
- * The system is requesting translation of the UI from {@code sourceLocale} to {@code
- * targetLocale}.
- * <p>
- * This is also called if either the requested {@code sourceLocale} or {@code targetLocale} has
- * changed; or called again after {@link #onPaused()}.
- */
- void onStarted(@NonNull String sourceLocale, @NonNull String targetLocale);
-
- /**
- * The system is requesting that the application temporarily show the UI contents in their
- * original language.
- */
- void onPaused();
-
- /**
- * The UI Translation session has ended.
- */
- void onFinished();
-}
diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java
index c203c790..05c0047 100644
--- a/core/java/android/widget/EdgeEffect.java
+++ b/core/java/android/widget/EdgeEffect.java
@@ -177,7 +177,7 @@
private long mStartTime;
private float mDuration;
private float mStretchIntensity = DEFAULT_MAX_STRETCH_INTENSITY;
- private float mStretchDistanceFraction = 0.1f;
+ private float mStretchDistanceFraction = 1f;
private float mStretchDistance = -1f;
private final Interpolator mInterpolator = new DecelerateInterpolator();
@@ -656,7 +656,7 @@
// for now leverage placeholder logic if no stretch distance is provided to
// consume the displacement ratio times the minimum of the width or height
mStretchDistance > 0 ? mStretchDistance :
- (mStretchDistanceFraction * Math.min(mWidth, mHeight))
+ (mStretchDistanceFraction * Math.max(mWidth, mHeight))
);
}
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index d02dd8c..2b73923 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -4493,7 +4493,8 @@
* Call a method taking one int, a size in pixels, on a view in the layout for this
* RemoteViews.
*
- * The dimension will be resolved from the resources at the time of inflation.
+ * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4525,7 +4526,8 @@
/**
* Call a method taking one int, a color, on a view in the layout for this RemoteViews.
*
- * The ColorStateList will be resolved from the resources at the time of inflation.
+ * The Color will be resolved from the resources at the time the {@link RemoteViews} is (re-)
+ * applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4602,7 +4604,8 @@
/**
* Call a method taking one ColorStateList on a view in the layout for this RemoteViews.
*
- * The ColorStateList will be resolved from the resources at the time of inflation.
+ * The ColorStateList will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4641,7 +4644,8 @@
* Call a method taking one float, a size in pixels, on a view in the layout for this
* RemoteViews.
*
- * The dimension will be resolved from the resources at the time of inflation.
+ * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4657,7 +4661,8 @@
* Call a method taking one float, a size in pixels, on a view in the layout for this
* RemoteViews.
*
- * The dimension will be resolved from the specified dimension at the time of inflation.
+ * The dimension will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
@@ -4719,7 +4724,8 @@
/**
* Call a method taking one CharSequence on a view in the layout for this RemoteViews.
*
- * The CharSequence will be resolved from the resources at the time of inflation.
+ * The CharSequence will be resolved from the resources at the time the {@link RemoteViews} is
+ * (re-)applied.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index e602cd2..a60b310 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -504,8 +504,10 @@
final File visibleFile = getFileForDocId(documentId, true);
final int pfdMode = ParcelFileDescriptor.parseMode(mode);
- if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY || visibleFile == null) {
- return openFileForRead(file);
+ if (visibleFile == null) {
+ return ParcelFileDescriptor.open(file, pfdMode);
+ } else if (pfdMode == ParcelFileDescriptor.MODE_READ_ONLY) {
+ return openFileForRead(visibleFile);
} else {
try {
// When finished writing, kick off media scanner
@@ -522,6 +524,10 @@
private ParcelFileDescriptor openFileForRead(final File target) throws FileNotFoundException {
final Uri uri = MediaStore.scanFile(getContext().getContentResolver(), target);
+ if (uri == null) {
+ Log.w(TAG, "Failed to retrieve media store URI for: " + target);
+ return ParcelFileDescriptor.open(target, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
// Passing the calling uid via EXTRA_MEDIA_CAPABILITIES_UID, so that the decision to
// transcode or not transcode can be made based upon the calling app's uid, and not based
@@ -532,7 +538,8 @@
final AssetFileDescriptor afd =
getContext().getContentResolver().openTypedAssetFileDescriptor(uri, "*/*", opts);
if (afd == null) {
- return null;
+ Log.w(TAG, "Failed to open with media_capabilities uid for URI: " + uri);
+ return ParcelFileDescriptor.open(target, ParcelFileDescriptor.MODE_READ_ONLY);
}
return afd.getParcelFileDescriptor();
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 6049486..39bde74 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -78,7 +78,7 @@
import android.view.CrossWindowBlurListeners;
import android.view.Gravity;
import android.view.IRotationWatcher.Stub;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindowManager;
import android.view.InputDevice;
import android.view.InputEvent;
@@ -3940,12 +3940,12 @@
/**
* System request to begin scroll capture.
*
- * @param callbacks to receive responses
+ * @param listener to receive the response
* @hide
*/
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
- getViewRootImpl().dispatchScrollCaptureRequest(callbacks);
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
+ getViewRootImpl().dispatchScrollCaptureRequest(listener);
}
/**
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index ab0149f..47341cd 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -24,7 +24,7 @@
import android.os.RemoteException;
import android.util.MergedConfiguration;
import android.view.DragEvent;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindow;
import android.view.IWindowSession;
import android.view.InsetsSourceControl;
@@ -159,9 +159,9 @@
}
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
try {
- callbacks.onScrollCaptureResponse(
+ listener.onScrollCaptureResponse(
new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build());
} catch (RemoteException ex) {
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index cbf4481..31cc77f 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -746,7 +746,7 @@
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
Rect crop(l, t, r, b);
- transaction->setCrop_legacy(ctrl, crop);
+ transaction->setCrop(ctrl, crop);
}
static void nativeSetCornerRadius(JNIEnv* env, jclass clazz, jlong transactionObj,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f135d67..68127e3 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -5607,6 +5607,10 @@
<!-- Attribution for Gnss Time Update service. -->
<attribution android:tag="GnssTimeUpdateService"
android:label="@string/gnss_time_update_service"/>
+ <!-- Attribution for MusicRecognitionManagerService.
+ <p>Not for use by third-party applications.</p> -->
+ <attribution android:tag="MusicRecognitionManagerService"
+ android:label="@string/music_recognition_manager_service"/>
<application android:process="system"
android:persistent="true"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2c7f9a4..054d108 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -453,6 +453,9 @@
<!-- Attribution for Gnss Time Update service. [CHAR LIMIT=NONE]-->
<string name="gnss_time_update_service">GNSS Time Update Service</string>
+ <!-- Attribution for MusicRecognitionManagerService. [CHAR LIMIT=NONE]-->
+ <string name="music_recognition_manager_service">Music Recognition Manager Service</string>
+
<!-- Factory reset warning dialog strings--> <skip />
<!-- Shows up in the dialog's title to warn about an impeding factory reset. [CHAR LIMIT=NONE] -->
<string name="factory_reset_warning">Your device will be erased</string>
diff --git a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
index 6c8b941..9915e38 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ObjectPoolTests.java
@@ -133,7 +133,7 @@
int ident = 57;
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.flags = 42;
- activityInfo.maxAspectRatio = 2.4f;
+ activityInfo.setMaxAspectRatio(2.4f);
activityInfo.launchToken = "token";
activityInfo.applicationInfo = new ApplicationInfo();
activityInfo.packageName = "packageName";
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index 6f3d7ae..f47fa39 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -182,7 +182,7 @@
int ident = 57;
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.flags = 42;
- activityInfo.maxAspectRatio = 2.4f;
+ activityInfo.setMaxAspectRatio(2.4f);
activityInfo.launchToken = "token";
activityInfo.applicationInfo = new ApplicationInfo();
activityInfo.packageName = "packageName";
diff --git a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
index 516fb76..f3a6f9e 100644
--- a/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
+++ b/core/tests/coretests/src/android/view/ScrollCaptureConnectionTest.java
@@ -74,7 +74,7 @@
mTarget = new ScrollCaptureTarget(mView, mLocalVisibleRect, mPositionInWindow, mCallback);
mTarget.setScrollBounds(mScrollBounds);
- mConnection = new ScrollCaptureConnection(Runnable::run, mTarget, mRemote);
+ mConnection = new ScrollCaptureConnection(Runnable::run, mTarget);
}
/** Test creating a client with valid info */
@@ -83,7 +83,7 @@
ScrollCaptureTarget target = new ScrollCaptureTarget(
mView, mLocalVisibleRect, mPositionInWindow, mCallback);
target.setScrollBounds(new Rect(1, 2, 3, 4));
- new ScrollCaptureConnection(Runnable::run, target, mRemote);
+ new ScrollCaptureConnection(Runnable::run, target);
}
/** Test creating a client fails if arguments are not valid. */
@@ -91,20 +91,20 @@
public void testConstruction_requiresScrollBounds() {
try {
mTarget.setScrollBounds(null);
- new ScrollCaptureConnection(Runnable::run, mTarget, mRemote);
+ new ScrollCaptureConnection(Runnable::run, mTarget);
fail("An exception was expected.");
} catch (RuntimeException ex) {
// Ignore, expected.
}
}
- /** @see ScrollCaptureConnection#startCapture(Surface) */
+ /** @see ScrollCaptureConnection#startCapture(Surface, IScrollCaptureCallbacks) */
@Test
public void testStartCapture() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
- assertTrue(mConnection.isStarted());
+ assertTrue(mConnection.isActive());
verify(mRemote, times(1)).onCaptureStarted();
verifyNoMoreInteractions(mRemote);
@@ -112,11 +112,11 @@
@Test
public void testStartCapture_cancellation() throws Exception {
- ICancellationSignal signal = mConnection.startCapture(mSurface);
+ ICancellationSignal signal = mConnection.startCapture(mSurface, mRemote);
signal.cancel();
mCallback.completeStartRequest();
- assertFalse(mConnection.isStarted());
+ assertFalse(mConnection.isActive());
verifyNoMoreInteractions(mRemote);
}
@@ -124,7 +124,7 @@
/** @see ScrollCaptureConnection#requestImage(Rect) */
@Test
public void testRequestImage() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
reset(mRemote);
@@ -138,7 +138,7 @@
@Test
public void testRequestImage_cancellation() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
reset(mRemote);
@@ -152,7 +152,7 @@
/** @see ScrollCaptureConnection#endCapture() */
@Test
public void testEndCapture() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
reset(mRemote);
@@ -167,7 +167,7 @@
/** @see ScrollCaptureConnection#endCapture() */
@Test
public void testEndCapture_cancellation() throws Exception {
- mConnection.startCapture(mSurface);
+ mConnection.startCapture(mSurface, mRemote);
mCallback.completeStartRequest();
reset(mRemote);
@@ -179,9 +179,9 @@
}
@Test
- public void testClose() throws Exception {
+ public void testClose() {
mConnection.close();
- assertFalse(mConnection.isConnected());
+ assertFalse(mConnection.isActive());
verifyNoMoreInteractions(mRemote);
}
diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java
index 7746bc2..e0d9ecfc 100644
--- a/core/tests/coretests/src/android/view/ViewRootImplTest.java
+++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java
@@ -205,7 +205,7 @@
@Test
public void requestScrollCapture_withoutContentRoot() {
final CountDownLatch latch = new CountDownLatch(1);
- mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
+ mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureResponseListener.Default() {
@Override
public void onScrollCaptureResponse(ScrollCaptureResponse response) {
latch.countDown();
@@ -237,7 +237,7 @@
final CountDownLatch latch = new CountDownLatch(1);
mViewRootImpl.setScrollCaptureRequestTimeout(100);
- mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureCallbacks.Default() {
+ mViewRootImpl.handleScrollCaptureRequest(new IScrollCaptureResponseListener.Default() {
@Override
public void onScrollCaptureResponse(ScrollCaptureResponse response) {
latch.countDown();
diff --git a/graphics/java/android/graphics/drawable/RippleAnimationSession.java b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
index f3bf63b..b57f7af 100644
--- a/graphics/java/android/graphics/drawable/RippleAnimationSession.java
+++ b/graphics/java/android/graphics/drawable/RippleAnimationSession.java
@@ -38,8 +38,7 @@
*/
public final class RippleAnimationSession {
private static final String TAG = "RippleAnimationSession";
- private static final int ENTER_ANIM_DURATION = 300;
- private static final int SLIDE_ANIM_DURATION = 450;
+ private static final int ENTER_ANIM_DURATION = 450;
private static final int EXIT_ANIM_DURATION = 300;
private static final TimeInterpolator LINEAR_INTERPOLATOR = new LinearInterpolator();
private static final TimeInterpolator PATH_INTERPOLATOR =
@@ -50,26 +49,21 @@
private Runnable mOnUpdate;
private long mStartTime;
private boolean mForceSoftware;
- private final float mWidth, mHeight;
private final ValueAnimator mSparkle = ValueAnimator.ofFloat(0, 1);
- private final ArraySet<Animator> mActiveAnimations = new ArraySet<>(3);
RippleAnimationSession(@NonNull AnimationProperties<Float, Paint> properties,
- boolean forceSoftware, float width, float height) {
+ boolean forceSoftware) {
mProperties = properties;
mForceSoftware = forceSoftware;
- mWidth = width;
- mHeight = height;
mSparkle.addUpdateListener(anim -> {
final long now = AnimationUtils.currentAnimationTimeMillis();
final long elapsed = now - mStartTime - ENTER_ANIM_DURATION;
- final float phase = (float) elapsed / 1000f;
- mProperties.getShader().setSecondsOffset(phase);
+ final float phase = (float) elapsed / 30000f;
+ mProperties.getShader().setNoisePhase(phase);
notifyUpdate();
});
mSparkle.setDuration(ENTER_ANIM_DURATION);
- mSparkle.setStartDelay(ENTER_ANIM_DURATION);
mSparkle.setInterpolator(LINEAR_INTERPOLATOR);
mSparkle.setRepeatCount(ValueAnimator.INFINITE);
}
@@ -85,7 +79,6 @@
}
@NonNull RippleAnimationSession exit(Canvas canvas) {
- mSparkle.end();
if (isHwAccelerated(canvas)) exitHardware((RecordingCanvas) canvas);
else exitSoftware();
return this;
@@ -93,7 +86,6 @@
private void onAnimationEnd(Animator anim) {
notifyUpdate();
- mActiveAnimations.remove(anim);
}
@NonNull RippleAnimationSession setOnSessionEnd(
@@ -123,18 +115,18 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
+ mSparkle.end();
Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
}
});
expand.setInterpolator(LINEAR_INTERPOLATOR);
expand.start();
- mActiveAnimations.add(expand);
}
private long computeDelay() {
final long timePassed = AnimationUtils.currentAnimationTimeMillis() - mStartTime;
- return Math.max((long) SLIDE_ANIM_DURATION - timePassed, 0);
+ return Math.max((long) ENTER_ANIM_DURATION - timePassed, 0);
}
private void notifyUpdate() {
@@ -157,6 +149,7 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
+ mSparkle.end();
Consumer<RippleAnimationSession> onEnd = mOnSessionEnd;
if (onEnd != null) onEnd.accept(RippleAnimationSession.this);
}
@@ -167,7 +160,6 @@
long delay = computeDelay();
exit.setStartDelay(delay);
exit.start();
- mActiveAnimations.add(exit);
}
private void enterHardware(RecordingCanvas canvas) {
@@ -175,54 +167,27 @@
props = getCanvasProperties();
RenderNodeAnimator expand =
new RenderNodeAnimator(props.getProgress(), .5f);
- RenderNodeAnimator slideX =
- new RenderNodeAnimator(props.getX(), mWidth / 2);
- RenderNodeAnimator slideY =
- new RenderNodeAnimator(props.getY(), mHeight / 2);
expand.setTarget(canvas);
- slideX.setTarget(canvas);
- slideY.setTarget(canvas);
- startAnimation(expand, slideX, slideY);
+ startAnimation(expand);
}
- private void startAnimation(Animator expand,
- Animator slideX, Animator slideY) {
- expand.setDuration(SLIDE_ANIM_DURATION);
- slideX.setDuration(SLIDE_ANIM_DURATION);
- slideY.setDuration(SLIDE_ANIM_DURATION);
- slideX.addListener(new AnimatorListener(this));
+ private void startAnimation(Animator expand) {
+ expand.setDuration(ENTER_ANIM_DURATION);
+ expand.addListener(new AnimatorListener(this));
expand.setInterpolator(LINEAR_INTERPOLATOR);
- slideX.setInterpolator(PATH_INTERPOLATOR);
- slideY.setInterpolator(PATH_INTERPOLATOR);
expand.start();
- slideX.start();
- slideY.start();
if (!mSparkle.isRunning()) {
mSparkle.start();
- mActiveAnimations.add(mSparkle);
}
- mActiveAnimations.add(expand);
- mActiveAnimations.add(slideX);
- mActiveAnimations.add(slideY);
}
private void enterSoftware() {
ValueAnimator expand = ValueAnimator.ofFloat(0f, 0.5f);
- ValueAnimator slideX = ValueAnimator.ofFloat(
- mProperties.getX(), mWidth / 2);
- ValueAnimator slideY = ValueAnimator.ofFloat(
- mProperties.getY(), mHeight / 2);
expand.addUpdateListener(updatedAnimation -> {
notifyUpdate();
mProperties.getShader().setProgress((Float) expand.getAnimatedValue());
});
- slideX.addUpdateListener(anim -> {
- float x = (float) slideX.getAnimatedValue();
- float y = (float) slideY.getAnimatedValue();
- mProperties.setOrigin(x, y);
- mProperties.getShader().setOrigin(x, y);
- });
- startAnimation(expand, slideX, slideY);
+ startAnimation(expand);
}
@NonNull AnimationProperties<Float, Paint> getProperties() {
diff --git a/graphics/java/android/graphics/drawable/RippleDrawable.java b/graphics/java/android/graphics/drawable/RippleDrawable.java
index d6bbee9..fb2b9b3 100644
--- a/graphics/java/android/graphics/drawable/RippleDrawable.java
+++ b/graphics/java/android/graphics/drawable/RippleDrawable.java
@@ -842,7 +842,7 @@
if (shouldAnimate && mRunningAnimations.size() <= MAX_RIPPLES) {
RippleAnimationSession.AnimationProperties<Float, Paint> properties =
createAnimationProperties(x, y, w, h);
- mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps, w, h)
+ mRunningAnimations.add(new RippleAnimationSession(properties, !useCanvasProps)
.setOnAnimationUpdated(() -> invalidateSelf(false))
.setOnSessionEnd(session -> {
mRunningAnimations.remove(session);
@@ -912,14 +912,14 @@
? mState.mColor.getColorForState(getState(), Color.BLACK)
: mMaskColorFilter.getColor();
shader.setColor(color);
- shader.setOrigin(x, y);
+ shader.setOrigin(w / 2, y / 2);
+ shader.setTouch(x, y);
shader.setResolution(w, h);
- shader.setSecondsOffset(0);
+ shader.setNoisePhase(0);
shader.setRadius(radius);
shader.setProgress(.0f);
properties = new RippleAnimationSession.AnimationProperties<>(
- x, y, radius, p, 0f,
- shader);
+ w / 2, h / 2, radius, p, 0f, shader);
if (mMaskShader == null) {
shader.setShader(null);
} else {
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 657a32c..ea9ba32 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -23,11 +23,12 @@
final class RippleShader extends RuntimeShader {
private static final String SHADER_UNIFORMS = "uniform vec2 in_origin;\n"
+ + "uniform vec2 in_touch;\n"
+ "uniform float in_progress;\n"
+ "uniform float in_maxRadius;\n"
+ "uniform vec2 in_resolution;\n"
+ "uniform float in_hasMask;\n"
- + "uniform float in_secondsOffset;\n"
+ + "uniform float in_noisePhase;\n"
+ "uniform vec4 in_color;\n"
+ "uniform shader in_shader;\n";
private static final String SHADER_LIB =
@@ -48,7 +49,7 @@
+ " float s = 0.0;\n"
+ " for (float i = 0; i < 4; i += 1) {\n"
+ " float l = i * 0.25;\n"
- + " float h = l + 0.025;\n"
+ + " float h = l + 0.005;\n"
+ " float o = abs(sin(0.1 * PI * (t + i)));\n"
+ " s += threshold(n + o, l, h);\n"
+ " }\n"
@@ -79,12 +80,12 @@
+ " float fadeIn = subProgress(0., 0.175, in_progress);\n"
+ " float fadeOutNoise = subProgress(0.375, 1., in_progress);\n"
+ " float fadeOutRipple = subProgress(0.375, 0.75, in_progress);\n"
- + " float ring = getRingMask(p, in_origin, in_maxRadius, fadeIn);\n"
+ + " vec2 center = mix(in_touch, in_origin, fadeIn);\n"
+ + " float ring = getRingMask(p, center, in_maxRadius, fadeIn);\n"
+ " float alpha = min(fadeIn, 1. - fadeOutNoise);\n"
- + " float sparkle = sparkles(p, in_progress * 0.25 + in_secondsOffset)\n"
- + " * ring * alpha;\n"
- + " float fade = min(fadeIn, 1.-fadeOutRipple);\n"
- + " vec4 circle = in_color * (softCircle(p, in_origin, in_maxRadius "
+ + " float sparkle = sparkles(p, in_noisePhase) * ring * alpha;\n"
+ + " float fade = min(fadeIn, 1. - fadeOutRipple);\n"
+ + " vec4 circle = in_color * (softCircle(p, center, in_maxRadius "
+ " * fadeIn, 0.2) * fade);\n"
+ " float mask = in_hasMask == 1. ? sample(in_shader).a > 0. ? 1. : 0. : 1.;\n"
+ " return mix(circle, vec4(sparkle), sparkle) * mask;\n"
@@ -109,14 +110,18 @@
/**
* Continuous offset used as noise phase.
*/
- public void setSecondsOffset(float t) {
- setUniform("in_secondsOffset", t);
+ public void setNoisePhase(float t) {
+ setUniform("in_noisePhase", t);
}
public void setOrigin(float x, float y) {
setUniform("in_origin", new float[] {x, y});
}
+ public void setTouch(float x, float y) {
+ setUniform("in_touch", new float[] {x, y});
+ }
+
public void setProgress(float progress) {
setUniform("in_progress", progress);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 4bb8e9b..9dabec7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -32,7 +32,7 @@
import android.util.SparseArray;
import android.view.Display;
import android.view.DragEvent;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindow;
import android.view.IWindowManager;
import android.view.IWindowSession;
@@ -369,9 +369,9 @@
public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {}
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener) {
try {
- callbacks.onScrollCaptureResponse(
+ listener.onScrollCaptureResponse(
new ScrollCaptureResponse.Builder()
.setDescription("Not Implemented")
.build());
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
index 17c51fb..bca2576 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenDockActivity.kt
@@ -29,8 +29,7 @@
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
@@ -60,6 +59,11 @@
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
+ splitScreenApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@FlakyTest(bugId = 169271943)
@Test
fun dockedStackPrimaryBoundsIsVisible() =
@@ -73,12 +77,8 @@
@FlakyTest(bugId = 178531736)
@Test
// b/178531736
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME,
- WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -91,12 +91,8 @@
@FlakyTest(bugId = 178531736)
@Test
// b/178531736
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME,
- WALLPAPER_TITLE, LIVE_WALLPAPER_PACKAGE_NAME,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
index a94fd46..9000f22 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenLaunchToSide.kt
@@ -30,8 +30,7 @@
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesVisible
import com.android.wm.shell.flicker.dockedStackPrimaryBoundsIsVisible
import com.android.wm.shell.flicker.dockedStackSecondaryBoundsIsVisible
@@ -62,6 +61,11 @@
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ secondaryApp.defaultWindowName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@FlakyTest(bugId = 169271943)
@Test
fun dockedStackPrimaryBoundsIsVisible() =
@@ -83,12 +87,8 @@
@Test
// TODO(b/178447631) Remove Splash Screen from white list when flicker lib
// add a wait for splash screen be gone
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -104,12 +104,8 @@
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, SPLASH_SCREEN_NAME,
- splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
index 238059b..219da27 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterSplitScreenNonResizableNotDock.kt
@@ -22,12 +22,10 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.WALLPAPER_TITLE
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.canSplitScreen
import com.android.server.wm.flicker.helpers.openQuickstep
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.Assert
@@ -66,28 +64,24 @@
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME,
+ nonResizeableApp.defaultWindowName,
+ splitScreenApp.defaultWindowName)
+
@Test
fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME,
- SPLASH_SCREEN_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(WALLPAPER_TITLE,
- LAUNCHER_PACKAGE_NAME,
- SPLASH_SCREEN_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
@Test
fun appWindowIsVisible() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
index acd570a..9717709 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenFromBottom.kt
@@ -30,8 +30,7 @@
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,17 +69,20 @@
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@Presubmit
@Test
fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(DOCKED_STACK_DIVIDER)
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
@Presubmit
@Test
@@ -97,11 +99,8 @@
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
index cef1886..3f714bb 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitPrimarySplitScreenShowSecondaryFullscreen.kt
@@ -30,8 +30,7 @@
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerIsInvisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -70,6 +69,11 @@
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ splitScreenApp.defaultWindowName, secondaryApp.defaultWindowName,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@FlakyTest(bugId = 175687842)
@Test
fun dockedStackDividerIsInvisible() = testSpec.dockedStackDividerIsInvisible()
@@ -80,11 +84,8 @@
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -101,11 +102,8 @@
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
- secondaryApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
index 1e89a25..08d5db0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenRotateTransition.kt
@@ -17,11 +17,13 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.view.Surface
+import androidx.test.filters.FlakyTest
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.helpers.openQuickStepAndClearRecentAppsFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
+import org.junit.Test
abstract class LegacySplitScreenRotateTransition(
testSpec: FlickerTestParameter
@@ -44,4 +46,16 @@
}
}
}
+
+ @FlakyTest
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ @FlakyTest
+ @Test
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
index 7f69a66..72d6f56 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenToLauncher.kt
@@ -34,14 +34,13 @@
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.dockedStackDividerBecomesInvisible
import com.android.wm.shell.flicker.helpers.SimpleAppHelper
import org.junit.FixMethodOrder
@@ -89,6 +88,10 @@
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(launcherPackageName, WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@Presubmit
@Test
fun navBarWindowIsAlwaysVisible() = testSpec.navBarWindowIsAlwaysVisible()
@@ -99,11 +102,6 @@
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
-
- @Presubmit
- @Test
fun navBarLayerIsAlwaysVisible() = testSpec.navBarLayerIsAlwaysVisible()
@Presubmit
@@ -122,8 +120,8 @@
@Presubmit
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(launcherPackageName))
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
index 91ea871..319fde1 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/LegacySplitScreenTransition.kt
@@ -17,6 +17,7 @@
package com.android.wm.shell.flicker.legacysplitscreen
import android.app.Instrumentation
+import android.platform.test.annotations.Presubmit
import android.support.test.launcherhelper.LauncherStrategyFactory
import android.view.Surface
import androidx.test.platform.app.InstrumentationRegistry
@@ -29,7 +30,9 @@
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
+import org.junit.Test
abstract class LegacySplitScreenTransition(protected val testSpec: FlickerTestParameter) {
protected val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
@@ -40,6 +43,15 @@
protected val LAUNCHER_PACKAGE_NAME = LauncherStrategyFactory.getInstance(instrumentation)
.launcherStrategy.supportedLauncherPackage
+ /**
+ * List of windows that are ignored when verifying that visible elements appear on 2
+ * consecutive entries in the trace.
+ *
+ * b/182720234
+ */
+ open val ignoredWindows: List<String> = listOf(WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
protected open val transition: FlickerBuilder.(Map<String, Any?>) -> Unit
get() = { configuration ->
setup {
@@ -88,11 +100,26 @@
}
}
+ @Presubmit
+ @Test
+ open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoredWindows)
+ }
+ }
+
+ @Presubmit
+ @Test
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoredWindows)
+ }
+ }
+
companion object {
internal const val LIVE_WALLPAPER_PACKAGE_NAME =
"com.breel.wallpapers18.soundviz.wallpaper.variations.SoundVizWallpaperV2"
internal const val LETTERBOX_NAME = "Letterbox"
internal const val TOAST_NAME = "Toast"
- internal const val SPLASH_SCREEN_NAME = "Splash Screen"
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
index caafa27..8a1715e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreen.kt
@@ -30,8 +30,7 @@
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -67,19 +66,20 @@
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME, TOAST_NAME,
+ splitScreenApp.defaultWindowName, nonResizeableApp.defaultWindowName,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@Presubmit
@Test
fun layerBecomesInvisible() = testSpec.layerBecomesInvisible(splitScreenApp.defaultWindowName)
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -97,13 +97,8 @@
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME, TOAST_NAME,
- splitScreenApp.defaultWindowName,
- nonResizeableApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
index 543484a..4c6705f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreen.kt
@@ -29,8 +29,7 @@
import com.android.server.wm.flicker.helpers.launchSplitScreen
import com.android.server.wm.flicker.layerBecomesInvisible
import com.android.server.wm.flicker.layerBecomesVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -67,6 +66,12 @@
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(DOCKED_STACK_DIVIDER, LAUNCHER_PACKAGE_NAME, LETTERBOX_NAME,
+ nonResizeableApp.defaultWindowName, splitScreenApp.defaultWindowName,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@Presubmit
@Test
fun layerBecomesVisible() = testSpec.layerBecomesVisible(nonResizeableApp.defaultWindowName)
@@ -77,14 +82,8 @@
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER,
- LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -98,14 +97,8 @@
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(DOCKED_STACK_DIVIDER,
- LAUNCHER_PACKAGE_NAME,
- LETTERBOX_NAME,
- nonResizeableApp.defaultWindowName,
- splitScreenApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
index d228337..8f15e50 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/OpenAppToLegacySplitScreen.kt
@@ -31,8 +31,7 @@
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import com.android.wm.shell.flicker.appPairsDividerBecomesVisible
import com.android.wm.shell.flicker.helpers.SplitScreenHelper
import org.junit.FixMethodOrder
@@ -61,12 +60,15 @@
}
}
+ override val ignoredWindows: List<String>
+ get() = listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME)
+
@FlakyTest(bugId = 178447631)
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName)
- )
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
@Presubmit
@Test
@@ -90,10 +92,8 @@
@FlakyTest(bugId = 178447631)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(
- listOf(LAUNCHER_PACKAGE_NAME, splitScreenApp.defaultWindowName)
- )
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@FlakyTest(bugId = 151179149)
@Test
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
index f5174bc..f40a08c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ResizeLegacySplitScreen.kt
@@ -40,8 +40,6 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.traces.layers.getVisibleBounds
import com.android.wm.shell.flicker.DOCKED_STACK_DIVIDER
@@ -107,8 +105,11 @@
fun statusBarWindowIsAlwaysVisible() = testSpec.statusBarWindowIsAlwaysVisible()
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ override fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ }
@FlakyTest(bugId = 156223549)
@Test
@@ -144,8 +145,8 @@
testSpec.statusBarLayerRotatesScales(testSpec.config.endRotation)
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
@Test
fun topAppLayerIsAlwaysVisible() {
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl
new file mode 100644
index 0000000..ceef73c
--- /dev/null
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionAttributionTagCallback.aidl
@@ -0,0 +1,10 @@
+package android.media.musicrecognition;
+
+/**
+ * Interface from {@link MusicRecognitionService} to system to pass attribution tag.
+ *
+ * @hide
+ */
+oneway interface IMusicRecognitionAttributionTagCallback {
+ void onAttributionTag(in String attributionTag);
+}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
index 26543ed..c970161 100644
--- a/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionService.aidl
@@ -4,6 +4,7 @@
import android.os.ParcelFileDescriptor;
import android.os.IBinder;
import android.media.musicrecognition.IMusicRecognitionServiceCallback;
+import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback;
/**
* Interface from the system to a {@link MusicRecognitionService}.
@@ -15,4 +16,6 @@
in ParcelFileDescriptor fd,
in AudioFormat audioFormat,
in IMusicRecognitionServiceCallback callback);
+
+ void getAttributionTag(in IMusicRecognitionAttributionTagCallback callback);
}
\ No newline at end of file
diff --git a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
index 15215c4..10a6554 100644
--- a/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
+++ b/media/java/android/media/musicrecognition/IMusicRecognitionServiceCallback.aidl
@@ -4,7 +4,7 @@
import android.media.MediaMetadata;
/**
- * Interface from a {@MusicRecognitionService} the system.
+ * Interface from a {@MusicRecognitionService} to the system.
*
* @hide
*/
diff --git a/media/java/android/media/musicrecognition/MusicRecognitionService.java b/media/java/android/media/musicrecognition/MusicRecognitionService.java
index 04b4c39b..385aff0 100644
--- a/media/java/android/media/musicrecognition/MusicRecognitionService.java
+++ b/media/java/android/media/musicrecognition/MusicRecognitionService.java
@@ -90,7 +90,7 @@
try {
callback.onRecognitionSucceeded(result, extras);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
@@ -99,11 +99,18 @@
try {
callback.onRecognitionFailed(failureCode);
} catch (RemoteException e) {
- e.rethrowFromSystemServer();
+ throw e.rethrowFromSystemServer();
}
}
}));
}
+
+ @Override
+ public void getAttributionTag(
+ IMusicRecognitionAttributionTagCallback callback) throws RemoteException {
+ String tag = MusicRecognitionService.this.getAttributionTag();
+ callback.onAttributionTag(tag);
+ }
};
@Override
diff --git a/media/jni/soundpool/StreamManager.cpp b/media/jni/soundpool/StreamManager.cpp
index 8b84bf3..49c2b39 100644
--- a/media/jni/soundpool/StreamManager.cpp
+++ b/media/jni/soundpool/StreamManager.cpp
@@ -43,6 +43,14 @@
// Amount of time for a StreamManager thread to wait before closing.
static constexpr int64_t kWaitTimeBeforeCloseNs = 9 * NANOS_PER_SECOND;
+// Debug flag:
+// kForceLockStreamManagerStop is set to true to force lock the StreamManager
+// worker thread during stop. This limits concurrency of Stream processing.
+// Normally we lock the StreamManager worker thread during stop ONLY
+// for SoundPools configured with a single Stream.
+//
+static constexpr bool kForceLockStreamManagerStop = false;
+
////////////
StreamMap::StreamMap(int32_t streams) {
@@ -103,6 +111,7 @@
: StreamMap(streams)
, mAttributes(*attributes)
, mOpPackageName(std::move(opPackageName))
+ , mLockStreamManagerStop(streams == 1 || kForceLockStreamManagerStop)
{
ALOGV("%s(%d, %zu, ...)", __func__, streams, threads);
forEach([this](Stream *stream) {
@@ -113,7 +122,8 @@
});
mThreadPool = std::make_unique<ThreadPool>(
- std::min(threads, (size_t)std::thread::hardware_concurrency()),
+ std::min((size_t)streams, // do not make more threads than streams to play
+ std::min(threads, (size_t)std::thread::hardware_concurrency())),
"SoundPool_");
}
@@ -375,12 +385,12 @@
}
mRestartStreams.erase(it);
mProcessingStreams.emplace(stream);
- lock.unlock();
+ if (!mLockStreamManagerStop) lock.unlock();
stream->stop();
ALOGV("%s(%d) stopping streamID:%d", __func__, id, stream->getStreamID());
if (Stream* nextStream = stream->playPairStream()) {
ALOGV("%s(%d) starting streamID:%d", __func__, id, nextStream->getStreamID());
- lock.lock();
+ if (!mLockStreamManagerStop) lock.lock();
if (nextStream->getStopTimeNs() > 0) {
// the next stream was stopped before we can move it to the active queue.
ALOGV("%s(%d) stopping started streamID:%d",
@@ -390,7 +400,7 @@
addToActiveQueue_l(nextStream);
}
} else {
- lock.lock();
+ if (!mLockStreamManagerStop) lock.lock();
mAvailableStreams.insert(stream);
}
mProcessingStreams.erase(stream);
diff --git a/media/jni/soundpool/StreamManager.h b/media/jni/soundpool/StreamManager.h
index 81ac69e..85b468c 100644
--- a/media/jni/soundpool/StreamManager.h
+++ b/media/jni/soundpool/StreamManager.h
@@ -437,6 +437,14 @@
void sanityCheckQueue_l() const REQUIRES(mStreamManagerLock);
const audio_attributes_t mAttributes;
+ const std::string mOpPackageName;
+
+ // For legacy compatibility, we lock the stream manager on stop when
+ // there is only one stream. This allows a play to be called immediately
+ // after stopping, otherwise it is possible that the play might be discarded
+ // (returns 0) because that stream may be in the worker thread call to stop.
+ const bool mLockStreamManagerStop;
+
std::unique_ptr<ThreadPool> mThreadPool; // locked internally
// mStreamManagerLock is used to lock access for transitions between the
@@ -477,8 +485,6 @@
// The paired stream may be active or restarting.
// No particular order.
std::unordered_set<Stream*> mProcessingStreams GUARDED_BY(mStreamManagerLock);
-
- const std::string mOpPackageName;
};
} // namespace android::soundpool
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index 4dd012a..44748e9 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -29,7 +29,7 @@
<string name="profile_name_watch">watch</string>
<!-- Title of the device association confirmation dialog. -->
- <string name="confirmation_title">Set <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong></string>
+ <string name="confirmation_title">Allow <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to manage your <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong></string>
<!-- Text of the device profile permissions explanation in the association dialog. -->
<string name="profile_summary">This app is needed to manage your <xliff:g id="profile_name" example="watch">%1$s</xliff:g>. <xliff:g id="privileges_discplaimer" example="Android Wear will get access to your Notifications, Calendar and Contacts.">%2$s</xliff:g></string>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index f1a98ad..91a6749 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -128,9 +128,11 @@
if (useDeviceProfile) {
profileSummary.setVisibility(View.VISIBLE);
+ String deviceRef = getRequest().isSingleDevice()
+ ? getService().mDevicesFound.get(0).getDisplayName()
+ : profileName;
profileSummary.setText(getString(R.string.profile_summary,
- getCallingAppName(),
- profileName,
+ deviceRef,
profilePrivacyDisclaimer));
} else {
profileSummary.setVisibility(View.GONE);
diff --git a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
index b969a15..4798b43 100644
--- a/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_footer_dialog.xml
@@ -38,7 +38,6 @@
android:id="@+id/device_management_subtitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:text="@string/monitoring_title_device_owned"
style="@style/DeviceManagementDialogTitle"
android:paddingBottom="@dimen/qs_footer_dialog_subtitle_padding"
/>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index db79e96..57c932e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1257,6 +1257,9 @@
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the organization can monitor network traffic on that device. The placeholder is the organization's name. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_named_management_monitoring"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> owns this device and may monitor network traffic</string>
+ <!-- Disclosure at the bottom of Quick Settings that indicates that the user's financed device belongs to the Creditor. The placeholder is the Creditor's name. [CHAR LIMIT=100] -->
+ <string name="quick_settings_financed_disclosure_named_management">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
+
<!-- Disclosure at the bottom of Quick Settings that indicates that the user's device belongs to their organization, and the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_management_named_vpn">This device belongs to your organization and is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
@@ -1296,6 +1299,9 @@
<!-- Disclosure at the bottom of Quick Settings that indicates that the device is connected to a VPN. The placeholder is the VPN name. [CHAR LIMIT=100] -->
<string name="quick_settings_disclosure_named_vpn">This device is connected to <xliff:g id="vpn_app" example="Foo VPN App">%1$s</xliff:g></string>
+ <!-- Monitoring dialog title for financed device [CHAR LIMIT=60] -->
+ <string name="monitoring_title_financed_device">This device is provided by <xliff:g id="organization_name" example="Foo, Inc.">%s</xliff:g></string>
+
<!-- Monitoring dialog title for device owned devices [CHAR LIMIT=35] -->
<string name="monitoring_title_device_owned">Device management</string>
@@ -1330,6 +1336,9 @@
<!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_named_management">This device belongs to <xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g>.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string>
+ <!-- Dialog that a user can access via Quick Settings. The dialog describes what a Creditor can monitor (and the changes they can make) on the user's financed device. [CHAR LIMIT=NONE]-->
+ <string name="monitoring_financed_description_named_management"><xliff:g id="organization_name" example="Foo, Inc.">%1$s</xliff:g> may be able to access data associated with this device and change this device\’s settings.\n\nIf you have questions, contact <xliff:g id="organization_name" example="Foo, Inc.">%2$s</xliff:g>.</string>
+
<!-- Dialog that a user can access via Quick Settings. The dialog describes what the IT admin can monitor (and the changes they can make) on the user's device. [CHAR LIMIT=NONE]-->
<string name="monitoring_description_management">This device belongs to your organization.\n\nYour IT admin can monitor and manage settings, corporate access, apps, data associated with your device, and your device\'s location information.\n\nFor more information, contact your IT admin.</string>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
index 2036150..cef0ce1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java
@@ -71,8 +71,12 @@
return false;
}
- private void updateAlpha() {
- getDrawable().setAlpha(mPauseAuth ? mAlpha : 255);
+ protected void updateAlpha() {
+ getDrawable().setAlpha(calculateAlpha());
+ }
+
+ protected final int calculateAlpha() {
+ return mPauseAuth ? mAlpha : 255;
}
private int expansionToAlpha(float expansion) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
index 727a40d..cd5abd7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollDrawable.java
@@ -128,6 +128,7 @@
mSensorOutlinePaint.setAlpha(alpha);
mBlueFill.setAlpha(alpha);
mBlueStroke.setAlpha(alpha);
+ mMovingTargetFpIcon.setAlpha(alpha);
invalidateSelf();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
index 7985d95..75e8638 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollView.java
@@ -20,6 +20,7 @@
import android.util.AttributeSet;
import android.widget.ImageView;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.systemui.R;
@@ -28,8 +29,9 @@
* View corresponding with udfps_enroll_view.xml
*/
public class UdfpsEnrollView extends UdfpsAnimationView {
- private final UdfpsEnrollDrawable mFingerprintDrawable;
- private ImageView mFingerprintView;
+ @NonNull private final UdfpsEnrollDrawable mFingerprintDrawable;
+ @NonNull private ImageView mFingerprintView;
+ @NonNull private UdfpsProgressBar mProgressBar;
public UdfpsEnrollView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
@@ -37,9 +39,17 @@
}
@Override
+ protected void updateAlpha() {
+ super.updateAlpha();
+ mProgressBar.setAlpha(calculateAlpha());
+ mProgressBar.getProgressDrawable().setAlpha(calculateAlpha());
+ }
+
+ @Override
protected void onFinishInflate() {
mFingerprintView = findViewById(R.id.udfps_enroll_animation_fp_view);
mFingerprintView.setImageDrawable(mFingerprintDrawable);
+ mProgressBar = findViewById(R.id.progress_bar);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
index 1411fa1..5e13fd3 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSSecurityFooter.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.qs;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_SECURITY_FOOTER_VIEW;
import android.app.AlertDialog;
@@ -244,8 +246,14 @@
if (organizationName == null) {
return mContext.getString(R.string.quick_settings_disclosure_management);
}
- return mContext.getString(R.string.quick_settings_disclosure_named_management,
- organizationName);
+ if (isFinancedDevice()) {
+ return mContext.getString(
+ R.string.quick_settings_financed_disclosure_named_management,
+ organizationName);
+ } else {
+ return mContext.getString(R.string.quick_settings_disclosure_named_management,
+ organizationName);
+ }
} // end if(isDeviceManaged)
if (hasCACertsInWorkProfile) {
if (workProfileOrganizationName == null) {
@@ -355,6 +363,10 @@
.inflate(R.layout.quick_settings_footer_dialog, null, false);
// device management section
+ TextView deviceManagementSubtitle =
+ dialogView.findViewById(R.id.device_management_subtitle);
+ deviceManagementSubtitle.setText(getManagementTitle(deviceOwnerOrganization));
+
CharSequence managementMessage = getManagementMessage(isDeviceManaged,
deviceOwnerOrganization, isProfileOwnerOfOrganizationOwnedDevice,
workProfileOrganizationName);
@@ -468,7 +480,8 @@
}
}
- private String getSettingsButton() {
+ @VisibleForTesting
+ String getSettingsButton() {
return mContext.getString(R.string.monitoring_button_view_policies);
}
@@ -490,8 +503,13 @@
return null;
}
if (isDeviceManaged && organizationName != null) {
- return mContext.getString(
- R.string.monitoring_description_named_management, organizationName);
+ if (isFinancedDevice()) {
+ return mContext.getString(R.string.monitoring_financed_description_named_management,
+ organizationName, organizationName);
+ } else {
+ return mContext.getString(
+ R.string.monitoring_description_named_management, organizationName);
+ }
} else if (isProfileOwnerOfOrganizationOwnedDevice && workProfileOrganizationName != null) {
return mContext.getString(
R.string.monitoring_description_named_management, workProfileOrganizationName);
@@ -557,14 +575,23 @@
return message;
}
- private int getTitle(String deviceOwner) {
- if (deviceOwner != null) {
- return R.string.monitoring_title_device_owned;
+ @VisibleForTesting
+ CharSequence getManagementTitle(CharSequence deviceOwnerOrganization) {
+ if (deviceOwnerOrganization != null && isFinancedDevice()) {
+ return mContext.getString(R.string.monitoring_title_financed_device,
+ deviceOwnerOrganization);
} else {
- return R.string.monitoring_title;
+ return mContext.getString(R.string.monitoring_title_device_owned);
}
}
+ private boolean isFinancedDevice() {
+ return mSecurityController.isDeviceManaged()
+ && mSecurityController.getDeviceOwnerType(
+ mSecurityController.getDeviceOwnerComponentOnAnyUser())
+ == DEVICE_OWNER_TYPE_FINANCED;
+ }
+
private final Runnable mUpdateIcon = new Runnable() {
@Override
public void run() {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
index bb8c367..1ec175c 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/CropView.java
@@ -26,6 +26,8 @@
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.IntArray;
import android.util.Log;
@@ -94,6 +96,25 @@
}
@Override
+ protected Parcelable onSaveInstanceState() {
+ Parcelable superState = super.onSaveInstanceState();
+
+ SavedState ss = new SavedState(superState);
+ ss.mTopBoundary = getTopBoundary();
+ ss.mBottomBoundary = getBottomBoundary();
+ return ss;
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state) {
+ SavedState ss = (SavedState) state;
+ super.onRestoreInstanceState(ss.getSuperState());
+
+ setBoundaryTo(CropBoundary.TOP, ss.mTopBoundary);
+ setBoundaryTo(CropBoundary.BOTTOM, ss.mBottomBoundary);
+ }
+
+ @Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
float top = mTopCrop + mTopDelta;
@@ -380,6 +401,44 @@
*/
void onCropMotionEvent(MotionEvent event, CropBoundary boundary, float boundaryPosition,
int boundaryPositionPx);
+ }
+ static class SavedState extends BaseSavedState {
+ float mTopBoundary;
+ float mBottomBoundary;
+
+ /**
+ * Constructor called from {@link CropView#onSaveInstanceState()}
+ */
+ SavedState(Parcelable superState) {
+ super(superState);
+ }
+
+ /**
+ * Constructor called from {@link #CREATOR}
+ */
+ private SavedState(Parcel in) {
+ super(in);
+ mTopBoundary = in.readFloat();
+ mBottomBoundary = in.readFloat();
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ super.writeToParcel(out, flags);
+ out.writeFloat(mTopBoundary);
+ out.writeFloat(mBottomBoundary);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
index bc8adc9..4aead817f 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageExporter.java
@@ -213,6 +213,20 @@
CompressFormat format;
boolean published;
boolean deleted;
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("Result{");
+ sb.append("uri=").append(uri);
+ sb.append(", requestId=").append(requestId);
+ sb.append(", fileName='").append(fileName).append('\'');
+ sb.append(", timestamp=").append(timestamp);
+ sb.append(", format=").append(format);
+ sb.append(", published=").append(published);
+ sb.append(", deleted=").append(deleted);
+ sb.append('}');
+ return sb.toString();
+ }
}
private static class Task {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
index 988b93c..7ee7c31 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageLoader.java
@@ -21,7 +21,6 @@
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
-import android.os.ParcelFileDescriptor;
import androidx.concurrent.futures.CallbackToFutureAdapter;
@@ -43,6 +42,16 @@
@Nullable Uri uri;
@Nullable File fileName;
@Nullable Bitmap bitmap;
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder("Result{");
+ sb.append("uri=").append(uri);
+ sb.append(", fileName=").append(fileName);
+ sb.append(", bitmap=").append(bitmap);
+ sb.append('}');
+ return sb.toString();
+ }
}
@Inject
@@ -54,7 +63,7 @@
* Loads an image via URI from ContentResolver.
*
* @param uri the identifier of the image to load
- * @return a listenable future result
+ * @return a listenable future result containing a bitmap
*/
ListenableFuture<Result> load(Uri uri) {
return CallbackToFutureAdapter.getFuture(completer -> {
@@ -76,7 +85,7 @@
* permissions to read this file/path.
*
* @param file the system file path of the image to load
- * @return a listenable future result
+ * @return a listenable future result containing a bitmap
*/
ListenableFuture<Result> load(File file) {
return CallbackToFutureAdapter.getFuture(completer -> {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
index 6743afa..07adc7b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ImageTileSet.java
@@ -31,6 +31,7 @@
import com.android.internal.util.CallbackRegistry.NotifierCallback;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
/**
@@ -69,9 +70,6 @@
private final Rect mBounds = new Rect();
private final Handler mHandler;
- private OnContentChangedListener mOnContentChangedListener;
- private OnBoundsChangedListener mOnBoundsChangedListener;
-
void addOnBoundsChangedListener(OnBoundsChangedListener listener) {
if (mOnBoundsListeners == null) {
mOnBoundsListeners = new CallbackRegistry<>(
@@ -204,18 +202,17 @@
return mBounds.height();
}
- @AnyThread
void clear() {
- if (!mHandler.getLooper().isCurrentThread()) {
- mHandler.post(this::clear);
- return;
- }
if (mTiles.isEmpty()) {
return;
}
mBounds.setEmpty();
- mTiles.forEach(ImageTile::close);
- mTiles.clear();
+ Iterator<ImageTile> i = mTiles.iterator();
+ while (i.hasNext()) {
+ ImageTile next = i.next();
+ next.close();
+ i.remove();
+ }
notifyBoundsChanged(mBounds);
notifyContentChanged();
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
index db99705..3ac884b 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/LongScreenshotActivity.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.graphics.HardwareRenderer;
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
@@ -30,10 +29,13 @@
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
-import android.os.SystemClock;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
+import android.view.IScrollCaptureConnection;
+import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
import android.view.View;
import android.widget.ImageView;
@@ -41,14 +43,14 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.screenshot.ScrollCaptureController.LongScreenshot;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
import java.time.ZonedDateTime;
import java.util.UUID;
+import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
@@ -61,18 +63,15 @@
public class LongScreenshotActivity extends Activity {
private static final String TAG = "LongScreenshotActivity";
- private static final String IMAGE_PATH_KEY = "saved-image";
- private static final String TOP_BOUNDARY_KEY = "top-boundary";
- private static final String BOTTOM_BOUNDARY_KEY = "bottom-boundary";
+ public static final String EXTRA_CAPTURE_RESPONSE = "capture-response";
+ private static final String KEY_SAVED_IMAGE_PATH = "saved-image-path";
private final UiEventLogger mUiEventLogger;
private final ScrollCaptureController mScrollCaptureController;
- private final ScrollCaptureClient.Connection mConnection;
private final Executor mUiExecutor;
private final Executor mBackgroundExecutor;
private final ImageExporter mImageExporter;
- private String mSavedImagePath;
// If true, the activity is re-loading an image from storage, which should either succeed and
// populate the UI or fail and finish the activity.
private boolean mRestoringInstance;
@@ -84,6 +83,14 @@
private View mShare;
private CropView mCropView;
private MagnifierView mMagnifierView;
+ private ScrollCaptureResponse mScrollCaptureResponse;
+ private File mSavedImagePath;
+
+ private ListenableFuture<File> mCacheSaveFuture;
+ private ListenableFuture<ImageLoader.Result> mCacheLoadFuture;
+
+ private ListenableFuture<LongScreenshot> mLongScreenshotFuture;
+ private LongScreenshot mLongScreenshot;
private enum PendingAction {
SHARE,
@@ -92,35 +99,31 @@
}
@Inject
- public LongScreenshotActivity(UiEventLogger uiEventLogger,
- ImageExporter imageExporter,
- @Main Executor mainExecutor,
- @Background Executor bgExecutor,
+ public LongScreenshotActivity(UiEventLogger uiEventLogger, ImageExporter imageExporter,
+ @Main Executor mainExecutor, @Background Executor bgExecutor, IWindowManager wms,
Context context) {
mUiEventLogger = uiEventLogger;
mUiExecutor = mainExecutor;
mBackgroundExecutor = bgExecutor;
mImageExporter = imageExporter;
-
- mScrollCaptureController = new ScrollCaptureController(context, mainExecutor, bgExecutor,
- imageExporter);
-
- mConnection = ScreenshotController.takeScrollCaptureConnection();
+ mScrollCaptureController = new ScrollCaptureController(context, bgExecutor, wms);
}
+
@Override
public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ Log.d(TAG, "onCreate(savedInstanceState = " + savedInstanceState + ")");
+ super.onCreate(savedInstanceState);
setContentView(R.layout.long_screenshot);
- mPreview = findViewById(R.id.preview);
- mSave = findViewById(R.id.save);
- mCancel = findViewById(R.id.cancel);
- mEdit = findViewById(R.id.edit);
- mShare = findViewById(R.id.share);
- mCropView = findViewById(R.id.crop_view);
- mMagnifierView = findViewById(R.id.magnifier);
+ mPreview = requireViewById(R.id.preview);
+ mSave = requireViewById(R.id.save);
+ mCancel = requireViewById(R.id.cancel);
+ mEdit = requireViewById(R.id.edit);
+ mShare = requireViewById(R.id.share);
+ mCropView = requireViewById(R.id.crop_view);
+ mMagnifierView = requireViewById(R.id.magnifier);
mCropView.setCropInteractionListener(mMagnifierView);
mSave.setOnClickListener(this::onClicked);
@@ -128,69 +131,186 @@
mEdit.setOnClickListener(this::onClicked);
mShare.setOnClickListener(this::onClicked);
- if (savedInstanceState != null) {
- String imagePath = savedInstanceState.getString(IMAGE_PATH_KEY);
- if (!TextUtils.isEmpty(imagePath)) {
- mRestoringInstance = true;
- mBackgroundExecutor.execute(() -> {
- Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
- if (bitmap == null) {
- Log.e(TAG, "Failed to read bitmap from " + imagePath);
- finishAndRemoveTask();
- } else {
- runOnUiThread(() -> {
- BitmapDrawable drawable = new BitmapDrawable(getResources(), bitmap);
- mPreview.setImageDrawable(drawable);
- mMagnifierView.setDrawable(drawable, bitmap.getWidth(),
- bitmap.getHeight());
-
- mCropView.setBoundaryTo(CropView.CropBoundary.TOP,
- savedInstanceState.getFloat(TOP_BOUNDARY_KEY, 0f));
- mCropView.setBoundaryTo(CropView.CropBoundary.BOTTOM,
- savedInstanceState.getFloat(BOTTOM_BOUNDARY_KEY, 1f));
- mRestoringInstance = false;
- // Reuse the same path for subsequent restoration.
- mSavedImagePath = imagePath;
- Log.d(TAG, "Loaded bitmap from " + imagePath);
- });
- }
- });
- }
- }
mPreview.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
updateCropLocation());
+
+ Intent intent = getIntent();
+ mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE);
+
+ if (savedInstanceState != null) {
+ String savedImagePath = savedInstanceState.getString(KEY_SAVED_IMAGE_PATH);
+ if (savedImagePath == null) {
+ Log.e(TAG, "Missing saved state entry with key '" + KEY_SAVED_IMAGE_PATH + "'!");
+ finishAndRemoveTask();
+ return;
+ }
+ mSavedImagePath = new File(savedImagePath);
+ ImageLoader imageLoader = new ImageLoader(getContentResolver());
+ mCacheLoadFuture = imageLoader.load(mSavedImagePath);
+ }
}
@Override
public void onStart() {
+ Log.d(TAG, "onStart");
super.onStart();
- if (mPreview.getDrawable() == null && !mRestoringInstance) {
- if (mConnection == null) {
- Log.e(TAG, "Failed to get scroll capture connection, bailing out");
+
+ if (mCacheLoadFuture != null) {
+ Log.d(TAG, "mRestoringInstance = true");
+ final ListenableFuture<ImageLoader.Result> future = mCacheLoadFuture;
+ mCacheLoadFuture.addListener(() -> {
+ Log.d(TAG, "cached bitmap load complete");
+ try {
+ onCachedImageLoaded(future.get());
+ } catch (CancellationException | ExecutionException | InterruptedException e) {
+ Log.e(TAG, "Failed to load cached image", e);
+ if (mSavedImagePath != null) {
+ //noinspection ResultOfMethodCallIgnored
+ mSavedImagePath.delete();
+ mSavedImagePath = null;
+ }
+ finishAndRemoveTask();
+ }
+ }, mUiExecutor);
+ mCacheLoadFuture = null;
+ return;
+ }
+
+ if (mLongScreenshotFuture == null) {
+ Log.d(TAG, "mLongScreenshotFuture == null");
+ // First run through, ensure we have a connection to use (see #onCreate)
+ if (mScrollCaptureResponse == null || !mScrollCaptureResponse.isConnected()) {
+ Log.e(TAG, "Did not receive a live scroll capture connection, bailing out!");
finishAndRemoveTask();
return;
}
- doCapture();
+ mLongScreenshotFuture = mScrollCaptureController.run(mScrollCaptureResponse);
+ mLongScreenshotFuture.addListener(() -> {
+ LongScreenshot longScreenshot;
+ try {
+ longScreenshot = mLongScreenshotFuture.get();
+ } catch (CancellationException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Error capturing long screenshot!", e);
+ finishAndRemoveTask();
+ return;
+ }
+ if (longScreenshot.getHeight() == 0) {
+ Log.e(TAG, "Got a zero height result");
+ finishAndRemoveTask();
+ return;
+ }
+ onCaptureCompleted(longScreenshot);
+ }, mUiExecutor);
+ } else {
+ Log.d(TAG, "mLongScreenshotFuture != null");
}
}
+ private void onCaptureCompleted(LongScreenshot longScreenshot) {
+ Log.d(TAG, "onCaptureCompleted(longScreenshot=" + longScreenshot + ")");
+ mLongScreenshot = longScreenshot;
+ mPreview.setImageDrawable(mLongScreenshot.getDrawable());
+ updateCropLocation();
+ mMagnifierView.setDrawable(mLongScreenshot.getDrawable(),
+ mLongScreenshot.getWidth(), mLongScreenshot.getHeight());
+ // Original boundaries go from the image tile set's y=0 to y=pageSize, so
+ // we animate to that as a starting crop position.
+ float topFraction = Math.max(0,
+ -mLongScreenshot.getTop() / (float) mLongScreenshot.getHeight());
+ float bottomFraction = Math.min(1f,
+ 1 - (mLongScreenshot.getBottom() - mLongScreenshot.getPageHeight())
+ / (float) mLongScreenshot.getHeight());
+ mCropView.animateBoundaryTo(CropView.CropBoundary.TOP, topFraction);
+ mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, bottomFraction);
+ setButtonsEnabled(true);
+
+ // Immediately export to temp image file for saved state
+ mCacheSaveFuture = mImageExporter.exportAsTempFile(mBackgroundExecutor,
+ mLongScreenshot.toBitmap());
+ mCacheSaveFuture.addListener(() -> {
+ try {
+ // Get the temp file path to persist, used in onSavedInstanceState
+ mSavedImagePath = mCacheSaveFuture.get();
+ } catch (CancellationException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "Error saving temp image file", e);
+ finishAndRemoveTask();
+ }
+ }, mUiExecutor);
+ }
+
+ private void onCachedImageLoaded(ImageLoader.Result imageResult) {
+ Log.d(TAG, "onCachedImageLoaded(imageResult=" + imageResult + ")");
+ BitmapDrawable drawable = new BitmapDrawable(getResources(), imageResult.bitmap);
+ mPreview.setImageDrawable(drawable);
+ mMagnifierView.setDrawable(drawable, imageResult.bitmap.getWidth(),
+ imageResult.bitmap.getHeight());
+ mSavedImagePath = imageResult.fileName;
+
+ setButtonsEnabled(true);
+ }
+
+ private static Bitmap renderBitmap(Drawable drawable, Rect bounds) {
+ final RenderNode output = new RenderNode("Bitmap Export");
+ output.setPosition(0, 0, bounds.width(), bounds.height());
+ RecordingCanvas canvas = output.beginRecording();
+ canvas.translate(-bounds.left, -bounds.top);
+ canvas.clipRect(bounds);
+ drawable.draw(canvas);
+ output.endRecording();
+ return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
+ }
+
@Override
protected void onSaveInstanceState(Bundle outState) {
+ Log.d(TAG, "onSaveInstanceState");
super.onSaveInstanceState(outState);
- outState.putString(IMAGE_PATH_KEY, mSavedImagePath);
- outState.putFloat(TOP_BOUNDARY_KEY, mCropView.getTopBoundary());
- outState.putFloat(BOTTOM_BOUNDARY_KEY, mCropView.getBottomBoundary());
+ if (mSavedImagePath != null) {
+ outState.putString(KEY_SAVED_IMAGE_PATH, mSavedImagePath.getPath());
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ Log.d(TAG, "onPause finishing=" + isFinishing());
+ super.onPause();
+ if (isFinishing()) {
+ if (mScrollCaptureResponse != null) {
+ mScrollCaptureResponse.close();
+ }
+ cleanupCache();
+
+ if (mLongScreenshotFuture != null) {
+ mLongScreenshotFuture.cancel(true);
+ }
+ if (mLongScreenshot != null) {
+ mLongScreenshot.release();
+ }
+ }
+ }
+
+ void cleanupCache() {
+ if (mCacheSaveFuture != null) {
+ mCacheSaveFuture.cancel(true);
+ }
+ if (mSavedImagePath != null) {
+ Log.d(TAG, "Deleting " + mSavedImagePath);
+ //noinspection ResultOfMethodCallIgnored
+ mSavedImagePath.delete();
+ mSavedImagePath = null;
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ Log.d(TAG, "onStop");
+ super.onStop();
}
@Override
protected void onDestroy() {
+ Log.d(TAG, "onDestroy");
super.onDestroy();
- if (isFinishing() && !TextUtils.isEmpty(mSavedImagePath)) {
- Log.d(TAG, "Deleting " + mSavedImagePath);
- File file = new File(mSavedImagePath);
- file.delete();
- }
}
private void setButtonsEnabled(boolean enabled) {
@@ -244,67 +364,51 @@
}
private void startExport(PendingAction action) {
+ Log.d(TAG, "startExport(action = " + action + ")");
Drawable drawable = mPreview.getDrawable();
+ if (drawable == null) {
+ Log.e(TAG, "No drawable, skipping export!");
+ return;
+ }
- Rect croppedPortion = new Rect(
- 0,
- (int) (drawable.getIntrinsicHeight() * mCropView.getTopBoundary()),
- drawable.getIntrinsicWidth(),
- (int) (drawable.getIntrinsicHeight() * mCropView.getBottomBoundary()));
+ Rect bounds = new Rect(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
+ int height = bounds.height();
+ bounds.top = (int) (height * mCropView.getTopBoundary());
+ bounds.bottom = (int) (height * mCropView.getBottomBoundary());
+
+ if (bounds.isEmpty()) {
+ Log.w(TAG, "Crop bounds empty, skipping export.");
+ return;
+ }
+
+ Bitmap output = renderBitmap(mPreview.getDrawable(), bounds);
ListenableFuture<ImageExporter.Result> exportFuture = mImageExporter.export(
- mBackgroundExecutor, UUID.randomUUID(), getBitmap(croppedPortion, drawable),
- ZonedDateTime.now());
- exportFuture.addListener(() -> {
- try {
- ImageExporter.Result result = exportFuture.get();
- setButtonsEnabled(true);
- switch (action) {
- case EDIT:
- doEdit(result.uri);
- break;
- case SHARE:
- doShare(result.uri);
- break;
- case SAVE:
- // Nothing more to do
- finishAndRemoveTask();
- break;
- }
- } catch (InterruptedException | ExecutionException e) {
- Log.e(TAG, "failed to export", e);
- setButtonsEnabled(true);
- }
- }, mUiExecutor);
+ mBackgroundExecutor, UUID.randomUUID(), output, ZonedDateTime.now());
+ exportFuture.addListener(() -> onExportCompleted(action, exportFuture), mUiExecutor);
}
- private Bitmap getBitmap(Rect bounds, Drawable drawable) {
- final RenderNode output = new RenderNode("Bitmap Export");
- output.setPosition(0, 0, bounds.width(), bounds.height());
- RecordingCanvas canvas = output.beginRecording();
- // Translating the canvas instead of setting drawable bounds since the drawable is still
- // used in the preview.
- canvas.translate(0, -bounds.top);
- drawable.draw(canvas);
- output.endRecording();
- return HardwareRenderer.createHardwareBitmap(output, bounds.width(), bounds.height());
- }
-
- private void saveCacheBitmap(ImageTileSet tileSet) {
- long startTime = SystemClock.uptimeMillis();
- Bitmap bitmap = tileSet.toBitmap();
- // TODO(b/181562529) Remove this
- mPreview.setImageDrawable(tileSet.getDrawable());
+ private void onExportCompleted(PendingAction action,
+ ListenableFuture<ImageExporter.Result> exportFuture) {
+ setButtonsEnabled(true);
+ ImageExporter.Result result;
try {
- File file = File.createTempFile("long_screenshot", ".png", null);
- FileOutputStream stream = new FileOutputStream(file);
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
- stream.flush();
- stream.close();
- mSavedImagePath = file.getAbsolutePath();
- Log.d(TAG, "Saved to " + file.getAbsolutePath() + " in "
- + (SystemClock.uptimeMillis() - startTime) + "ms");
- } catch (IOException e) {
- Log.e(TAG, "Failed to save bitmap", e);
+ result = exportFuture.get();
+ } catch (CancellationException | InterruptedException | ExecutionException e) {
+ Log.e(TAG, "failed to export", e);
+ return;
+ }
+
+ switch (action) {
+ case EDIT:
+ doEdit(result.uri);
+ break;
+ case SHARE:
+ doShare(result.uri);
+ break;
+ case SAVE:
+ // Nothing more to do
+ finishAndRemoveTask();
+ break;
}
}
@@ -313,8 +417,8 @@
if (drawable == null) {
return;
}
-
- float imageRatio = drawable.getBounds().width() / (float) drawable.getBounds().height();
+ Rect bounds = drawable.getBounds();
+ float imageRatio = bounds.width() / (float) bounds.height();
float viewRatio = mPreview.getWidth() / (float) mPreview.getHeight();
if (imageRatio > viewRatio) {
@@ -328,35 +432,4 @@
mCropView.setExtraPadding(0, 0);
}
}
-
- private void doCapture() {
- mScrollCaptureController.start(mConnection,
- new ScrollCaptureController.ScrollCaptureCallback() {
- @Override
- public void onError() {
- Log.e(TAG, "Error capturing long screenshot!");
- finishAndRemoveTask();
- }
-
- @Override
- public void onComplete(ImageTileSet imageTileSet, int pageSize) {
- Log.i(TAG, "Got tiles " + imageTileSet.getWidth() + " x "
- + imageTileSet.getHeight());
- mPreview.setImageDrawable(imageTileSet.getDrawable());
- updateCropLocation();
- mMagnifierView.setDrawable(imageTileSet.getDrawable(),
- imageTileSet.getWidth(), imageTileSet.getHeight());
- // Original boundaries go from the image tile set's y=0 to y=pageSize, so
- // we animate to that as a starting crop position.
- float topFraction = Math.max(0,
- -imageTileSet.getTop() / (float) imageTileSet.getHeight());
- float bottomFraction = Math.min(1f,
- 1 - (imageTileSet.getBottom() - pageSize)
- / (float) imageTileSet.getHeight());
- mCropView.animateBoundaryTo(CropView.CropBoundary.TOP, topFraction);
- mCropView.animateBoundaryTo(CropView.CropBoundary.BOTTOM, bottomFraction);
- mBackgroundExecutor.execute(() -> saveCacheBitmap(imageTileSet));
- }
- });
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 3d6dea3..798a063 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -65,6 +65,7 @@
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
+import android.view.ScrollCaptureResponse;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -86,10 +87,14 @@
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback;
import com.android.systemui.util.DeviceConfigProxy;
+import com.google.common.util.concurrent.ListenableFuture;
+
import java.util.List;
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -101,7 +106,8 @@
public class ScreenshotController {
private static final String TAG = logTag(ScreenshotController.class);
- private static ScrollCaptureClient.Connection sScrollConnection;
+ private ScrollCaptureResponse mLastScrollCaptureResponse;
+ private ListenableFuture<ScrollCaptureResponse> mLastScrollCaptureRequest;
/**
* POD used in the AsyncTask which saves an image in the background.
@@ -222,12 +228,6 @@
| ActivityInfo.CONFIG_SCREEN_LAYOUT
| ActivityInfo.CONFIG_ASSETS_PATHS);
- public static @Nullable ScrollCaptureClient.Connection takeScrollCaptureConnection() {
- ScrollCaptureClient.Connection connection = sScrollConnection;
- sScrollConnection = null;
- return connection;
- }
-
@Inject
ScreenshotController(
Context context,
@@ -352,6 +352,11 @@
} else {
mScreenshotView.animateDismissal();
}
+
+ if (mLastScrollCaptureResponse != null) {
+ mLastScrollCaptureResponse.close();
+ mLastScrollCaptureResponse = null;
+ }
}
boolean isPendingSharedTransition() {
@@ -526,16 +531,15 @@
// the reference used to locate the target window (below).
withWindowAttached(() -> {
mScrollCaptureClient.setHostWindowToken(decorView.getWindowToken());
- mScrollCaptureClient.request(DEFAULT_DISPLAY,
- /* onConnection */
- (connection) -> mScreenshotHandler.post(() ->
- mScreenshotView.showScrollChip(() ->
- /* onClick */
- runScrollCapture(connection))));
+ if (mLastScrollCaptureRequest != null) {
+ mLastScrollCaptureRequest.cancel(true);
+ }
+ mLastScrollCaptureRequest = mScrollCaptureClient.request(DEFAULT_DISPLAY);
+ mLastScrollCaptureRequest.addListener(() ->
+ onScrollCaptureResponseReady(mLastScrollCaptureRequest), mMainExecutor);
});
}
-
attachWindow();
mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@@ -560,6 +564,27 @@
cancelTimeout(); // restarted after animation
}
+ private void onScrollCaptureResponseReady(Future<ScrollCaptureResponse> responseFuture) {
+ try {
+ if (mLastScrollCaptureResponse != null) {
+ mLastScrollCaptureResponse.close();
+ }
+ mLastScrollCaptureResponse = responseFuture.get();
+ final Intent intent = new Intent(mContext, LongScreenshotActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ intent.putExtra(LongScreenshotActivity.EXTRA_CAPTURE_RESPONSE,
+ mLastScrollCaptureResponse);
+ mScreenshotView.showScrollChip(/* onClick */ () -> {
+ // Clear the reference to prevent close() in dismissScreenshot
+ mLastScrollCaptureResponse = null;
+ mContext.startActivity(intent);
+ dismissScreenshot(false);
+ });
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "requestScrollCapture failed", e);
+ }
+ }
+
private void withWindowAttached(Runnable action) {
View decorView = mWindow.getDecorView();
if (decorView.isAttachedToWindow()) {
@@ -606,15 +631,6 @@
}
}
- private void runScrollCapture(ScrollCaptureClient.Connection connection) {
- sScrollConnection = connection; // For LongScreenshotActivity to pick up.
-
- Intent intent = new Intent(mContext, LongScreenshotActivity.class);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mContext.startActivity(intent);
- dismissScreenshot(false);
- }
-
/**
* Save the bitmap but don't show the normal screenshot UI.. just a toast (or notification on
* failure).
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
index 54b99bb..926d5c4 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureClient.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,18 +30,23 @@
import android.hardware.HardwareBuffer;
import android.media.Image;
import android.media.ImageReader;
+import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.util.Log;
import android.view.IScrollCaptureCallbacks;
import android.view.IScrollCaptureConnection;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindowManager;
import android.view.ScrollCaptureResponse;
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
+
import com.android.internal.annotations.VisibleForTesting;
-import java.util.function.Consumer;
+import com.google.common.util.concurrent.ListenableFuture;
import javax.inject.Inject;
@@ -57,61 +62,6 @@
private static final String TAG = LogConfig.logTag(ScrollCaptureClient.class);
-
- /**
- * A connection to a remote window. Starts a capture session.
- */
- public interface Connection {
- /**
- * Start a session.
-
- * @param sessionConsumer listener to receive the session once active
- * @param maxPages the capture buffer size expressed as a multiple of the content height
- */
- // TODO ListenableFuture
- void start(Consumer<Session> sessionConsumer, float maxPages);
-
- /**
- * Close the connection. Must end capture if started to avoid potential unwanted visual
- * artifacts.
- *
- * @see Session#end(Runnable)
- */
- void close();
- }
-
- static class CaptureResult {
- public final Image image;
- /**
- * The area requested, in content rect space, relative to scroll-bounds.
- */
- public final Rect requested;
- /**
- * The actual area captured, in content rect space, relative to scroll-bounds. This may be
- * cropped or empty depending on available content.
- */
- public final Rect captured;
-
- // Error?
-
- private CaptureResult(Image image, Rect request, Rect captured) {
- this.image = image;
- this.requested = request;
- this.captured = captured;
- }
-
- @Override
- public String toString() {
- return "CaptureResult{"
- + "requested=" + requested
- + " (" + requested.width() + "x" + requested.height() + ")"
- + ", captured=" + captured
- + " (" + captured.width() + "x" + captured.height() + ")"
- + ", image=" + image
- + '}';
- }
- }
-
/**
* Represents the connection to a target window and provides a mechanism for requesting tiles.
*/
@@ -121,10 +71,8 @@
* and from left 0, to {@link #getPageWidth()}
*
* @param top the top (y) position of the tile to capture, in content rect space
- * @param consumer listener to be informed of the result
*/
- // TODO ListenableFuture
- void requestTile(int top, Consumer<CaptureResult> consumer);
+ ListenableFuture<CaptureResult> requestTile(int top);
/**
* Returns the maximum number of tiles which may be requested and retained without
@@ -139,6 +87,7 @@
*/
int getTileHeight();
+
/**
* @return the height of scrollable content being captured
*/
@@ -155,11 +104,42 @@
Rect getWindowBounds();
/**
- * End the capture session, return the target app to original state. The listener
- * will be called when the target app is ready to before visible and interactive.
+ * End the capture session, return the target app to original state. The returned Future
+ * will complete once the target app is ready to become visible and interactive.
*/
- // TODO ListenableFuture
- void end(Runnable listener);
+ ListenableFuture<Void> end();
+
+ void release();
+ }
+
+ static class CaptureResult {
+ public final Image image;
+ /**
+ * The area requested, in content rect space, relative to scroll-bounds.
+ */
+ public final Rect requested;
+ /**
+ * The actual area captured, in content rect space, relative to scroll-bounds. This may be
+ * cropped or empty depending on available content.
+ */
+ public final Rect captured;
+
+ CaptureResult(Image image, Rect request, Rect captured) {
+ this.image = image;
+ this.requested = request;
+ this.captured = captured;
+ }
+
+ @Override
+ public String toString() {
+ return "CaptureResult{"
+ + "requested=" + requested
+ + " (" + requested.width() + "x" + requested.height() + ")"
+ + ", captured=" + captured
+ + " (" + captured.width() + "x" + captured.height() + ")"
+ + ", image=" + image
+ + '}';
+ }
}
private final IWindowManager mWindowManagerService;
@@ -185,10 +165,9 @@
* Check for scroll capture support.
*
* @param displayId id for the display containing the target window
- * @param consumer receives a connection when available
*/
- public void request(int displayId, Consumer<Connection> consumer) {
- request(displayId, MATCH_ANY_TASK, consumer);
+ public ListenableFuture<ScrollCaptureResponse> request(int displayId) {
+ return request(displayId, MATCH_ANY_TASK);
}
/**
@@ -196,195 +175,210 @@
*
* @param displayId id for the display containing the target window
* @param taskId id for the task containing the target window or {@link #MATCH_ANY_TASK}.
- * @param consumer receives a connection when available
+ * @return a listenable future providing the response
*/
- public void request(int displayId, int taskId, Consumer<Connection> consumer) {
- try {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "requestScrollCapture(displayId=" + displayId + ", " + mHostWindowToken
- + ", taskId=" + taskId + ", consumer=" + consumer + ")");
+ public ListenableFuture<ScrollCaptureResponse> request(int displayId, int taskId) {
+ return CallbackToFutureAdapter.getFuture((completer) -> {
+ try {
+ mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId,
+ new IScrollCaptureResponseListener.Stub() {
+ @Override
+ public void onScrollCaptureResponse(ScrollCaptureResponse response) {
+ completer.set(response);
+ }
+ });
+
+ } catch (RemoteException e) {
+ completer.setException(e);
}
- mWindowManagerService.requestScrollCapture(displayId, mHostWindowToken, taskId,
- new ClientCallbacks(consumer));
- } catch (RemoteException e) {
- Log.e(TAG, "Ignored remote exception", e);
- }
+ return "ScrollCaptureClient#request"
+ + "(displayId=" + displayId + ", taskId=" + taskId + ")";
+ });
}
- private static class ClientCallbacks extends IScrollCaptureCallbacks.Stub implements
- Connection, Session, IBinder.DeathRecipient {
+ /**
+ * Start a scroll capture session.
+ *
+ * @param response a response provided from a request containing a connection
+ * @param maxPages the capture buffer size expressed as a multiple of the content height
+ * @return a listenable future providing the session
+ */
+ public ListenableFuture<Session> start(ScrollCaptureResponse response, float maxPages) {
+ IScrollCaptureConnection connection = response.getConnection();
+ return CallbackToFutureAdapter.getFuture((completer) -> {
+ if (connection == null || !connection.asBinder().isBinderAlive()) {
+ completer.setException(new DeadObjectException("No active connection!"));
+ return "";
+ }
+ SessionWrapper session = new SessionWrapper(connection, response.getWindowBounds(),
+ response.getBoundsInWindow(), maxPages);
+ session.start(completer);
+ return "IScrollCaptureCallbacks#onCaptureStarted";
+ });
+ }
+
+ private static class SessionWrapper extends IScrollCaptureCallbacks.Stub implements Session,
+ IBinder.DeathRecipient {
private IScrollCaptureConnection mConnection;
- private Consumer<Connection> mConnectionConsumer;
- private Consumer<Session> mSessionConsumer;
- private Consumer<CaptureResult> mResultConsumer;
- private Runnable mShutdownListener;
private ImageReader mReader;
- private Rect mScrollBounds;
- private int mTileHeight;
- private int mTileWidth;
+ private final int mTileHeight;
+ private final int mTileWidth;
private Rect mRequestRect;
private boolean mStarted;
private ICancellationSignal mCancellationSignal;
- private Rect mWindowBounds;
- private Rect mBoundsInWindow;
- private int mMaxTiles;
+ private final Rect mWindowBounds;
+ private final Rect mBoundsInWindow;
+ private final int mMaxTiles;
- private ClientCallbacks(Consumer<Connection> connectionConsumer) {
- mConnectionConsumer = connectionConsumer;
- }
+ private Completer<Session> mStartCompleter;
+ private Completer<CaptureResult> mTileRequestCompleter;
+ private Completer<Void> mEndCompleter;
- @BinderThread
- @Override
- public void onScrollCaptureResponse(ScrollCaptureResponse response) throws RemoteException {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "onScrollCaptureResponse(response=" + response + ")");
- }
- if (response.isConnected()) {
- mConnection = response.getConnection();
- mConnection.asBinder().linkToDeath(this, 0);
- mWindowBounds = response.getWindowBounds();
- mBoundsInWindow = response.getBoundsInWindow();
+ private SessionWrapper(IScrollCaptureConnection connection, Rect windowBounds,
+ Rect boundsInWindow, float maxPages) throws RemoteException {
+ mConnection = requireNonNull(connection);
+ mConnection.asBinder().linkToDeath(SessionWrapper.this, 0);
+ mWindowBounds = requireNonNull(windowBounds);
+ mBoundsInWindow = requireNonNull(boundsInWindow);
- int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height();
- int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE));
- mTileWidth = mBoundsInWindow.width();
- mTileHeight = pxPerTile / mBoundsInWindow.width();
- if (DEBUG_SCROLL) {
- Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
- Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
- Log.d(TAG, "maxHeight: " + (mMaxTiles * mTileHeight) + "px");
- }
- mConnectionConsumer.accept(this);
- }
- mConnectionConsumer = null;
- }
+ int pxPerPage = mBoundsInWindow.width() * mBoundsInWindow.height();
+ int pxPerTile = min(TILE_SIZE_PX_MAX, (pxPerPage / TILES_PER_PAGE));
- @Override
- public void start(Consumer<Session> sessionConsumer, float maxPages) {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "start(sessionConsumer=" + sessionConsumer + ","
- + " maxPages=" + maxPages + ")");
- }
+ mTileWidth = mBoundsInWindow.width();
+ mTileHeight = pxPerTile / mBoundsInWindow.width();
mMaxTiles = (int) Math.ceil(maxPages * TILES_PER_PAGE);
+
+ if (DEBUG_SCROLL) {
+ Log.d(TAG, "boundsInWindow: " + mBoundsInWindow);
+ Log.d(TAG, "tile size: " + mTileWidth + "x" + mTileHeight);
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ Log.d(TAG, "binderDied! The target process just crashed :-(");
+ // Clean up
+ mConnection = null;
+
+ // Pass along the bad news.
+ if (mStartCompleter != null) {
+ mStartCompleter.setException(new DeadObjectException("The remote process died"));
+ }
+ if (mTileRequestCompleter != null) {
+ mTileRequestCompleter.setException(
+ new DeadObjectException("The remote process died"));
+ }
+ if (mEndCompleter != null) {
+ mEndCompleter.setException(new DeadObjectException("The remote process died"));
+ }
+ }
+
+ private void start(Completer<Session> completer) {
mReader = ImageReader.newInstance(mTileWidth, mTileHeight, PixelFormat.RGBA_8888,
mMaxTiles, HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE);
- mSessionConsumer = sessionConsumer;
-
+ mStartCompleter = completer;
try {
- mCancellationSignal = mConnection.startCapture(mReader.getSurface());
+ mCancellationSignal = mConnection.startCapture(mReader.getSurface(), this);
+ completer.addCancellationListener(() -> {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }, Runnable::run);
mStarted = true;
} catch (RemoteException e) {
- Log.w(TAG, "Failed to start", e);
mReader.close();
+ completer.setException(e);
}
}
@BinderThread
@Override
public void onCaptureStarted() {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "onCaptureStarted()");
- }
- mSessionConsumer.accept(this);
- mSessionConsumer = null;
+ Log.d(TAG, "onCaptureStarted");
+ mStartCompleter.set(this);
}
@Override
- public void requestTile(int top, Consumer<CaptureResult> consumer) {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "requestTile(top=" + top + ", consumer=" + consumer + ")");
- }
- cancelPendingRequest();
+ public ListenableFuture<CaptureResult> requestTile(int top) {
mRequestRect = new Rect(0, top, mTileWidth, top + mTileHeight);
- mResultConsumer = consumer;
- try {
- mCancellationSignal = mConnection.requestImage(mRequestRect);
- } catch (RemoteException e) {
- Log.e(TAG, "Caught remote exception from requestImage", e);
- }
+ return CallbackToFutureAdapter.getFuture((completer -> {
+ if (mConnection == null || !mConnection.asBinder().isBinderAlive()) {
+ completer.setException(new DeadObjectException("Connection is closed!"));
+ return "";
+ }
+ try {
+ mTileRequestCompleter = completer;
+ mCancellationSignal = mConnection.requestImage(mRequestRect);
+ completer.addCancellationListener(() -> {
+ try {
+ mCancellationSignal.cancel();
+ } catch (RemoteException e) {
+ // Ignore
+ }
+ }, Runnable::run);
+ } catch (RemoteException e) {
+ completer.setException(e);
+ }
+ return "IScrollCaptureCallbacks#onImageRequestCompleted";
+ }));
}
+ @BinderThread
@Override
public void onImageRequestCompleted(int flags, Rect contentArea) {
Image image = mReader.acquireLatestImage();
- if (DEBUG_SCROLL) {
- Log.d(TAG, "onCaptureBufferSent(flags=" + flags
- + ", contentArea=" + contentArea + ") image=" + image);
- }
- // Save and clear first, since the consumer will likely request the next
- // tile, otherwise the new consumer will be wiped out.
- Consumer<CaptureResult> consumer = mResultConsumer;
- mResultConsumer = null;
- consumer.accept(new CaptureResult(image, mRequestRect, contentArea));
+ mTileRequestCompleter.set(new CaptureResult(image, mRequestRect, contentArea));
}
@Override
- public void end(Runnable listener) {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "end(listener=" + listener + ")");
- }
- if (mStarted) {
- mShutdownListener = listener;
- mReader.close();
+ public ListenableFuture<Void> end() {
+ Log.d(TAG, "end()");
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ if (!mStarted) {
+ try {
+ mConnection.asBinder().unlinkToDeath(SessionWrapper.this, 0);
+ mConnection.close();
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ mConnection = null;
+ completer.set(null);
+ return "";
+ }
+
+ mEndCompleter = completer;
try {
- // listener called from onConnectionClosed callback
mConnection.endCapture();
} catch (RemoteException e) {
- Log.d(TAG, "Ignored exception from endCapture()", e);
- disconnect();
- listener.run();
+ completer.setException(e);
}
- } else {
- disconnect();
- listener.run();
- }
+ return "IScrollCaptureCallbacks#onCaptureEnded";
+ });
+ }
+
+ public void release() {
+ mReader.close();
}
@BinderThread
@Override
public void onCaptureEnded() {
- close();
- if (mShutdownListener != null) {
- mShutdownListener.run();
- mShutdownListener = null;
+ try {
+ mConnection.close();
+ } catch (RemoteException e) {
+ /* ignore */
}
- }
-
- @Override
- public void close() {
- if (mConnection != null) {
- try {
- mConnection.close();
- } catch (RemoteException e) {
- /* ignore */
- }
- disconnect();
- }
+ mConnection = null;
+ mEndCompleter.set(null);
}
// Misc
- private void disconnect() {
- if (mConnection != null) {
- mConnection.asBinder().unlinkToDeath(this, 0);
- }
- mConnection = null;
- }
-
- /**
- * The process hosting the window went away abruptly!
- */
- @Override
- public void binderDied() {
- if (DEBUG_SCROLL) {
- Log.d(TAG, "binderDied()");
- }
- disconnect();
- }
-
@Override
public int getPageHeight() {
return mBoundsInWindow.height();
@@ -408,16 +402,5 @@
public int getMaxTiles() {
return mMaxTiles;
}
-
- private void cancelPendingRequest() {
- if (mCancellationSignal != null) {
- try {
- mCancellationSignal.cancel();
- } catch (RemoteException e) {
- /* ignore */
- }
- mCancellationSignal = null;
- }
- }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index 34094cd..4f69904 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -16,18 +16,27 @@
package com.android.systemui.screenshot;
-import android.annotation.UiThread;
import android.content.Context;
-import android.net.Uri;
+import android.graphics.Bitmap;
+import android.graphics.HardwareRenderer;
+import android.graphics.RecordingCanvas;
+import android.graphics.Rect;
+import android.graphics.RenderNode;
+import android.graphics.drawable.Drawable;
import android.provider.Settings;
import android.util.Log;
+import android.view.IWindowManager;
+import android.view.ScrollCaptureResponse;
+
+import androidx.concurrent.futures.CallbackToFutureAdapter;
+import androidx.concurrent.futures.CallbackToFutureAdapter.Completer;
import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
-import java.time.ZonedDateTime;
-import java.util.UUID;
+import com.google.common.util.concurrent.ListenableFuture;
+
+import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
/**
@@ -47,44 +56,136 @@
// or if the desired bitmap size is reached.
private boolean mFinishOnBoundary;
- private Session mSession;
-
public static final int MAX_HEIGHT = 12000;
private final Context mContext;
-
- private final Executor mUiExecutor;
private final Executor mBgExecutor;
- private final ImageExporter mImageExporter;
private final ImageTileSet mImageTileSet;
+ private final ScrollCaptureClient mClient;
- private ZonedDateTime mCaptureTime;
- private UUID mRequestId;
- private ScrollCaptureCallback mCaptureCallback;
+ private Completer<LongScreenshot> mCaptureCompleter;
- public ScrollCaptureController(Context context, Executor uiExecutor, Executor bgExecutor,
- ImageExporter exporter) {
+ private ListenableFuture<Session> mSessionFuture;
+ private Session mSession;
+ private ListenableFuture<CaptureResult> mTileFuture;
+ private ListenableFuture<Void> mEndFuture;
+
+ static class LongScreenshot {
+ private final ImageTileSet mImageTileSet;
+ private final Session mSession;
+
+ LongScreenshot(Session session, ImageTileSet imageTileSet) {
+ mSession = session;
+ mImageTileSet = imageTileSet;
+ }
+
+ /** Returns a bitmap containing the combinded result. */
+ public Bitmap toBitmap() {
+ return mImageTileSet.toBitmap();
+ }
+
+ public Bitmap toBitmap(Rect bounds) {
+ return mImageTileSet.toBitmap(bounds);
+ }
+
+ /** Releases image resources from the screenshot. */
+ public void release() {
+ Log.d(TAG, "LongScreenshot :: release()");
+ mImageTileSet.clear();
+ mSession.release();
+ }
+
+ public int getLeft() {
+ return mImageTileSet.getLeft();
+ }
+
+ public int getTop() {
+ return mImageTileSet.getTop();
+ }
+
+ public int getBottom() {
+ return mImageTileSet.getBottom();
+ }
+
+ public int getWidth() {
+ return mImageTileSet.getWidth();
+ }
+
+ public int getHeight() {
+ return mImageTileSet.getHeight();
+ }
+
+ /** @return the height of the visible area of the scrolling page, in pixels */
+ public int getPageHeight() {
+ return mSession.getPageHeight();
+ }
+
+ @Override
+ public String toString() {
+ return "LongScreenshot{w=" + mImageTileSet.getWidth()
+ + ", h=" + mImageTileSet.getHeight() + "}";
+ }
+
+ public Drawable getDrawable() {
+ return mImageTileSet.getDrawable();
+ }
+ }
+
+ ScrollCaptureController(Context context, Executor bgExecutor, IWindowManager wms) {
mContext = context;
- mUiExecutor = uiExecutor;
mBgExecutor = bgExecutor;
- mImageExporter = exporter;
mImageTileSet = new ImageTileSet(context.getMainThreadHandler());
+ mClient = new ScrollCaptureClient(mContext, wms);
}
/**
- * Run scroll capture!
+ * Run scroll capture. Performs a batch capture, collecting image tiles.
*
- * @param connection connection to the remote window to be used
- * @param callback request callback to report back to the service
+ * @param response a scroll capture response from a previous request which is
+ * {@link ScrollCaptureResponse#isConnected() connected}.
+ * @return a future ImageTile set containing the result
*/
- public void start(Connection connection, ScrollCaptureCallback callback) {
- mCaptureTime = ZonedDateTime.now();
- mRequestId = UUID.randomUUID();
- mCaptureCallback = callback;
+ ListenableFuture<LongScreenshot> run(ScrollCaptureResponse response) {
+ Log.d(TAG, "run: " + response);
+ return CallbackToFutureAdapter.getFuture(completer -> {
+ Log.d(TAG, "getFuture(ImageTileSet) ");
+ mCaptureCompleter = completer;
+ mBgExecutor.execute(() -> {
+ Log.d(TAG, "bgExecutor.execute");
+ float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(),
+ SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT);
+ Log.d(TAG, "client start, maxPages=" + maxPages);
+ mSessionFuture = mClient.start(response, maxPages);
+ mSessionFuture.addListener(this::onStartComplete, mContext.getMainExecutor());
+ });
+ return "<batch scroll capture>";
+ });
+ }
- float maxPages = Settings.Secure.getFloat(mContext.getContentResolver(),
- SETTING_KEY_MAX_PAGES, MAX_PAGES_DEFAULT);
- connection.start(this::startCapture, maxPages);
+ private void onStartComplete() {
+ try {
+ mSession = mSessionFuture.get();
+ Log.d(TAG, "got session " + mSession);
+ requestNextTile(0);
+ } catch (InterruptedException | ExecutionException e) {
+ // Failure to start, propagate to caller
+ Log.d(TAG, "session start failed!");
+ mCaptureCompleter.setException(e);
+ }
+ }
+
+ private void requestNextTile(int topPx) {
+ Log.d(TAG, "requestNextTile: " + topPx);
+ mTileFuture = mSession.requestTile(topPx);
+ mTileFuture.addListener(() -> {
+ try {
+ Log.d(TAG, "onCaptureResult");
+ onCaptureResult(mTileFuture.get());
+ } catch (InterruptedException | ExecutionException e) {
+ Log.e(TAG, "requestTile failed!", e);
+ mCaptureCompleter.setException(e);
+ }
+ }, mContext.getMainExecutor());
}
private void onCaptureResult(CaptureResult result) {
@@ -146,49 +247,24 @@
}
if (finish) {
- Session session = mSession;
- mSession = null;
Log.d(TAG, "Stop.");
- mUiExecutor.execute(() -> afterCaptureComplete(session));
+ finishCapture();
return;
}
int nextTop = (mScrollingUp)
? result.captured.top - mSession.getTileHeight() : result.captured.bottom;
- Log.d(TAG, "requestTile: " + nextTop);
- mSession.requestTile(nextTop, /* consumer */ this::onCaptureResult);
+ requestNextTile(nextTop);
}
- private void startCapture(Session session) {
- mSession = session;
- session.requestTile(0, this::onCaptureResult);
+ private void finishCapture() {
+ Log.d(TAG, "finishCapture()");
+ mEndFuture = mSession.end();
+ mEndFuture.addListener(() -> {
+ Log.d(TAG, "endCapture completed");
+ // Provide result to caller and complete the top-level future
+ // Caller is responsible for releasing this resource (ImageReader/HardwareBuffers)
+ mCaptureCompleter.set(new LongScreenshot(mSession, mImageTileSet));
+ }, mContext.getMainExecutor());
}
-
- @UiThread
- void afterCaptureComplete(Session session) {
- Log.d(TAG, "afterCaptureComplete");
-
- if (mImageTileSet.isEmpty()) {
- mCaptureCallback.onError();
- } else {
- mCaptureCallback.onComplete(mImageTileSet, session.getPageHeight());
- }
- }
-
- /**
- * Callback for image capture completion or error.
- */
- public interface ScrollCaptureCallback {
- void onComplete(ImageTileSet imageTileSet, int pageHeight);
- void onError();
- }
-
- /**
- * Callback for image export completion or error.
- */
- public interface ExportCallback {
- void onExportComplete(Uri outputUri);
- void onError();
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
index 34b29ca..2856ebb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationRemoteInputManager.java
@@ -31,6 +31,7 @@
import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Handler;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
@@ -69,8 +70,10 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Stream;
import dagger.Lazy;
@@ -630,24 +633,17 @@
Notification.Builder b = Notification.Builder
.recoverBuilder(mContext, sbn.getNotification().clone());
if (remoteInputText != null || uri != null) {
- RemoteInputHistoryItem[] oldHistoryItems = (RemoteInputHistoryItem[])
- sbn.getNotification().extras.getParcelableArray(
- Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
- RemoteInputHistoryItem[] newHistoryItems;
-
- if (oldHistoryItems == null) {
- newHistoryItems = new RemoteInputHistoryItem[1];
- } else {
- newHistoryItems = new RemoteInputHistoryItem[oldHistoryItems.length + 1];
- System.arraycopy(oldHistoryItems, 0, newHistoryItems, 1, oldHistoryItems.length);
- }
- RemoteInputHistoryItem newItem;
- if (uri != null) {
- newItem = new RemoteInputHistoryItem(mimeType, uri, remoteInputText);
- } else {
- newItem = new RemoteInputHistoryItem(remoteInputText);
- }
- newHistoryItems[0] = newItem;
+ RemoteInputHistoryItem newItem = uri != null
+ ? new RemoteInputHistoryItem(mimeType, uri, remoteInputText)
+ : new RemoteInputHistoryItem(remoteInputText);
+ Parcelable[] oldHistoryItems = sbn.getNotification().extras
+ .getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ RemoteInputHistoryItem[] newHistoryItems = oldHistoryItems != null
+ ? Stream.concat(
+ Stream.of(newItem),
+ Arrays.stream(oldHistoryItems).map(p -> (RemoteInputHistoryItem) p))
+ .toArray(RemoteInputHistoryItem[]::new)
+ : new RemoteInputHistoryItem[] { newItem };
b.setRemoteInputHistory(newHistoryItems);
}
b.setShowRemoteInputSpinner(showSpinner);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
index dbd8580..5f93f480 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java
@@ -41,11 +41,11 @@
import android.app.NotificationManager.Policy;
import android.app.Person;
import android.app.RemoteInput;
-import android.app.RemoteInputHistoryItem;
import android.content.Context;
import android.content.pm.ShortcutInfo;
import android.net.Uri;
import android.os.Bundle;
+import android.os.Parcelable;
import android.os.SystemClock;
import android.service.notification.NotificationListenerService.Ranking;
import android.service.notification.SnoozeCriterion;
@@ -534,8 +534,8 @@
return false;
}
Bundle extras = mSbn.getNotification().extras;
- RemoteInputHistoryItem[] replyTexts = (RemoteInputHistoryItem[]) extras.getParcelableArray(
- Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
+ Parcelable[] replyTexts =
+ extras.getParcelableArray(Notification.EXTRA_REMOTE_INPUT_HISTORY_ITEMS);
if (!ArrayUtils.isEmpty(replyTexts)) {
return true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
index e8331a1..e76b803 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityController.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.policy;
import android.app.admin.DeviceAdminInfo;
+import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import com.android.systemui.Dumpable;
@@ -33,6 +34,10 @@
String getProfileOwnerName();
CharSequence getDeviceOwnerOrganizationName();
CharSequence getWorkProfileOrganizationName();
+ /** Device owner component even if not on this user. **/
+ ComponentName getDeviceOwnerComponentOnAnyUser();
+ /** Device owner type for a device owner. **/
+ int getDeviceOwnerType(ComponentName admin);
boolean isNetworkLoggingEnabled();
boolean isVpnEnabled();
boolean isVpnRestricted();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
index 5638503..4afb86b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SecurityControllerImpl.java
@@ -15,9 +15,11 @@
*/
package com.android.systemui.statusbar.policy;
+import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.admin.DeviceAdminInfo;
import android.app.admin.DevicePolicyManager;
+import android.app.admin.DevicePolicyManager.DeviceOwnerType;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -225,6 +227,18 @@
}
@Override
+ @Nullable
+ public ComponentName getDeviceOwnerComponentOnAnyUser() {
+ return mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser();
+ }
+
+ @Override
+ @DeviceOwnerType
+ public int getDeviceOwnerType(@NonNull ComponentName admin) {
+ return mDevicePolicyManager.getDeviceOwnerType(admin);
+ }
+
+ @Override
public boolean isNetworkLoggingEnabled() {
return mDevicePolicyManager.isNetworkLoggingEnabled(null);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
index 862e374..5422ae8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSSecurityFooterTest.java
@@ -14,6 +14,9 @@
package com.android.systemui.qs;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_DEFAULT;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
@@ -24,6 +27,8 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.content.ComponentName;
+import android.content.DialogInterface;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.VectorDrawable;
@@ -74,6 +79,8 @@
private final String VPN_PACKAGE = "TestVPN";
private final String VPN_PACKAGE_2 = "TestVPN 2";
private static final String PARENTAL_CONTROLS_LABEL = "Parental Control App";
+ private static final ComponentName DEVICE_OWNER_COMPONENT =
+ new ComponentName("TestDPC", "Test");
private ViewGroup mRootView;
private TextView mFooterText;
@@ -101,6 +108,11 @@
mFooterIcon = mRootView.findViewById(R.id.footer_icon);
mPrimaryFooterIcon = mRootView.findViewById(R.id.primary_footer_icon);
mFooter.setHostEnvironment(null);
+
+ when(mSecurityController.getDeviceOwnerComponentOnAnyUser())
+ .thenReturn(DEVICE_OWNER_COMPONENT);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_DEFAULT);
}
@Test
@@ -148,6 +160,27 @@
}
@Test
+ public void testManagedFinancedDeviceWithOwnerName() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.getDeviceOwnerOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ mFooter.refreshState();
+
+ TestableLooper.get(this).processAllMessages();
+ assertEquals(mContext.getString(
+ R.string.quick_settings_financed_disclosure_named_management,
+ MANAGING_ORGANIZATION), mFooterText.getText());
+ assertEquals(View.VISIBLE, mRootView.getVisibility());
+ assertEquals(View.VISIBLE, mFooterIcon.getVisibility());
+ assertEquals(View.GONE, mPrimaryFooterIcon.getVisibility());
+ // -1 == never set.
+ assertEquals(-1, mFooterIcon.getLastImageResource());
+ }
+
+ @Test
public void testManagedDemoMode() {
when(mSecurityController.isDeviceManaged()).thenReturn(true);
when(mSecurityController.getDeviceOwnerOrganizationName()).thenReturn(null);
@@ -383,6 +416,25 @@
}
@Test
+ public void testGetManagementTitleForNonFinancedDevice() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+
+ assertEquals(mContext.getString(R.string.monitoring_title_device_owned),
+ mFooter.getManagementTitle(MANAGING_ORGANIZATION));
+ }
+
+ @Test
+ public void testGetManagementTitleForFinancedDevice() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ assertEquals(mContext.getString(R.string.monitoring_title_financed_device,
+ MANAGING_ORGANIZATION),
+ mFooter.getManagementTitle(MANAGING_ORGANIZATION));
+ }
+
+ @Test
public void testGetManagementMessage_noManagement() {
assertEquals(null, mFooter.getManagementMessage(
/* isDeviceManaged= */ false,
@@ -409,6 +461,21 @@
}
@Test
+ public void testGetManagementMessage_deviceOwner_asFinancedDevice() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management,
+ MANAGING_ORGANIZATION, MANAGING_ORGANIZATION),
+ mFooter.getManagementMessage(
+ /* isDeviceManaged= */ true,
+ MANAGING_ORGANIZATION,
+ /* isProfileOwnerOfOrganizationOwnedDevice= */ false,
+ /* workProfileOrganizationName= */ null));
+ }
+
+ @Test
public void testGetManagementMessage_profileOwnerOfOrganizationOwnedDevice() {
assertEquals(mContext.getString(R.string.monitoring_description_named_management,
MANAGING_ORGANIZATION),
@@ -587,6 +654,34 @@
assertEquals(PARENTAL_CONTROLS_LABEL, textView.getText());
}
+ @Test
+ public void testCreateDialogViewForFinancedDevice() {
+ when(mSecurityController.isDeviceManaged()).thenReturn(true);
+ when(mSecurityController.getDeviceOwnerOrganizationName())
+ .thenReturn(MANAGING_ORGANIZATION);
+ when(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+
+ // Initialize AlertDialog which sets the text for the negative button, which is used when
+ // creating the dialog for a financed device.
+ mFooter.showDeviceMonitoringDialog();
+ // The above statement would display the Quick Settings dialog which requires user input,
+ // so simulate the press to continue with the unit test (otherwise, it is stuck).
+ mFooter.onClick(null, DialogInterface.BUTTON_NEGATIVE);
+ View view = mFooter.createDialogView();
+
+ TextView managementSubtitle = view.findViewById(R.id.device_management_subtitle);
+ assertEquals(View.VISIBLE, managementSubtitle.getVisibility());
+ assertEquals(mContext.getString(R.string.monitoring_title_financed_device,
+ MANAGING_ORGANIZATION), managementSubtitle.getText());
+ TextView managementMessage = view.findViewById(R.id.device_management_warning);
+ assertEquals(View.VISIBLE, managementMessage.getVisibility());
+ assertEquals(mContext.getString(R.string.monitoring_financed_description_named_management,
+ MANAGING_ORGANIZATION, MANAGING_ORGANIZATION), managementMessage.getText());
+ assertEquals(mContext.getString(R.string.monitoring_button_view_policies),
+ mFooter.getSettingsButton());
+ }
+
private CharSequence addLink(CharSequence description) {
final SpannableStringBuilder message = new SpannableStringBuilder();
message.append(description);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
index 9e62a62..63f7c97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/FakeScrollCaptureConnection.java
@@ -24,6 +24,7 @@
import android.graphics.RecordingCanvas;
import android.graphics.Rect;
import android.graphics.RenderNode;
+import android.os.CancellationSignal;
import android.os.ICancellationSignal;
import android.os.RemoteException;
import android.view.IScrollCaptureCallbacks;
@@ -37,19 +38,15 @@
class FakeScrollCaptureConnection extends IScrollCaptureConnection.Stub {
private final int[] mColors = {Color.RED, Color.GREEN, Color.BLUE};
private IScrollCaptureCallbacks mCallbacks;
- private Surface mSurface;
private Paint mPaint;
private int mNextColor;
private HwuiContext mHwuiContext;
-
- FakeScrollCaptureConnection(IScrollCaptureCallbacks cb) {
- mCallbacks = cb;
- }
+ private CancellationSignal mCancellationSignal;
@Override
- public ICancellationSignal startCapture(Surface surface) {
- mSurface = surface;
- mHwuiContext = new HwuiContext(false, surface);
+ public ICancellationSignal startCapture(Surface surface, IScrollCaptureCallbacks callbacks) {
+ mCallbacks = callbacks;
+ mHwuiContext = new HwuiContext(surface);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL);
try {
@@ -57,7 +54,9 @@
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
- return null;
+ ICancellationSignal signal = CancellationSignal.createTransport();
+ mCancellationSignal = CancellationSignal.fromTransport(signal);
+ return signal;
}
@Override
@@ -72,7 +71,9 @@
} catch (RemoteException e) {
e.rethrowAsRuntimeException();
}
- return null;
+ ICancellationSignal signal = CancellationSignal.createTransport();
+ mCancellationSignal = CancellationSignal.fromTransport(signal);
+ return signal;
}
@Override
@@ -83,15 +84,15 @@
e.rethrowAsRuntimeException();
} finally {
mHwuiContext.destroy();
- mSurface = null;
mCallbacks = null;
}
- return null;
+ ICancellationSignal signal = CancellationSignal.createTransport();
+ mCancellationSignal = CancellationSignal.fromTransport(signal);
+ return signal;
}
@Override
public void close() throws RemoteException {
-
}
// From android.view.Surface, but issues render requests synchronously with waitForPresent(true)
@@ -99,21 +100,16 @@
private final RenderNode mRenderNode;
private final HardwareRenderer mHardwareRenderer;
private RecordingCanvas mCanvas;
- private final boolean mIsWideColorGamut;
- HwuiContext(boolean isWideColorGamut, Surface surface) {
+ HwuiContext(Surface surface) {
mRenderNode = RenderNode.create("HwuiCanvas", null);
mRenderNode.setClipToBounds(false);
mRenderNode.setForceDarkAllowed(false);
- mIsWideColorGamut = isWideColorGamut;
mHardwareRenderer = new HardwareRenderer();
mHardwareRenderer.setContentRoot(mRenderNode);
mHardwareRenderer.setSurface(surface, true);
- mHardwareRenderer.setColorMode(
- isWideColorGamut
- ? ActivityInfo.COLOR_MODE_WIDE_COLOR_GAMUT
- : ActivityInfo.COLOR_MODE_DEFAULT);
+ mHardwareRenderer.setColorMode(ActivityInfo.COLOR_MODE_DEFAULT);
mHardwareRenderer.setLightSourceAlpha(0.0f, 0.0f);
mHardwareRenderer.setLightSourceGeometry(0.0f, 0.0f, 0.0f, 0.0f);
}
@@ -142,9 +138,5 @@
void destroy() {
mHardwareRenderer.destroy();
}
-
- boolean isWideColorGamut() {
- return mIsWideColorGamut;
- }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
index 802b462..cf7dc20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureClientTest.java
@@ -16,15 +16,15 @@
package com.android.systemui.screenshot;
-import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
import static java.util.Objects.requireNonNull;
@@ -34,7 +34,7 @@
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.view.Display;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindowManager;
import android.view.ScrollCaptureResponse;
@@ -43,30 +43,29 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.screenshot.ScrollCaptureClient.CaptureResult;
-import com.android.systemui.screenshot.ScrollCaptureClient.Connection;
import com.android.systemui.screenshot.ScrollCaptureClient.Session;
+import com.google.common.util.concurrent.ListenableFuture;
+
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
-import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-import org.mockito.Spy;
import org.mockito.stubbing.Answer;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class ScrollCaptureClientTest extends SysuiTestCase {
- private static final float MAX_PAGES = 3f;
+ private static final float MAX_PAGES = 3.0f;
private Context mContext;
private IWindowManager mWm;
- @Spy private TestableConsumer<Session> mSessionConsumer;
- @Spy private TestableConsumer<Connection> mConnectionConsumer;
- @Spy private TestableConsumer<CaptureResult> mResultConsumer;
- @Mock private Runnable mRunnable;
-
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -79,46 +78,50 @@
}
@Test
- public void testBasicClientFlow() throws RemoteException {
+ public void testDetectAndConnect()
+ throws RemoteException, InterruptedException, ExecutionException, TimeoutException {
doAnswer((Answer<Void>) invocation -> {
- IScrollCaptureCallbacks cb = invocation.getArgument(3);
- cb.onScrollCaptureResponse(new ScrollCaptureResponse.Builder()
+ IScrollCaptureResponseListener listener = invocation.getArgument(3);
+ listener.onScrollCaptureResponse(new ScrollCaptureResponse.Builder()
.setBoundsInWindow(new Rect(0, 0, 100, 100))
.setWindowBounds(new Rect(0, 0, 100, 100))
- .setConnection(new FakeScrollCaptureConnection(cb))
+ .setConnection(new FakeScrollCaptureConnection())
.build());
return null;
}).when(mWm).requestScrollCapture(/* displayId */ anyInt(), /* token */ isNull(),
- /* taskId */ anyInt(), any(IScrollCaptureCallbacks.class));
+ /* taskId */ anyInt(), any(IScrollCaptureResponseListener.class));
// Create client
ScrollCaptureClient client = new ScrollCaptureClient(mContext, mWm);
- client.request(Display.DEFAULT_DISPLAY, mConnectionConsumer);
- verify(mConnectionConsumer, timeout(100)).accept(any(Connection.class));
+ // Request scroll capture
+ ListenableFuture<ScrollCaptureResponse> requestFuture =
+ client.request(Display.DEFAULT_DISPLAY);
+ assertNotNull(requestFuture.get(100, TimeUnit.MILLISECONDS));
- Connection conn = mConnectionConsumer.getValue();
+ ScrollCaptureResponse response = requestFuture.get();
+ assertTrue(response.isConnected());
- conn.start(mSessionConsumer, MAX_PAGES);
- verify(mSessionConsumer, timeout(100)).accept(any(Session.class));
+ // Start a session
+ ListenableFuture<Session> startFuture = client.start(response, MAX_PAGES);
+ assertNotNull(startFuture.get(100, TimeUnit.MILLISECONDS));
- Session session = mSessionConsumer.getValue();
+ Session session = startFuture.get();
Rect request = new Rect(0, 0, session.getPageWidth(), session.getTileHeight());
- session.requestTile(0, mResultConsumer);
- verify(mResultConsumer, timeout(100)).accept(any(CaptureResult.class));
+ // Request a tile
+ ListenableFuture<CaptureResult> tileFuture = session.requestTile(0);
+ assertNotNull(tileFuture.get(100, TimeUnit.MILLISECONDS));
- CaptureResult result = mResultConsumer.getValue();
- assertThat(result.requested).isEqualTo(request);
- assertThat(result.captured).isEqualTo(result.requested);
- assertThat(result.image).isNotNull();
+ CaptureResult result = tileFuture.get();
+ assertEquals(request, result.requested);
+ assertEquals(result.requested, result.captured);
+ assertNotNull(result.image);
- session.end(mRunnable);
- verify(mRunnable, timeout(100)).run();
-
- // TODO verify image
- // TODO test threading
- // TODO test failures
+ // End the session
+ ListenableFuture<Void> endFuture = session.end();
+ CountDownLatch latch = new CountDownLatch(1);
+ endFuture.addListener(latch::countDown, Runnable::run);
+ assertTrue(latch.await(100, TimeUnit.MILLISECONDS));
}
-
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
similarity index 74%
rename from packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
rename to packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
index 6564d58..06b39ab 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/ScrollCaptureFrameworkSmokeTest.java
@@ -16,15 +16,16 @@
package com.android.systemui.screenshot;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.content.Intent;
-import android.graphics.Rect;
import android.os.RemoteException;
import android.testing.AndroidTestingRunner;
import android.util.Log;
import android.view.Display;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindowManager;
import android.view.ScrollCaptureResponse;
import android.view.WindowManagerGlobal;
@@ -45,8 +46,9 @@
*/
@RunWith(AndroidTestingRunner.class)
@SmallTest
-public class ScrollCaptureTest extends SysuiTestCase {
- private static final String TAG = "ScrollCaptureTest";
+public class ScrollCaptureFrameworkSmokeTest extends SysuiTestCase {
+ private static final String TAG = "ScrollCaptureFrameworkSmokeTest";
+ private volatile ScrollCaptureResponse mResponse;
/**
* Verifies that a request traverses from SystemUI, to WindowManager and to the app process and
@@ -64,34 +66,23 @@
final CountDownLatch latch = new CountDownLatch(1);
try {
wms.requestScrollCapture(Display.DEFAULT_DISPLAY, null, -1,
- new IScrollCaptureCallbacks.Stub() {
+ new IScrollCaptureResponseListener.Stub() {
@Override
- public void onScrollCaptureResponse(ScrollCaptureResponse response)
+ public void onScrollCaptureResponse(
+ ScrollCaptureResponse response)
throws RemoteException {
- Log.d(TAG, "onScrollCaptureResponse: " + response);
+ mResponse = response;
latch.countDown();
}
-
- @Override
- public void onCaptureStarted() {
- }
-
- @Override
- public void onImageRequestCompleted(int i, Rect rect)
- throws RemoteException {
-
- }
-
- @Override
- public void onCaptureEnded() throws RemoteException {
-
- }
-
});
} catch (RemoteException e) {
Log.e(TAG, "request failed", e);
fail("caught remote exception " + e);
}
latch.await(1000, TimeUnit.MILLISECONDS);
+
+ assertNotNull(mResponse);
+ assertTrue(mResponse.isConnected());
+ assertTrue(mResponse.getWindowTitle().contains(ScrollViewActivity.class.getSimpleName()));
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java
deleted file mode 100644
index a554e5f..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TestableConsumer.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.screenshot;
-
-import java.util.function.Consumer;
-
-/** Accepts and retains the most recent value for verification */
-class TestableConsumer<T> implements Consumer<T> {
- T mValue;
-
- @Override
- public void accept(T t) {
- mValue = t;
- }
-
- public T getValue() {
- return mValue;
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
index 44184ee..ed87a40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/SecurityControllerTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar.policy;
+import static android.app.admin.DevicePolicyManager.DEVICE_OWNER_TYPE_FINANCED;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -62,6 +64,9 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class SecurityControllerTest extends SysuiTestCase {
+ private static final ComponentName DEVICE_OWNER_COMPONENT =
+ new ComponentName("com.android.foo", "bar");
+
private final DevicePolicyManager mDevicePolicyManager = mock(DevicePolicyManager.class);
private final IKeyChainService.Stub mKeyChainService = mock(IKeyChainService.Stub.class);
private final UserManager mUserManager = mock(UserManager.class);
@@ -127,6 +132,22 @@
}
@Test
+ public void testGetDeviceOwnerComponentOnAnyUser() {
+ when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser())
+ .thenReturn(DEVICE_OWNER_COMPONENT);
+ assertEquals(mSecurityController.getDeviceOwnerComponentOnAnyUser(),
+ DEVICE_OWNER_COMPONENT);
+ }
+
+ @Test
+ public void testGetDeviceOwnerType() {
+ when(mDevicePolicyManager.getDeviceOwnerType(DEVICE_OWNER_COMPONENT))
+ .thenReturn(DEVICE_OWNER_TYPE_FINANCED);
+ assertEquals(mSecurityController.getDeviceOwnerType(DEVICE_OWNER_COMPONENT),
+ DEVICE_OWNER_TYPE_FINANCED);
+ }
+
+ @Test
public void testWorkAccount() throws Exception {
assertFalse(mSecurityController.hasCACertInCurrentUser());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
index c0722a4..3640bcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakeSecurityController.java
@@ -15,6 +15,7 @@
package com.android.systemui.utils.leaks;
import android.app.admin.DeviceAdminInfo;
+import android.content.ComponentName;
import android.graphics.drawable.Drawable;
import android.testing.LeakCheck;
@@ -68,6 +69,16 @@
}
@Override
+ public ComponentName getDeviceOwnerComponentOnAnyUser() {
+ return null;
+ }
+
+ @Override
+ public int getDeviceOwnerType(ComponentName admin) {
+ return 0;
+ }
+
+ @Override
public boolean isNetworkLoggingEnabled() {
return false;
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 1e49071..d0aa28b 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -4251,12 +4251,6 @@
}
mPowerManager.wakeUp(wakeTime, reason, details);
-
- // Turn on the connected TV and switch HDMI input if we're a HDMI playback device.
- final HdmiControl hdmiControl = getHdmiControl();
- if (hdmiControl != null) {
- hdmiControl.turnOnTv();
- }
return true;
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 370d921..75f06e5 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -90,7 +90,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.EventLog;
-import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
@@ -2447,8 +2446,8 @@
@Override
public void removeOnLocalColorsChangedListener(
- @NonNull ILocalWallpaperColorConsumer callback, int which, int userId,
- int displayId) throws RemoteException {
+ @NonNull ILocalWallpaperColorConsumer callback, List<RectF> removeAreas, int which,
+ int userId, int displayId) throws RemoteException {
if (which != FLAG_LOCK && which != FLAG_SYSTEM) {
throw new IllegalArgumentException("which should be either FLAG_LOCK or FLAG_SYSTEM");
}
@@ -2457,43 +2456,45 @@
throw new SecurityException("calling user id does not match");
}
final long identity = Binder.clearCallingIdentity();
- ArrayList<RectF> removeAreas = new ArrayList<>();
- ArrayList<Pair<RemoteCallbackList, ILocalWallpaperColorConsumer>>
- callbacksToRemove = new ArrayList<>();
+ ArrayList<RectF> purgeAreas = new ArrayList<>();
+ IBinder binder = callback.asBinder();
try {
synchronized (mLock) {
- ArraySet<RectF> areas = mLocalColorCallbackAreas.remove(callback.asBinder());
- mLocalColorCallbackDisplayId.remove(callback.asBinder());
- if (areas == null) areas = new ArraySet<>();
- for (RectF area : areas) {
- RemoteCallbackList callbacks = mLocalColorAreaCallbacks.get(area);
- if (callbacks == null) continue;
- callbacksToRemove.add(new Pair<>(callbacks, callback));
- if (callbacks.getRegisteredCallbackCount() == 0) {
- mLocalColorAreaCallbacks.remove(area);
- removeAreas.add(area);
- }
- ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId);
- if (displayAreas != null) {
- displayAreas.remove(area);
+ ArraySet<RectF> currentAreas = mLocalColorCallbackAreas.get(binder);
+ if (currentAreas == null) return;
+ currentAreas.removeAll(removeAreas);
+ if (currentAreas.size() == 0) {
+ mLocalColorCallbackDisplayId.remove(binder);
+ for (RectF removeArea : removeAreas) {
+ RemoteCallbackList<ILocalWallpaperColorConsumer> remotes =
+ mLocalColorAreaCallbacks.get(removeArea);
+ if (remotes == null) continue;
+ remotes.unregister(callback);
+ if (remotes.getRegisteredCallbackCount() == 0) {
+ mLocalColorAreaCallbacks.remove(removeArea);
+ purgeAreas.add(removeArea);
+ ArraySet<RectF> displayAreas = mLocalColorDisplayIdAreas.get(displayId);
+ if (displayAreas != null) {
+ displayAreas.remove(removeArea);
+ if (displayAreas.size() == 0) {
+ mLocalColorDisplayIdAreas.remove(displayId);
+ }
+ }
+ }
}
}
}
- for (int i = 0; i < callbacksToRemove.size(); i++) {
- Pair<RemoteCallbackList, ILocalWallpaperColorConsumer>
- pair = callbacksToRemove.get(i);
- pair.first.unregister(pair.second);
- }
+
} catch (Exception e) {
// ignore any exception
} finally {
Binder.restoreCallingIdentity(identity);
}
- if (removeAreas.size() == 0) return;
+ if (purgeAreas.size() == 0) return;
IWallpaperEngine engine = getEngine(which, userId, displayId);
if (engine == null) return;
- engine.removeLocalColorsAreas(removeAreas);
+ engine.removeLocalColorsAreas(purgeAreas);
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 89dac05..5891748 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1081,11 +1081,16 @@
pw.println(prefix + "supportsEnterPipOnTaskSwitch: "
+ supportsEnterPipOnTaskSwitch);
}
- if (info.maxAspectRatio != 0) {
- pw.println(prefix + "maxAspectRatio=" + info.maxAspectRatio);
+ if (info.getMaxAspectRatio() != 0) {
+ pw.println(prefix + "maxAspectRatio=" + info.getMaxAspectRatio());
}
- if (info.minAspectRatio != 0) {
- pw.println(prefix + "minAspectRatio=" + info.minAspectRatio);
+ if (info.getMinAspectRatio() != 0) {
+ pw.println(prefix + "minAspectRatio=" + info.getMinAspectRatio());
+ }
+ if (info.getMinAspectRatio() != info.getManifestMinAspectRatio()) {
+ // Log the fact that we've overridden the min aspect ratio from the manifest
+ pw.println(prefix + "manifestMinAspectRatio="
+ + info.getManifestMinAspectRatio());
}
pw.println(prefix + "supportsSizeChanges="
+ ActivityInfo.sizeChangesSupportModeToString(info.supportsSizeChanges()));
@@ -7055,8 +7060,8 @@
// Adjust the fixed orientation letterbox bounds to fit the app request aspect ratio in
// order to use the extra available space.
- final float maxAspectRatio = info.maxAspectRatio;
- final float minAspectRatio = info.minAspectRatio;
+ final float maxAspectRatio = info.getMaxAspectRatio();
+ final float minAspectRatio = info.getMinAspectRatio();
if (aspect > maxAspectRatio && maxAspectRatio != 0) {
aspect = maxAspectRatio;
} else if (aspect < minAspectRatio) {
@@ -7293,21 +7298,21 @@
// The rest of the condition is that only one side is smaller than the container, but it
// still needs to exclude the cases where the size is limited by the fixed aspect ratio.
- if (info.maxAspectRatio > 0) {
+ if (info.getMaxAspectRatio() > 0) {
final float aspectRatio = (0.5f + Math.max(appWidth, appHeight))
/ Math.min(appWidth, appHeight);
- if (aspectRatio >= info.maxAspectRatio) {
+ if (aspectRatio >= info.getMaxAspectRatio()) {
// The current size has reached the max aspect ratio.
return false;
}
}
- if (info.minAspectRatio > 0) {
+ if (info.getMinAspectRatio() > 0) {
// The activity should have at least the min aspect ratio, so this checks if the
// container still has available space to provide larger aspect ratio.
final float containerAspectRatio =
(0.5f + Math.max(containerAppWidth, containerAppHeight))
/ Math.min(containerAppWidth, containerAppHeight);
- if (containerAspectRatio <= info.minAspectRatio) {
+ if (containerAspectRatio <= info.getMinAspectRatio()) {
// The long side has reached the parent.
return false;
}
@@ -7474,9 +7479,9 @@
// TODO(b/36505427): Consider moving this method and similar ones to ConfigurationContainer.
private void applyAspectRatio(Rect outBounds, Rect containingAppBounds,
Rect containingBounds) {
- final float maxAspectRatio = info.maxAspectRatio;
+ final float maxAspectRatio = info.getMaxAspectRatio();
final Task rootTask = getRootTask();
- final float minAspectRatio = info.minAspectRatio;
+ final float minAspectRatio = info.getMinAspectRatio();
if (task == null || rootTask == null
|| (inMultiWindowMode() && !shouldCreateCompatDisplayInsets())
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9f60878..c78f9ec 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -5661,16 +5661,18 @@
}
return false; /* continue */
}
- if (taskId != INVALID_TASK_ID) {
+ if (taskId == INVALID_TASK_ID) {
+ if (!nextWindow.canReceiveKeys()) {
+ return false; /* continue */
+ }
+ } else {
Task task = nextWindow.getTask();
if (task == null || !task.isTaskId(taskId)) {
return false; /* continue */
}
}
- if (!nextWindow.canReceiveKeys()) {
- return false; /* continue */
- }
- return true; /* stop */
+
+ return true; /* stop, match found */
}
});
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 73b0555..0830269 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -231,7 +231,7 @@
import android.view.IPinnedTaskListener;
import android.view.IRecentsAnimationRunner;
import android.view.IRotationWatcher;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.ISystemGestureExclusionListener;
import android.view.IWallpaperVisibilityListener;
import android.view.IWindow;
@@ -7145,10 +7145,10 @@
* @param displayId the display for the request
* @param behindClient token for a window, used to filter the search to windows behind it
* @param taskId specifies the id of a task the result must belong to or -1 to match any task
- * @param callbacks to receive responses
+ * @param listener to receive the response
*/
public void requestScrollCapture(int displayId, @Nullable IBinder behindClient, int taskId,
- IScrollCaptureCallbacks callbacks) {
+ IScrollCaptureResponseListener listener) {
if (!checkCallingPermission(READ_FRAME_BUFFER, "requestScrollCapture()")) {
throw new SecurityException("Requires READ_FRAME_BUFFER permission");
}
@@ -7161,7 +7161,7 @@
ProtoLog.e(WM_ERROR,
"Invalid displayId for requestScrollCapture: %d", displayId);
responseBuilder.setDescription(String.format("bad displayId: %d", displayId));
- callbacks.onScrollCaptureResponse(responseBuilder.build());
+ listener.onScrollCaptureResponse(responseBuilder.build());
return;
}
WindowState topWindow = null;
@@ -7171,19 +7171,19 @@
WindowState targetWindow = dc.findScrollCaptureTargetWindow(topWindow, taskId);
if (targetWindow == null) {
responseBuilder.setDescription("findScrollCaptureTargetWindow returned null");
- callbacks.onScrollCaptureResponse(responseBuilder.build());
+ listener.onScrollCaptureResponse(responseBuilder.build());
return;
}
try {
// Forward to the window for handling, which will respond using the callback.
- targetWindow.mClient.requestScrollCapture(callbacks);
+ targetWindow.mClient.requestScrollCapture(listener);
} catch (RemoteException e) {
ProtoLog.w(WM_ERROR,
"requestScrollCapture: caught exception dispatching to window."
+ "token=%s", targetWindow.mClient.asBinder());
responseBuilder.setWindowTitle(targetWindow.getName());
responseBuilder.setDescription(String.format("caught exception: %s", e));
- callbacks.onScrollCaptureResponse(responseBuilder.build());
+ listener.onScrollCaptureResponse(responseBuilder.build());
}
}
} catch (RemoteException e) {
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
index 87b2c84..4c5bbeb 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/MusicRecognitionManagerPerUserService.java
@@ -48,6 +48,8 @@
import java.io.IOException;
import java.io.OutputStream;
import java.util.Objects;
+import java.util.concurrent.CompletableFuture;
+
/**
* Handles per-user requests received by
@@ -60,6 +62,11 @@
implements RemoteMusicRecognitionService.Callbacks {
private static final String TAG = MusicRecognitionManagerPerUserService.class.getSimpleName();
+ private static final String MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG =
+ "MusicRecognitionManagerService";
+ private static final String KEY_MUSIC_RECOGNITION_SERVICE_ATTRIBUTION_TAG =
+ "android.media.musicrecognition.attributiontag";
+
// Number of bytes per sample of audio (which is a short).
private static final int BYTES_PER_SAMPLE = 2;
private static final int MAX_STREAMING_SECONDS = 24;
@@ -68,18 +75,24 @@
@GuardedBy("mLock")
private RemoteMusicRecognitionService mRemoteService;
private final AppOpsManager mAppOpsManager;
+ private final String mAttributionMessage;
- private String mAttributionTag;
- private String mAttributionMessage;
+ // Service info of the remote MusicRecognitionService (which the audio gets forwarded to).
private ServiceInfo mServiceInfo;
+ private CompletableFuture<String> mAttributionTagFuture;
MusicRecognitionManagerPerUserService(
@NonNull MusicRecognitionManagerService primary,
@NonNull Object lock, int userId) {
super(primary, lock, userId);
- mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
+
+ // When attributing audio-access, this establishes that audio access is performed by
+ // MusicRecognitionManager (on behalf of the receiving service, whose attribution tag,
+ // provided by mAttributionTagFuture, is used for the actual calls to startProxyOp(...).
+ mAppOpsManager = getContext().createAttributionContext(
+ MUSIC_RECOGNITION_MANAGER_ATTRIBUTION_TAG).getSystemService(AppOpsManager.class);
mAttributionMessage = String.format("MusicRecognitionManager.invokedByUid.%s", userId);
- mAttributionTag = null;
+ mAttributionTagFuture = null;
mServiceInfo = null;
}
@@ -126,10 +139,13 @@
new MusicRecognitionServiceCallback(clientCallback),
mMaster.isBindInstantServiceAllowed(),
mMaster.verbose);
+
try {
mServiceInfo =
getContext().getPackageManager().getServiceInfo(
- mRemoteService.getComponentName(), 0);
+ mRemoteService.getComponentName(), PackageManager.GET_META_DATA);
+ mAttributionTagFuture = mRemoteService.getAttributionTag();
+ Slog.i(TAG, "Remote service bound: " + mRemoteService.getComponentName());
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Service was not found.", e);
}
@@ -172,11 +188,13 @@
ParcelFileDescriptor audioSink = clientPipe.second;
ParcelFileDescriptor clientRead = clientPipe.first;
- mMaster.mExecutorService.execute(() -> {
- streamAudio(recognitionRequest, clientCallback, audioSink);
- });
+ mAttributionTagFuture.thenAcceptAsync(
+ tag -> {
+ streamAudio(tag, recognitionRequest, clientCallback, audioSink);
+ }, mMaster.mExecutorService);
+
// Send the pipe down to the lookup service while we write to it asynchronously.
- mRemoteService.writeAudioToPipe(clientRead, recognitionRequest.getAudioFormat());
+ mRemoteService.onAudioStreamStarted(clientRead, recognitionRequest.getAudioFormat());
}
/**
@@ -186,10 +204,12 @@
* @param clientCallback the callback to notify on errors.
* @param audioSink the sink to which to stream audio to.
*/
- private void streamAudio(@NonNull RecognitionRequest recognitionRequest,
- IMusicRecognitionManagerCallback clientCallback, ParcelFileDescriptor audioSink) {
+ private void streamAudio(@Nullable String attributionTag,
+ @NonNull RecognitionRequest recognitionRequest,
+ IMusicRecognitionManagerCallback clientCallback,
+ ParcelFileDescriptor audioSink) {
try {
- startRecordAudioOp();
+ startRecordAudioOp(attributionTag);
} catch (SecurityException e) {
// A security exception can occur if the MusicRecognitionService (receiving the audio)
// does not (or does no longer) hold the necessary permissions to record audio.
@@ -214,7 +234,7 @@
Slog.e(TAG, "Audio streaming stopped.", e);
} finally {
audioRecord.release();
- finishRecordAudioOp();
+ finishRecordAudioOp(attributionTag);
try {
clientCallback.onAudioStreamClosed();
} catch (RemoteException ignored) {
@@ -323,23 +343,32 @@
* Tracks that the RECORD_AUDIO operation started (attributes it to the service receiving the
* audio).
*/
- private void startRecordAudioOp() {
- mAppOpsManager.startProxyOp(
+ private void startRecordAudioOp(@Nullable String attributionTag) {
+ int status = mAppOpsManager.startProxyOp(
Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)),
mServiceInfo.applicationInfo.uid,
mServiceInfo.packageName,
- mAttributionTag,
+ attributionTag,
mAttributionMessage);
+ // The above should already throw a SecurityException. This is just a fallback.
+ if (status != AppOpsManager.MODE_ALLOWED) {
+ throw new SecurityException(String.format(
+ "Failed to obtain RECORD_AUDIO permission (status: %d) for "
+ + "receiving service: %s", status, mServiceInfo.getComponentName()));
+ }
+ Slog.i(TAG, String.format(
+ "Starting audio streaming. Attributing to %s (%d) with tag '%s'",
+ mServiceInfo.packageName, mServiceInfo.applicationInfo.uid, attributionTag));
}
/** Tracks that the RECORD_AUDIO operation finished. */
- private void finishRecordAudioOp() {
+ private void finishRecordAudioOp(@Nullable String attributionTag) {
mAppOpsManager.finishProxyOp(
Objects.requireNonNull(AppOpsManager.permissionToOp(RECORD_AUDIO)),
mServiceInfo.applicationInfo.uid,
mServiceInfo.packageName,
- mAttributionTag);
+ attributionTag);
}
/** Establishes an audio stream from the DSP audio source. */
diff --git a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
index 6c7d673..99b4482 100644
--- a/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
+++ b/services/musicrecognition/java/com/android/server/musicrecognition/RemoteMusicRecognitionService.java
@@ -20,15 +20,20 @@
import android.content.ComponentName;
import android.content.Context;
import android.media.AudioFormat;
+import android.media.musicrecognition.IMusicRecognitionAttributionTagCallback;
import android.media.musicrecognition.IMusicRecognitionService;
import android.media.musicrecognition.MusicRecognitionService;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
import android.text.format.DateUtils;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.server.musicrecognition.MusicRecognitionManagerPerUserService.MusicRecognitionServiceCallback;
+import java.util.concurrent.CompletableFuture;
+
+
/** Remote connection to an instance of {@link MusicRecognitionService}. */
public class RemoteMusicRecognitionService extends
AbstractMultiplePendingRequestsRemoteService<RemoteMusicRecognitionService,
@@ -81,9 +86,26 @@
* Sends the given descriptor to the app's {@link MusicRecognitionService} to read the
* audio.
*/
- public void writeAudioToPipe(@NonNull ParcelFileDescriptor fd,
+ public void onAudioStreamStarted(@NonNull ParcelFileDescriptor fd,
@NonNull AudioFormat audioFormat) {
scheduleAsyncRequest(
binder -> binder.onAudioStreamStarted(fd, audioFormat, mServerCallback));
}
+
+
+ /**
+ * Returns the name of the <attribution> tag defined in the remote service's manifest.
+ */
+ public CompletableFuture<String> getAttributionTag() {
+ CompletableFuture<String> attributionTagFuture = new CompletableFuture<String>();
+ scheduleAsyncRequest(
+ binder -> binder.getAttributionTag(
+ new IMusicRecognitionAttributionTagCallback.Stub() {
+ @Override
+ public void onAttributionTag(String tag) throws RemoteException {
+ attributionTagFuture.complete(tag);
+ }
+ }));
+ return attributionTagFuture;
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index f5d831b..67fe7bf 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -117,6 +117,7 @@
import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
+import android.view.View;
import android.view.WindowManager;
import androidx.test.filters.SmallTest;
@@ -1699,6 +1700,20 @@
}
@Test
+ public void testFindScrollCaptureTargetWindow_cantReceiveKeys() {
+ DisplayContent display = createNewDisplay();
+ Task stack = createTaskStackOnDisplay(display);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+ WindowState activityWindow = createAppWindow(task, TYPE_APPLICATION, "App Window");
+ WindowState invisible = createWindow(null, TYPE_APPLICATION, "invisible");
+ invisible.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false
+
+ WindowState result = display.findScrollCaptureTargetWindow(null,
+ ActivityTaskManager.INVALID_TASK_ID);
+ assertEquals(activityWindow, result);
+ }
+
+ @Test
public void testFindScrollCaptureTargetWindow_taskId() {
DisplayContent display = createNewDisplay();
Task stack = createTaskStackOnDisplay(display);
@@ -1711,6 +1726,19 @@
}
@Test
+ public void testFindScrollCaptureTargetWindow_taskIdCantReceiveKeys() {
+ DisplayContent display = createNewDisplay();
+ Task stack = createTaskStackOnDisplay(display);
+ Task task = createTaskInStack(stack, 0 /* userId */);
+ WindowState window = createAppWindow(task, TYPE_APPLICATION, "App Window");
+ window.mViewVisibility = View.INVISIBLE; // make canReceiveKeys return false
+ WindowState behindWindow = createWindow(null, TYPE_SCREENSHOT, display, "Screenshot");
+
+ WindowState result = display.findScrollCaptureTargetWindow(null, task.mTaskId);
+ assertEquals(window, result);
+ }
+
+ @Test
public void testEnsureActivitiesVisibleNotRecursive() {
final TaskDisplayArea mockTda = mock(TaskDisplayArea.class);
final boolean[] called = { false };
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 36cf9c9..5c7e580 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -161,7 +161,8 @@
final Rect displayBounds = mActivity.mDisplayContent.getWindowConfiguration().getBounds();
final float aspectRatio = 1.2f;
- mActivity.info.minAspectRatio = mActivity.info.maxAspectRatio = aspectRatio;
+ mActivity.info.setMaxAspectRatio(aspectRatio);
+ mActivity.info.setMinAspectRatio(aspectRatio);
prepareUnresizable(mActivity, -1f, SCREEN_ORIENTATION_UNSPECIFIED);
final Rect appBounds = mActivity.getWindowConfiguration().getAppBounds();
@@ -780,6 +781,139 @@
}
@Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testOverrideMinAspectRatioMedium() {
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 3:2 aspect ratio
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1200 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testOverrideMinAspectRatioLowerThanManifest() {
+ setUpDisplaySizeWithApp(1400, 1600);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setMinAspectRatio(2f)
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override should have no effect, because the manifest aspect ratio is
+ // larger (2:1)
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(800, activity.getBounds().width());
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testOverrideMinAspectRatioLargerThanManifest() {
+ setUpDisplaySizeWithApp(1400, 1600);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setMinAspectRatio(1.1f)
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override should have no effect, because the manifest aspect ratio is
+ // larger (2:1)
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testOverrideMinAspectRatioLarge() {
+ setUpDisplaySizeWithApp(1500, 1600);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 16:9 aspect ratio
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM,
+ ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE})
+ public void testOverrideMinAspectRatio_Both() {
+ // If multiple override aspect ratios are set, we should use the largest one
+
+ setUpDisplaySizeWithApp(1400, 1600);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override forces the activity into a 16:9 aspect ratio
+ assertEquals(1600, activity.getBounds().height());
+ assertEquals(1600 / ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_LARGE_VALUE,
+ activity.getBounds().width(), 0.5);
+ }
+
+ @Test
+ @EnableCompatChanges({ActivityInfo.OVERRIDE_MIN_ASPECT_RATIO_MEDIUM})
+ public void testOverrideMinAspectRatioWithoutGlobalOverride() {
+ // In this test, only OVERRIDE_MIN_ASPECT_RATIO_1_5 is set, which has no effect without
+ // OVERRIDE_MIN_ASPECT_RATIO being also set.
+
+ setUpDisplaySizeWithApp(1000, 1200);
+
+ // Create a size compat activity on the same task.
+ final ActivityRecord activity = new ActivityBuilder(mAtm)
+ .setTask(mTask)
+ .setScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT)
+ .setComponent(ComponentName.createRelative(mContext,
+ SizeCompatTests.class.getName()))
+ .setUid(android.os.Process.myUid())
+ .build();
+
+ // The per-package override should have no effect
+ assertEquals(1200, activity.getBounds().height());
+ assertEquals(1000, activity.getBounds().width());
+ }
+
+ @Test
public void testLaunchWithFixedRotationTransform() {
final int dw = 1000;
final int dh = 2500;
@@ -872,7 +1006,7 @@
// Portrait fixed app with min aspect ratio higher that aspect ratio override for fixed
// orientation letterbox.
mActivity.mWmService.setFixedOrientationLetterboxAspectRatio(1.1f);
- mActivity.info.minAspectRatio = 3;
+ mActivity.info.setMinAspectRatio(3);
prepareUnresizable(mActivity, /* maxAspect= */ 0, SCREEN_ORIENTATION_PORTRAIT);
final Rect displayBounds = new Rect(mActivity.mDisplayContent.getBounds());
@@ -889,7 +1023,8 @@
// Activity bounds should respect minimum aspect ratio for activity.
assertEquals(displayBounds.height(), activityBounds.height());
- assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.minAspectRatio),
+ assertEquals((int) Math.rint(displayBounds.height()
+ / mActivity.info.getManifestMinAspectRatio()),
activityBounds.width());
}
@@ -918,7 +1053,8 @@
// Activity bounds should respect maximum aspect ratio for activity.
assertEquals(displayBounds.height(), activityBounds.height());
- assertEquals((int) Math.rint(displayBounds.height() / mActivity.info.maxAspectRatio),
+ assertEquals((int) Math.rint(displayBounds.height()
+ / mActivity.info.getMaxAspectRatio()),
activityBounds.width());
}
@@ -1098,7 +1234,8 @@
assertTrue(mActivity.isLetterboxedForFixedOrientationAndAspectRatio());
assertFalse(newActivity.inSizeCompatMode());
assertEquals(displayBounds.height(), newActivityBounds.height());
- assertEquals((long) Math.rint(newActivityBounds.height() / newActivity.info.maxAspectRatio),
+ assertEquals((long) Math.rint(newActivityBounds.height()
+ / newActivity.info.getMaxAspectRatio()),
newActivityBounds.width());
}
@@ -1347,7 +1484,7 @@
: RESIZE_MODE_RESIZEABLE;
activity.mVisibleRequested = true;
if (maxAspect >= 0) {
- activity.info.maxAspectRatio = maxAspect;
+ activity.info.setMaxAspectRatio(maxAspect);
}
if (screenOrientation != SCREEN_ORIENTATION_UNSPECIFIED) {
activity.info.screenOrientation = screenOrientation;
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
index cac6965..21536a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestDisplayContent.java
@@ -136,7 +136,10 @@
final Display display = new Display(DisplayManagerGlobal.getInstance(), displayId,
mInfo, DEFAULT_DISPLAY_ADJUSTMENTS);
final TestDisplayContent newDisplay = createInternal(display);
-
+ // Ensure letterbox aspect ratio is not overridden on any device target.
+ // {@link com.android.internal.R.dimen.config_taskLetterboxAspectRatio}, provided by
+ // the below method, is set on some device form factors.
+ mService.mWindowManager.setFixedOrientationLetterboxAspectRatio(0);
// disable the normal system decorations
final DisplayPolicy displayPolicy = newDisplay.getDisplayPolicy();
spyOn(displayPolicy);
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 001afe3..b3a0745 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -22,10 +22,11 @@
import android.os.RemoteException;
import android.util.MergedConfiguration;
import android.view.DragEvent;
-import android.view.IScrollCaptureCallbacks;
+import android.view.IScrollCaptureResponseListener;
import android.view.IWindow;
import android.view.InsetsSourceControl;
import android.view.InsetsState;
+import android.view.ScrollCaptureResponse;
import android.window.ClientWindowFrames;
import com.android.internal.os.IResultReceiver;
@@ -116,7 +117,15 @@
}
@Override
- public void requestScrollCapture(IScrollCaptureCallbacks callbacks) throws RemoteException {
+ public void requestScrollCapture(IScrollCaptureResponseListener listener)
+ throws RemoteException {
+ try {
+ listener.onScrollCaptureResponse(
+ new ScrollCaptureResponse.Builder().setDescription("Not Implemented").build());
+
+ } catch (RemoteException ex) {
+ // ignore
+ }
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
index 4a7784c..779457b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -714,6 +714,7 @@
private int mLaunchMode;
private int mResizeMode = RESIZE_MODE_RESIZEABLE;
private float mMaxAspectRatio;
+ private float mMinAspectRatio;
private boolean mSupportsSizeChanges;
private int mScreenOrientation = SCREEN_ORIENTATION_UNSPECIFIED;
private boolean mLaunchTaskBehind = false;
@@ -793,6 +794,11 @@
return this;
}
+ ActivityBuilder setMinAspectRatio(float minAspectRatio) {
+ mMinAspectRatio = minAspectRatio;
+ return this;
+ }
+
ActivityBuilder setSupportsSizeChanges(boolean supportsSizeChanges) {
mSupportsSizeChanges = supportsSizeChanges;
return this;
@@ -884,7 +890,8 @@
aInfo.flags |= mActivityFlags;
aInfo.launchMode = mLaunchMode;
aInfo.resizeMode = mResizeMode;
- aInfo.maxAspectRatio = mMaxAspectRatio;
+ aInfo.setMaxAspectRatio(mMaxAspectRatio);
+ aInfo.setMinAspectRatio(mMinAspectRatio);
aInfo.supportsSizeChanges = mSupportsSizeChanges;
aInfo.screenOrientation = mScreenOrientation;
aInfo.configChanges |= mConfigChanges;
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerService.java b/services/translation/java/com/android/server/translation/TranslationManagerService.java
index 72e1e33..8874e0a 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerService.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerService.java
@@ -27,7 +27,6 @@
import android.content.pm.PackageManager;
import android.os.Binder;
import android.os.IBinder;
-import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
@@ -204,28 +203,6 @@
}
}
- @Override
- public void registerUiTranslationStateCallback(IRemoteCallback callback, int userId) {
- TranslationManagerServiceImpl service;
- synchronized (mLock) {
- service = getServiceForUserLocked(userId);
- }
- if (service != null) {
- service.registerUiTranslationStateCallback(callback, Binder.getCallingUid());
- }
- }
-
- @Override
- public void unregisterUiTranslationStateCallback(IRemoteCallback callback, int userId) {
- TranslationManagerServiceImpl service;
- synchronized (mLock) {
- service = getServiceForUserLocked(userId);
- }
- if (service != null) {
- service.unregisterUiTranslationStateCallback(callback);
- }
- }
-
/**
* Dump the service state into the given stream. You run "adb shell dumpsys translation".
*/
diff --git a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
index 1ca07cb..ab6ac12 100644
--- a/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
+++ b/services/translation/java/com/android/server/translation/TranslationManagerServiceImpl.java
@@ -17,24 +17,17 @@
package com.android.server.translation;
import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_SUCCESS;
-import static android.view.translation.UiTranslationManager.EXTRA_SOURCE_LOCALE;
-import static android.view.translation.UiTranslationManager.EXTRA_STATE;
-import static android.view.translation.UiTranslationManager.EXTRA_TARGET_LOCALE;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
-import android.os.Bundle;
import android.os.IBinder;
-import android.os.IRemoteCallback;
-import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.service.translation.TranslationServiceInfo;
import android.util.Slog;
import android.view.autofill.AutofillId;
-import android.view.inputmethod.InputMethodInfo;
import android.view.translation.TranslationSpec;
import android.view.translation.UiTranslationManager.UiTranslationState;
@@ -43,7 +36,6 @@
import com.android.internal.util.SyncResultReceiver;
import com.android.server.LocalServices;
import com.android.server.infra.AbstractPerUserSystemService;
-import com.android.server.inputmethod.InputMethodManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal;
import com.android.server.wm.ActivityTaskManagerInternal.ActivityTokens;
@@ -182,50 +174,5 @@
} catch (RemoteException e) {
Slog.w(TAG, "Update UiTranslationState fail: " + e);
}
- invokeCallbacks(state, sourceSpec, destSpec);
}
-
- private void invokeCallbacks(
- int state, TranslationSpec sourceSpec, TranslationSpec targetSpec) {
- Bundle res = new Bundle();
- res.putInt(EXTRA_STATE, state);
- // TODO(177500482): Store the locale pair so it can be sent for RESUME events.
- if (sourceSpec != null) {
- res.putString(EXTRA_SOURCE_LOCALE, sourceSpec.getLanguage());
- res.putString(EXTRA_TARGET_LOCALE, targetSpec.getLanguage());
- }
- // TODO(177500482): Only support the *current* Input Method.
- List<InputMethodInfo> enabledInputMethods =
- LocalServices.getService(InputMethodManagerInternal.class)
- .getEnabledInputMethodListAsUser(mUserId);
- mCallbacks.broadcast((callback, uid) -> {
- // Code here is non-optimal since it's temporary..
- boolean isIme = false;
- for (InputMethodInfo inputMethod : enabledInputMethods) {
- if ((int) uid == inputMethod.getServiceInfo().applicationInfo.uid) {
- isIme = true;
- }
- }
- // TODO(177500482): Invoke it for the application being translated too.
- if (!isIme) {
- return;
- }
- try {
- callback.sendResult(res);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed to invoke UiTranslationStateCallback: " + e);
- }
- });
- }
-
- public void registerUiTranslationStateCallback(IRemoteCallback callback, int sourceUid) {
- mCallbacks.register(callback, sourceUid);
- // TODO(177500482): trigger the callback here if we're already translating the UI.
- }
-
- public void unregisterUiTranslationStateCallback(IRemoteCallback callback) {
- mCallbacks.unregister(callback);
- }
-
- private final RemoteCallbackList<IRemoteCallback> mCallbacks = new RemoteCallbackList<>();
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
index a8b7b05..e6a4501 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -35,15 +35,6 @@
}
}
-@JvmOverloads
-fun FlickerTestParameter.visibleWindowsShownMoreThanOneConsecutiveEntry(
- ignoreWindows: List<String> = emptyList()
-) {
- assertWm {
- this.visibleWindowsShownMoreThanOneConsecutiveEntry(ignoreWindows)
- }
-}
-
fun FlickerTestParameter.launcherReplacesAppWindowAsTopWindow(testApp: IAppHelper) {
assertWm {
this.showsAppWindowOnTop(testApp.getPackage())
@@ -184,15 +175,6 @@
}
}
-@JvmOverloads
-fun FlickerTestParameter.visibleLayersShownMoreThanOneConsecutiveEntry(
- ignoreLayers: List<String> = emptyList()
-) {
- assertLayers {
- this.visibleLayersShownMoreThanOneConsecutiveEntry(ignoreLayers)
- }
-}
-
fun FlickerTestParameter.appLayerReplacesWallpaperLayer(appName: String) {
assertLayers {
this.isVisible(WALLPAPER_TITLE)
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
index fef49d9..d669a01 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppTransition.kt
@@ -36,8 +36,6 @@
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.wallpaperLayerReplacesAppLayer
import com.android.server.wm.flicker.wallpaperWindowBecomesVisible
import org.junit.Assume
@@ -122,13 +120,17 @@
@FlakyTest(bugId = 173689015)
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
}
@FlakyTest(bugId = 173689015)
@Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
}
@Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
index 0dcc8c9..aad06ee 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToAppTest.kt
@@ -32,8 +32,6 @@
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -43,6 +41,7 @@
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -100,8 +99,13 @@
@Postsubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ }
+ }
@Postsubmit
@Test
@@ -157,8 +161,11 @@
@FlakyTest
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
index 7a1bb11..d08e7e2e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeAutoOpenWindowToHomeTest.kt
@@ -31,8 +31,6 @@
import com.android.server.wm.flicker.helpers.ImeAppAutoFocusHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.helpers.wakeUpAndGoToHomeScreen
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
@@ -42,6 +40,7 @@
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -100,8 +99,13 @@
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ }
+ }
@Presubmit
@Test
@@ -164,14 +168,20 @@
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry() {
Assume.assumeFalse(testSpec.isRotated)
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+ }
}
@FlakyTest
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() {
Assume.assumeTrue(testSpec.isRotated)
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+ }
}
companion object {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
index 2d09d23..19dec7a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToAppTest.kt
@@ -34,12 +34,11 @@
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
import com.android.server.wm.flicker.noUncoveredRegions
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -95,8 +94,13 @@
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ }
+ }
@Presubmit
@Test
@@ -126,8 +130,11 @@
@Presubmit
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ }
@Presubmit
@Test
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 73760c5..b214414 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -39,8 +39,7 @@
import com.android.server.wm.flicker.startRotation
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
+import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -102,8 +101,13 @@
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE,
+ WindowManagerStateHelper.SPLASH_SCREEN_NAME,
+ WindowManagerStateHelper.SNAPSHOT_WINDOW_NAME))
+ }
+ }
@Presubmit
@Test
@@ -155,8 +159,12 @@
@FlakyTest
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ listOf(IME_WINDOW_TITLE, WindowManagerStateHelper.SPLASH_SCREEN_NAME))
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
index 2815e05..ab5e9b4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/OpenImeWindowTest.kt
@@ -33,8 +33,6 @@
import com.android.server.wm.flicker.navBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.navBarLayerRotatesAndScales
import com.android.server.wm.flicker.navBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.appWindowAlwaysVisibleOnTop
import com.android.server.wm.flicker.dsl.FlickerBuilder
@@ -156,13 +154,19 @@
@FlakyTest
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ }
@FlakyTest
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
index 7ca985e..52a8266 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ReOpenImeWindowTest.kt
@@ -37,8 +37,6 @@
import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
import com.android.server.wm.flicker.appLayerReplacesWallpaperLayer
import com.android.server.wm.flicker.dsl.FlickerBuilder
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.noUncoveredRegions
import com.android.server.wm.flicker.repetitions
import com.android.server.wm.flicker.startRotation
@@ -108,8 +106,11 @@
@Presubmit
@Test
- fun visibleWindowsShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
+ }
@Presubmit
@Test
@@ -177,8 +178,11 @@
@FlakyTest
@Test
- fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ }
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index fee01d2..5d015d9 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -23,8 +23,6 @@
import com.android.server.wm.flicker.FlickerParametersRunnerFactory
import com.android.server.wm.flicker.FlickerTestParameter
import com.android.server.wm.flicker.FlickerTestParameterFactory
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.helpers.reopenAppFromOverview
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.startRotation
@@ -115,7 +113,7 @@
@Test
fun visibleWindowsShownMoreThanOneConsecutiveEntry_Flaky() {
Assume.assumeTrue(testSpec.isRotated)
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ super.visibleWindowsShownMoreThanOneConsecutiveEntry()
}
@Presubmit
@@ -129,7 +127,7 @@
@Test
fun visibleLayersShownMoreThanOneConsecutiveEntry_Flaky() {
Assume.assumeTrue(testSpec.isRotated)
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
}
companion object {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
index e9f0534..5197f0e 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppTransition.kt
@@ -40,8 +40,6 @@
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import com.android.server.wm.flicker.wallpaperWindowBecomesInvisible
import org.junit.Assume
import org.junit.Test
@@ -128,13 +126,17 @@
@Presubmit
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
}
@FlakyTest
@Test
open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
}
@Presubmit
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
index 9d78eb8..24417f4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/rotation/RotationTransition.kt
@@ -38,8 +38,6 @@
import com.android.server.wm.flicker.statusBarLayerIsAlwaysVisible
import com.android.server.wm.flicker.statusBarLayerRotatesScales
import com.android.server.wm.flicker.statusBarWindowIsAlwaysVisible
-import com.android.server.wm.flicker.visibleLayersShownMoreThanOneConsecutiveEntry
-import com.android.server.wm.flicker.visibleWindowsShownMoreThanOneConsecutiveEntry
import org.junit.Test
abstract class RotationTransition(protected val testSpec: FlickerTestParameter) {
@@ -117,13 +115,18 @@
@FlakyTest(bugId = 140855415)
@Test
- open fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- testSpec.visibleLayersShownMoreThanOneConsecutiveEntry()
+ open fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ testSpec.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+ }
@Presubmit
@Test
open fun visibleWindowsShownMoreThanOneConsecutiveEntry() {
- testSpec.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ testSpec.assertWm {
+ this.visibleWindowsShownMoreThanOneConsecutiveEntry()
+ }
}
@Presubmit