diff options
18 files changed, 209 insertions, 1151 deletions
diff --git a/core/java/android/view/AttachedSurfaceControl.java b/core/java/android/view/AttachedSurfaceControl.java index f28574ecb3b2..fd5517d29d74 100644 --- a/core/java/android/view/AttachedSurfaceControl.java +++ b/core/java/android/view/AttachedSurfaceControl.java @@ -27,6 +27,9 @@ import android.window.SurfaceSyncGroup; import com.android.window.flags.Flags; +import java.util.concurrent.Executor; +import java.util.function.Consumer; + /** * Provides an interface to the root-Surface of a View Hierarchy or Window. This * is used in combination with the {@link android.view.SurfaceControl} API to enable @@ -194,6 +197,42 @@ public interface AttachedSurfaceControl { } /** + * Add a trusted presentation listener on the SurfaceControl associated with this window. + * + * @param t Transaction that the trusted presentation listener is added on. This should + * be applied by the caller. + * @param thresholds The {@link SurfaceControl.TrustedPresentationThresholds} that will specify + * when the to invoke the callback. + * @param executor The {@link Executor} where the callback will be invoked on. + * @param listener The {@link Consumer} that will receive the callbacks when entered or + * exited the threshold. + * + * @see SurfaceControl.Transaction#setTrustedPresentationCallback(SurfaceControl, + * SurfaceControl.TrustedPresentationThresholds, Executor, Consumer) + * + * @hide b/287076178 un-hide with API bump + */ + default void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.TrustedPresentationThresholds thresholds, + @NonNull Executor executor, @NonNull Consumer<Boolean> listener) { + } + + /** + * Remove a trusted presentation listener on the SurfaceControl associated with this window. + * + * @param t Transaction that the trusted presentation listener removed on. This should + * be applied by the caller. + * @param listener The {@link Consumer} that was previously registered with + * addTrustedPresentationCallback that should be removed. + * + * @see SurfaceControl.Transaction#clearTrustedPresentationCallback(SurfaceControl) + * @hide b/287076178 un-hide with API bump + */ + default void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull Consumer<Boolean> listener) { + } + + /** * Transfer the currently in progress touch gesture from the host to the requested * {@link SurfaceControlViewHost.SurfacePackage}. This requires that the * SurfaceControlViewHost was created with the current host's inputToken. diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl index 36b74e39072a..17bbee6d020f 100644 --- a/core/java/android/view/IWindowManager.aidl +++ b/core/java/android/view/IWindowManager.aidl @@ -73,8 +73,6 @@ import android.window.ISurfaceSyncGroupCompletedListener; import android.window.ITaskFpsCallback; import android.window.ScreenCapture; import android.window.WindowContextInfo; -import android.window.ITrustedPresentationListener; -import android.window.TrustedPresentationThresholds; /** * System private interface to the window manager. @@ -1077,10 +1075,4 @@ interface IWindowManager @JavaPassthrough(annotation = "@android.annotation.RequiresPermission(android.Manifest" + ".permission.MONITOR_INPUT)") void unregisterDecorViewGestureListener(IDecorViewGestureListener listener, int displayId); - - void registerTrustedPresentationListener(in IBinder window, in ITrustedPresentationListener listener, - in TrustedPresentationThresholds thresholds, int id); - - - void unregisterTrustedPresentationListener(in ITrustedPresentationListener listener, int id); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 8acab2a43823..cac5387116a1 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -11913,6 +11913,18 @@ public final class ViewRootImpl implements ViewParent, scheduleTraversals(); } + @Override + public void addTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull SurfaceControl.TrustedPresentationThresholds thresholds, + @NonNull Executor executor, @NonNull Consumer<Boolean> listener) { + t.setTrustedPresentationCallback(getSurfaceControl(), thresholds, executor, listener); + } + + @Override + public void removeTrustedPresentationCallback(@NonNull SurfaceControl.Transaction t, + @NonNull Consumer<Boolean> listener) { + t.clearTrustedPresentationCallback(getSurfaceControl()); + } private void logAndTrace(String msg) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index f668088e6b44..046ea77f196d 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -122,9 +122,7 @@ import android.view.WindowInsets.Side.InsetsSide; import android.view.WindowInsets.Type; import android.view.WindowInsets.Type.InsetsType; import android.view.accessibility.AccessibilityNodeInfo; -import android.window.ITrustedPresentationListener; import android.window.TaskFpsCallback; -import android.window.TrustedPresentationThresholds; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -5886,35 +5884,4 @@ public interface WindowManager extends ViewManager { default boolean replaceContentOnDisplayWithSc(int displayId, @NonNull SurfaceControl sc) { throw new UnsupportedOperationException(); } - - /** - * Add a trusted presentation listener associated with a window. If the listener has already - * been registered, an AndroidRuntimeException will be thrown. - * - * @param window The Window to add the trusted presentation listener for - * @param thresholds The {@link TrustedPresentationThresholds} that will specify - * when the to invoke the callback. - * @param executor The {@link Executor} where the callback will be invoked on. - * @param listener The {@link ITrustedPresentationListener} that will receive the callbacks - * when entered or exited trusted presentation per the thresholds. - * - * @hide b/287076178 un-hide with API bump - */ - default void registerTrustedPresentationListener(@NonNull IBinder window, - @NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor, - @NonNull Consumer<Boolean> listener) { - throw new UnsupportedOperationException(); - } - - /** - * Removes a presentation listener associated with a window. If the listener was not previously - * registered, the call will be a noop. - * - * @hide - * @see #registerTrustedPresentationListener(IBinder, - * TrustedPresentationThresholds, Executor, Consumer) - */ - default void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) { - throw new UnsupportedOperationException(); - } } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index a7d814e9ab8c..214f1ec3d1ec 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -30,13 +30,9 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.util.AndroidRuntimeException; -import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; -import android.util.Pair; import android.view.inputmethod.InputMethodManager; -import android.window.ITrustedPresentationListener; -import android.window.TrustedPresentationThresholds; import com.android.internal.util.FastPrintWriter; @@ -47,7 +43,6 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.WeakHashMap; import java.util.concurrent.Executor; -import java.util.function.Consumer; import java.util.function.IntConsumer; /** @@ -148,9 +143,6 @@ public final class WindowManagerGlobal { private Runnable mSystemPropertyUpdater; - private final TrustedPresentationListener mTrustedPresentationListener = - new TrustedPresentationListener(); - private WindowManagerGlobal() { } @@ -332,7 +324,7 @@ public final class WindowManagerGlobal { final Context context = view.getContext(); if (context != null && (context.getApplicationInfo().flags - & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { + & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) { wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; } } @@ -490,7 +482,7 @@ public final class WindowManagerGlobal { if (who != null) { WindowLeaked leak = new WindowLeaked( what + " " + who + " has leaked window " - + root.getView() + " that was originally added here"); + + root.getView() + " that was originally added here"); leak.setStackTrace(root.getLocation().getStackTrace()); Log.e(TAG, "", leak); } @@ -798,86 +790,6 @@ public final class WindowManagerGlobal { } } - public void registerTrustedPresentationListener(@NonNull IBinder window, - @NonNull TrustedPresentationThresholds thresholds, Executor executor, - @NonNull Consumer<Boolean> listener) { - mTrustedPresentationListener.addListener(window, thresholds, listener, executor); - } - - public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) { - mTrustedPresentationListener.removeListener(listener); - } - - private final class TrustedPresentationListener extends - ITrustedPresentationListener.Stub { - private static int sId = 0; - private final ArrayMap<Consumer<Boolean>, Pair<Integer, Executor>> mListeners = - new ArrayMap<>(); - - private final Object mTplLock = new Object(); - - private void addListener(IBinder window, TrustedPresentationThresholds thresholds, - Consumer<Boolean> listener, Executor executor) { - synchronized (mTplLock) { - if (mListeners.containsKey(listener)) { - throw new AndroidRuntimeException("Trying to add duplicate listener"); - } - int id = sId++; - mListeners.put(listener, new Pair<>(id, executor)); - try { - WindowManagerGlobal.getWindowManagerService() - .registerTrustedPresentationListener(window, this, thresholds, id); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } - } - } - - private void removeListener(Consumer<Boolean> listener) { - synchronized (mTplLock) { - var removedListener = mListeners.remove(listener); - if (removedListener == null) { - Log.i(TAG, "listener " + listener + " does not exist."); - return; - } - - try { - WindowManagerGlobal.getWindowManagerService() - .unregisterTrustedPresentationListener(this, removedListener.first); - } catch (RemoteException e) { - e.rethrowAsRuntimeException(); - } - } - } - - @Override - public void onTrustedPresentationChanged(int[] inTrustedStateListenerIds, - int[] outOfTrustedStateListenerIds) { - ArrayList<Runnable> firedListeners = new ArrayList<>(); - synchronized (mTplLock) { - mListeners.forEach((listener, idExecutorPair) -> { - final var listenerId = idExecutorPair.first; - final var executor = idExecutorPair.second; - for (int id : inTrustedStateListenerIds) { - if (listenerId == id) { - firedListeners.add(() -> executor.execute( - () -> listener.accept(/*presentationState*/true))); - } - } - for (int id : outOfTrustedStateListenerIds) { - if (listenerId == id) { - firedListeners.add(() -> executor.execute( - () -> listener.accept(/*presentationState*/false))); - } - } - }); - } - for (int i = 0; i < firedListeners.size(); i++) { - firedListeners.get(i).run(); - } - } - } - /** @hide */ public void addWindowlessRoot(ViewRootImpl impl) { synchronized (mLock) { @@ -889,7 +801,7 @@ public final class WindowManagerGlobal { public void removeWindowlessRoot(ViewRootImpl impl) { synchronized (mLock) { mWindowlessRoots.remove(impl); - } + } } public void setRecentsAppBehindSystemBars(boolean behindSystemBars) { diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java index b4b1fde89a46..d7b74b3bcfe2 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -37,7 +37,6 @@ import android.os.StrictMode; import android.util.Log; import android.window.ITaskFpsCallback; import android.window.TaskFpsCallback; -import android.window.TrustedPresentationThresholds; import android.window.WindowContext; import android.window.WindowMetricsController; import android.window.WindowProvider; @@ -509,17 +508,4 @@ public final class WindowManagerImpl implements WindowManager { } return false; } - - @Override - public void registerTrustedPresentationListener(@NonNull IBinder window, - @NonNull TrustedPresentationThresholds thresholds, @NonNull Executor executor, - @NonNull Consumer<Boolean> listener) { - mGlobal.registerTrustedPresentationListener(window, thresholds, executor, listener); - } - - @Override - public void unregisterTrustedPresentationListener(@NonNull Consumer<Boolean> listener) { - mGlobal.unregisterTrustedPresentationListener(listener); - - } } diff --git a/core/java/android/window/ITrustedPresentationListener.aidl b/core/java/android/window/ITrustedPresentationListener.aidl deleted file mode 100644 index b33128abb7e5..000000000000 --- a/core/java/android/window/ITrustedPresentationListener.aidl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.window; - -/** - * @hide - */ -oneway interface ITrustedPresentationListener { - void onTrustedPresentationChanged(in int[] enteredTrustedStateIds, in int[] exitedTrustedStateIds); -}
\ No newline at end of file diff --git a/core/java/android/window/TrustedPresentationListener.java b/core/java/android/window/TrustedPresentationListener.java deleted file mode 100644 index 02fd6d98fb0d..000000000000 --- a/core/java/android/window/TrustedPresentationListener.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.window; - -/** - * @hide - */ -public interface TrustedPresentationListener { - - void onTrustedPresentationChanged(boolean inTrustedPresentationState); - -} diff --git a/core/java/android/window/TrustedPresentationThresholds.aidl b/core/java/android/window/TrustedPresentationThresholds.aidl deleted file mode 100644 index d7088bf0fddc..000000000000 --- a/core/java/android/window/TrustedPresentationThresholds.aidl +++ /dev/null @@ -1,3 +0,0 @@ -package android.window; - -parcelable TrustedPresentationThresholds; diff --git a/core/java/android/window/TrustedPresentationThresholds.java b/core/java/android/window/TrustedPresentationThresholds.java deleted file mode 100644 index 801d35c49228..000000000000 --- a/core/java/android/window/TrustedPresentationThresholds.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.window; - -import android.annotation.FloatRange; -import android.annotation.IntRange; -import android.os.Parcel; -import android.os.Parcelable; -import android.view.SurfaceControl; - -import androidx.annotation.NonNull; - -/** - * @hide - */ -public final class TrustedPresentationThresholds implements Parcelable { - /** - * The min alpha the {@link SurfaceControl} is required to have to be considered inside the - * threshold. - */ - @FloatRange(from = 0f, fromInclusive = false, to = 1f) - public final float mMinAlpha; - - /** - * The min fraction of the SurfaceControl that was presented to the user to be considered - * inside the threshold. - */ - @FloatRange(from = 0f, fromInclusive = false, to = 1f) - public final float mMinFractionRendered; - - /** - * The time in milliseconds required for the {@link SurfaceControl} to be in the threshold. - */ - @IntRange(from = 1) - public final int mStabilityRequirementMs; - - private void checkValid() { - if (mMinAlpha <= 0 || mMinFractionRendered <= 0 || mStabilityRequirementMs < 1) { - throw new IllegalArgumentException( - "TrustedPresentationThresholds values are invalid"); - } - } - - /** - * Creates a new TrustedPresentationThresholds. - * - * @param minAlpha The min alpha the {@link SurfaceControl} is required to - * have to be considered inside the - * threshold. - * @param minFractionRendered The min fraction of the SurfaceControl that was presented - * to the user to be considered - * inside the threshold. - * @param stabilityRequirementMs The time in milliseconds required for the - * {@link SurfaceControl} to be in the threshold. - */ - public TrustedPresentationThresholds( - @FloatRange(from = 0f, fromInclusive = false, to = 1f) float minAlpha, - @FloatRange(from = 0f, fromInclusive = false, to = 1f) float minFractionRendered, - @IntRange(from = 1) int stabilityRequirementMs) { - this.mMinAlpha = minAlpha; - this.mMinFractionRendered = minFractionRendered; - this.mStabilityRequirementMs = stabilityRequirementMs; - checkValid(); - } - - @Override - public String toString() { - return "TrustedPresentationThresholds { " - + "minAlpha = " + mMinAlpha + ", " - + "minFractionRendered = " + mMinFractionRendered + ", " - + "stabilityRequirementMs = " + mStabilityRequirementMs - + " }"; - } - - @Override - public void writeToParcel(@NonNull Parcel dest, int flags) { - dest.writeFloat(mMinAlpha); - dest.writeFloat(mMinFractionRendered); - dest.writeInt(mStabilityRequirementMs); - } - - @Override - public int describeContents() { - return 0; - } - - /** - * @hide - */ - TrustedPresentationThresholds(@NonNull Parcel in) { - mMinAlpha = in.readFloat(); - mMinFractionRendered = in.readFloat(); - mStabilityRequirementMs = in.readInt(); - - checkValid(); - } - - /** - * @hide - */ - public static final @NonNull Creator<TrustedPresentationThresholds> CREATOR = - new Creator<TrustedPresentationThresholds>() { - @Override - public TrustedPresentationThresholds[] newArray(int size) { - return new TrustedPresentationThresholds[size]; - } - - @Override - public TrustedPresentationThresholds createFromParcel(@NonNull Parcel in) { - return new TrustedPresentationThresholds(in); - } - }; -} diff --git a/core/java/com/android/internal/protolog/ProtoLogGroup.java b/core/java/com/android/internal/protolog/ProtoLogGroup.java index 8c2a52560050..4bb7c33b41e2 100644 --- a/core/java/com/android/internal/protolog/ProtoLogGroup.java +++ b/core/java/com/android/internal/protolog/ProtoLogGroup.java @@ -93,7 +93,6 @@ public enum ProtoLogGroup implements IProtoLogGroup { WM_DEBUG_DREAM(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, true, Consts.TAG_WM), WM_DEBUG_DIMMER(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), - WM_DEBUG_TPL(Consts.ENABLE_DEBUG, Consts.ENABLE_LOG_TO_PROTO_DEBUG, false, Consts.TAG_WM), TEST_GROUP(true, true, false, "WindowManagerProtoLogTest"); private final boolean mEnabled; diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json index 19128212094d..2237ba1924db 100644 --- a/data/etc/services.core.protolog.json +++ b/data/etc/services.core.protolog.json @@ -595,12 +595,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "-1518132958": { - "message": "fractionRendered boundsOverSource=%f", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "-1517908912": { "message": "requestScrollCapture: caught exception dispatching to window.token=%s", "level": "WARN", @@ -967,12 +961,6 @@ "group": "WM_DEBUG_CONTENT_RECORDING", "at": "com\/android\/server\/wm\/ContentRecorder.java" }, - "-1209762265": { - "message": "Registering listener=%s with id=%d for window=%s with %s", - "level": "DEBUG", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "-1209252064": { "message": "Clear animatingExit: reason=clearAnimatingFlags win=%s", "level": "DEBUG", @@ -1345,12 +1333,6 @@ "group": "WM_DEBUG_WINDOW_TRANSITIONS", "at": "com\/android\/server\/wm\/Transition.java" }, - "-888703350": { - "message": "Skipping %s", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "-883738232": { "message": "Adding more than one toast window for UID at a time.", "level": "WARN", @@ -2821,12 +2803,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "360319850": { - "message": "fractionRendered scale=%f", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "364992694": { "message": "freezeDisplayRotation: current rotation=%d, new rotation=%d, caller=%s", "level": "VERBOSE", @@ -3007,12 +2983,6 @@ "group": "WM_DEBUG_BACK_PREVIEW", "at": "com\/android\/server\/wm\/BackNavigationController.java" }, - "532771960": { - "message": "Adding untrusted state listener=%s with id=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "535103992": { "message": "Wallpaper may change! Adjusting", "level": "VERBOSE", @@ -3091,12 +3061,6 @@ "group": "WM_DEBUG_DREAM", "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java" }, - "605179032": { - "message": "checkIfInThreshold fractionRendered=%f alpha=%f currTimeMs=%d", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "608694300": { "message": " NEW SURFACE SESSION %s", "level": "INFO", @@ -3325,12 +3289,6 @@ "group": "WM_SHOW_TRANSACTIONS", "at": "com\/android\/server\/wm\/WindowState.java" }, - "824532141": { - "message": "lastState=%s newState=%s alpha=%f minAlpha=%f fractionRendered=%f minFractionRendered=%f", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "829434921": { "message": "Draw state now committed in %s", "level": "VERBOSE", @@ -3625,12 +3583,6 @@ "group": "WM_SHOW_SURFACE_ALLOC", "at": "com\/android\/server\/wm\/ScreenRotationAnimation.java" }, - "1090378847": { - "message": "Checking %d windows", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "1100065297": { "message": "Attempted to get IME policy of a display that does not exist: %d", "level": "WARN", @@ -3763,12 +3715,6 @@ "group": "WM_DEBUG_FOCUS", "at": "com\/android\/server\/wm\/ActivityRecord.java" }, - "1251721200": { - "message": "unregister failed, couldn't find deathRecipient for %s with id=%d", - "level": "ERROR", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "1252594551": { "message": "Window types in WindowContext and LayoutParams.type should match! Type from LayoutParams is %d, but type from WindowContext is %d", "level": "WARN", @@ -3907,12 +3853,6 @@ "group": "WM_DEBUG_ORIENTATION", "at": "com\/android\/server\/wm\/TaskDisplayArea.java" }, - "1382634842": { - "message": "Unregistering listener=%s with id=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "1393721079": { "message": "Starting remote display change: from [rot = %d], to [%dx%d, rot = %d]", "level": "VERBOSE", @@ -3961,12 +3901,6 @@ "group": "WM_ERROR", "at": "com\/android\/server\/wm\/WindowManagerService.java" }, - "1445704347": { - "message": "coveredRegionsAbove updated with %s frame:%s region:%s", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "1448683958": { "message": "Override pending remote transitionSet=%b adapter=%s", "level": "INFO", @@ -4267,12 +4201,6 @@ "group": "WM_DEBUG_RECENTS_ANIMATIONS", "at": "com\/android\/server\/wm\/RecentsAnimation.java" }, - "1786463281": { - "message": "Adding trusted state listener=%s with id=%d", - "level": "DEBUG", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "1789321832": { "message": "Then token:%s is invalid. It might be removed", "level": "WARN", @@ -4447,12 +4375,6 @@ "group": "WM_DEBUG_TASKS", "at": "com\/android\/server\/wm\/RootWindowContainer.java" }, - "1955470028": { - "message": "computeFractionRendered: visibleRegion=%s screenBounds=%s contentSize=%s scale=%f,%f", - "level": "VERBOSE", - "group": "WM_DEBUG_TPL", - "at": "com\/android\/server\/wm\/TrustedPresentationListenerController.java" - }, "1964565370": { "message": "Starting remote animation", "level": "INFO", @@ -4737,9 +4659,6 @@ "WM_DEBUG_TASKS": { "tag": "WindowManager" }, - "WM_DEBUG_TPL": { - "tag": "WindowManager" - }, "WM_DEBUG_WALLPAPER": { "tag": "WindowManager" }, diff --git a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java b/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java deleted file mode 100644 index e82dc37c2b6a..000000000000 --- a/services/core/java/com/android/server/wm/TrustedPresentationListenerController.java +++ /dev/null @@ -1,448 +0,0 @@ -/* - * Copyright 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.graphics.Matrix.MSCALE_X; -import static android.graphics.Matrix.MSCALE_Y; -import static android.graphics.Matrix.MSKEW_X; -import static android.graphics.Matrix.MSKEW_Y; - -import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_TPL; - -import android.graphics.Matrix; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.Region; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; -import android.os.RemoteException; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.IntArray; -import android.util.Pair; -import android.util.Size; -import android.view.InputWindowHandle; -import android.window.ITrustedPresentationListener; -import android.window.TrustedPresentationThresholds; -import android.window.WindowInfosListener; - -import com.android.internal.protolog.common.ProtoLog; -import com.android.server.wm.utils.RegionUtils; - -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Optional; - -/** - * Class to handle TrustedPresentationListener registrations in a thread safe manner. This class - * also takes care of cleaning up listeners when the remote process dies. - */ -public class TrustedPresentationListenerController { - - // Should only be accessed by the posting to the handler - private class Listeners { - private final class ListenerDeathRecipient implements IBinder.DeathRecipient { - IBinder mListenerBinder; - int mInstances; - - ListenerDeathRecipient(IBinder listenerBinder) { - mListenerBinder = listenerBinder; - mInstances = 0; - try { - mListenerBinder.linkToDeath(this, 0); - } catch (RemoteException ignore) { - } - } - - void addInstance() { - mInstances++; - } - - // return true if there are no instances alive - boolean removeInstance() { - mInstances--; - if (mInstances > 0) { - return false; - } - mListenerBinder.unlinkToDeath(this, 0); - return true; - } - - public void binderDied() { - mHandler.post(() -> { - mUniqueListeners.remove(mListenerBinder); - removeListeners(mListenerBinder, Optional.empty()); - }); - } - } - - // tracks binder deaths for cleanup - ArrayMap<IBinder, ListenerDeathRecipient> mUniqueListeners = new ArrayMap<>(); - ArrayMap<IBinder /*window*/, ArrayList<TrustedPresentationInfo>> mWindowToListeners = - new ArrayMap<>(); - - void register(IBinder window, ITrustedPresentationListener listener, - TrustedPresentationThresholds thresholds, int id) { - var listenersForWindow = mWindowToListeners.computeIfAbsent(window, - iBinder -> new ArrayList<>()); - listenersForWindow.add(new TrustedPresentationInfo(thresholds, id, listener)); - - // register death listener - var listenerBinder = listener.asBinder(); - var deathRecipient = mUniqueListeners.computeIfAbsent(listenerBinder, - ListenerDeathRecipient::new); - deathRecipient.addInstance(); - } - - void unregister(ITrustedPresentationListener trustedPresentationListener, int id) { - var listenerBinder = trustedPresentationListener.asBinder(); - var deathRecipient = mUniqueListeners.get(listenerBinder); - if (deathRecipient == null) { - ProtoLog.e(WM_DEBUG_TPL, "unregister failed, couldn't find" - + " deathRecipient for %s with id=%d", trustedPresentationListener, id); - return; - } - - if (deathRecipient.removeInstance()) { - mUniqueListeners.remove(listenerBinder); - } - removeListeners(listenerBinder, Optional.of(id)); - } - - boolean isEmpty() { - return mWindowToListeners.isEmpty(); - } - - ArrayList<TrustedPresentationInfo> get(IBinder windowToken) { - return mWindowToListeners.get(windowToken); - } - - private void removeListeners(IBinder listenerBinder, Optional<Integer> id) { - for (int i = mWindowToListeners.size() - 1; i >= 0; i--) { - var listeners = mWindowToListeners.valueAt(i); - for (int j = listeners.size() - 1; j >= 0; j--) { - var listener = listeners.get(j); - if (listener.mListener.asBinder() == listenerBinder && (id.isEmpty() - || listener.mId == id.get())) { - listeners.remove(j); - } - } - if (listeners.isEmpty()) { - mWindowToListeners.removeAt(i); - } - } - } - } - - private final Object mHandlerThreadLock = new Object(); - private HandlerThread mHandlerThread; - private Handler mHandler; - - private WindowInfosListener mWindowInfosListener; - - Listeners mRegisteredListeners = new Listeners(); - - private InputWindowHandle[] mLastWindowHandles; - - private final Object mIgnoredWindowTokensLock = new Object(); - - private final ArraySet<IBinder> mIgnoredWindowTokens = new ArraySet<>(); - - private void startHandlerThreadIfNeeded() { - synchronized (mHandlerThreadLock) { - if (mHandler == null) { - mHandlerThread = new HandlerThread("WindowInfosListenerForTpl"); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - } - } - } - - void addIgnoredWindowTokens(IBinder token) { - synchronized (mIgnoredWindowTokensLock) { - mIgnoredWindowTokens.add(token); - } - } - - void removeIgnoredWindowTokens(IBinder token) { - synchronized (mIgnoredWindowTokensLock) { - mIgnoredWindowTokens.remove(token); - } - } - - void registerListener(IBinder window, ITrustedPresentationListener listener, - TrustedPresentationThresholds thresholds, int id) { - startHandlerThreadIfNeeded(); - mHandler.post(() -> { - ProtoLog.d(WM_DEBUG_TPL, "Registering listener=%s with id=%d for window=%s with %s", - listener, id, window, thresholds); - - mRegisteredListeners.register(window, listener, thresholds, id); - registerWindowInfosListener(); - // Update the initial state for the new registered listener - computeTpl(mLastWindowHandles); - }); - } - - void unregisterListener(ITrustedPresentationListener listener, int id) { - startHandlerThreadIfNeeded(); - mHandler.post(() -> { - ProtoLog.d(WM_DEBUG_TPL, "Unregistering listener=%s with id=%d", - listener, id); - - mRegisteredListeners.unregister(listener, id); - if (mRegisteredListeners.isEmpty()) { - unregisterWindowInfosListener(); - } - }); - } - - void dump(PrintWriter pw) { - final String innerPrefix = " "; - pw.println("TrustedPresentationListenerController:"); - pw.println(innerPrefix + "Active unique listeners (" - + mRegisteredListeners.mUniqueListeners.size() + "):"); - for (int i = 0; i < mRegisteredListeners.mWindowToListeners.size(); i++) { - pw.println( - innerPrefix + " window=" + mRegisteredListeners.mWindowToListeners.keyAt(i)); - final var listeners = mRegisteredListeners.mWindowToListeners.valueAt(i); - for (int j = 0; j < listeners.size(); j++) { - final var listener = listeners.get(j); - pw.println(innerPrefix + innerPrefix + " listener=" + listener.mListener.asBinder() - + " id=" + listener.mId - + " thresholds=" + listener.mThresholds); - } - } - } - - private void registerWindowInfosListener() { - if (mWindowInfosListener != null) { - return; - } - - mWindowInfosListener = new WindowInfosListener() { - @Override - public void onWindowInfosChanged(InputWindowHandle[] windowHandles, - DisplayInfo[] displayInfos) { - mHandler.post(() -> computeTpl(windowHandles)); - } - }; - mLastWindowHandles = mWindowInfosListener.register().first; - } - - private void unregisterWindowInfosListener() { - if (mWindowInfosListener == null) { - return; - } - - mWindowInfosListener.unregister(); - mWindowInfosListener = null; - mLastWindowHandles = null; - } - - private void computeTpl(InputWindowHandle[] windowHandles) { - mLastWindowHandles = windowHandles; - if (mLastWindowHandles == null || mLastWindowHandles.length == 0 - || mRegisteredListeners.isEmpty()) { - return; - } - - Rect tmpRect = new Rect(); - Matrix tmpInverseMatrix = new Matrix(); - float[] tmpMatrix = new float[9]; - Region coveredRegionsAbove = new Region(); - long currTimeMs = System.currentTimeMillis(); - ProtoLog.v(WM_DEBUG_TPL, "Checking %d windows", mLastWindowHandles.length); - - ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates = - new ArrayMap<>(); - ArraySet<IBinder> ignoredWindowTokens; - synchronized (mIgnoredWindowTokensLock) { - ignoredWindowTokens = new ArraySet<>(mIgnoredWindowTokens); - } - for (var windowHandle : mLastWindowHandles) { - if (ignoredWindowTokens.contains(windowHandle.getWindowToken())) { - ProtoLog.v(WM_DEBUG_TPL, "Skipping %s", windowHandle.name); - continue; - } - tmpRect.set(windowHandle.frame); - var listeners = mRegisteredListeners.get(windowHandle.getWindowToken()); - if (listeners != null) { - Region region = new Region(); - region.op(tmpRect, coveredRegionsAbove, Region.Op.DIFFERENCE); - windowHandle.transform.invert(tmpInverseMatrix); - tmpInverseMatrix.getValues(tmpMatrix); - float scaleX = (float) Math.sqrt(tmpMatrix[MSCALE_X] * tmpMatrix[MSCALE_X] - + tmpMatrix[MSKEW_X] * tmpMatrix[MSKEW_X]); - float scaleY = (float) Math.sqrt(tmpMatrix[MSCALE_Y] * tmpMatrix[MSCALE_Y] - + tmpMatrix[MSKEW_Y] * tmpMatrix[MSKEW_Y]); - - float fractionRendered = computeFractionRendered(region, new RectF(tmpRect), - windowHandle.contentSize, - scaleX, scaleY); - - checkIfInThreshold(listeners, listenerUpdates, fractionRendered, windowHandle.alpha, - currTimeMs); - } - - coveredRegionsAbove.op(tmpRect, Region.Op.UNION); - ProtoLog.v(WM_DEBUG_TPL, "coveredRegionsAbove updated with %s frame:%s region:%s", - windowHandle.name, tmpRect.toShortString(), coveredRegionsAbove); - } - - for (int i = 0; i < listenerUpdates.size(); i++) { - var updates = listenerUpdates.valueAt(i); - var listener = listenerUpdates.keyAt(i); - try { - listener.onTrustedPresentationChanged(updates.first.toArray(), - updates.second.toArray()); - } catch (RemoteException ignore) { - } - } - } - - private void addListenerUpdate( - ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates, - ITrustedPresentationListener listener, int id, boolean presentationState) { - var updates = listenerUpdates.get(listener); - if (updates == null) { - updates = new Pair<>(new IntArray(), new IntArray()); - listenerUpdates.put(listener, updates); - } - if (presentationState) { - updates.first.add(id); - } else { - updates.second.add(id); - } - } - - - private void checkIfInThreshold( - ArrayList<TrustedPresentationInfo> listeners, - ArrayMap<ITrustedPresentationListener, Pair<IntArray, IntArray>> listenerUpdates, - float fractionRendered, float alpha, long currTimeMs) { - ProtoLog.v(WM_DEBUG_TPL, "checkIfInThreshold fractionRendered=%f alpha=%f currTimeMs=%d", - fractionRendered, alpha, currTimeMs); - for (int i = 0; i < listeners.size(); i++) { - var trustedPresentationInfo = listeners.get(i); - var listener = trustedPresentationInfo.mListener; - boolean lastState = trustedPresentationInfo.mLastComputedTrustedPresentationState; - boolean newState = - (alpha >= trustedPresentationInfo.mThresholds.mMinAlpha) && (fractionRendered - >= trustedPresentationInfo.mThresholds.mMinFractionRendered); - trustedPresentationInfo.mLastComputedTrustedPresentationState = newState; - - ProtoLog.v(WM_DEBUG_TPL, - "lastState=%s newState=%s alpha=%f minAlpha=%f fractionRendered=%f " - + "minFractionRendered=%f", - lastState, newState, alpha, trustedPresentationInfo.mThresholds.mMinAlpha, - fractionRendered, trustedPresentationInfo.mThresholds.mMinFractionRendered); - - if (lastState && !newState) { - // We were in the trusted presentation state, but now we left it, - // emit the callback if needed - if (trustedPresentationInfo.mLastReportedTrustedPresentationState) { - trustedPresentationInfo.mLastReportedTrustedPresentationState = false; - addListenerUpdate(listenerUpdates, listener, - trustedPresentationInfo.mId, /*presentationState*/ false); - ProtoLog.d(WM_DEBUG_TPL, "Adding untrusted state listener=%s with id=%d", - listener, trustedPresentationInfo.mId); - } - // Reset the timer - trustedPresentationInfo.mEnteredTrustedPresentationStateTime = -1; - } else if (!lastState && newState) { - // We were not in the trusted presentation state, but we entered it, begin the timer - // and make sure this gets called at least once more! - trustedPresentationInfo.mEnteredTrustedPresentationStateTime = currTimeMs; - mHandler.postDelayed(() -> { - computeTpl(mLastWindowHandles); - }, (long) (trustedPresentationInfo.mThresholds.mStabilityRequirementMs * 1.5)); - } - - // Has the timer elapsed, but we are still in the state? Emit a callback if needed - if (!trustedPresentationInfo.mLastReportedTrustedPresentationState && newState && ( - currTimeMs - trustedPresentationInfo.mEnteredTrustedPresentationStateTime - > trustedPresentationInfo.mThresholds.mStabilityRequirementMs)) { - trustedPresentationInfo.mLastReportedTrustedPresentationState = true; - addListenerUpdate(listenerUpdates, listener, - trustedPresentationInfo.mId, /*presentationState*/ true); - ProtoLog.d(WM_DEBUG_TPL, "Adding trusted state listener=%s with id=%d", - listener, trustedPresentationInfo.mId); - } - } - } - - private float computeFractionRendered(Region visibleRegion, RectF screenBounds, - Size contentSize, - float sx, float sy) { - ProtoLog.v(WM_DEBUG_TPL, - "computeFractionRendered: visibleRegion=%s screenBounds=%s contentSize=%s " - + "scale=%f,%f", - visibleRegion, screenBounds, contentSize, sx, sy); - - if (contentSize.getWidth() == 0 || contentSize.getHeight() == 0) { - return -1; - } - if (screenBounds.width() == 0 || screenBounds.height() == 0) { - return -1; - } - - float fractionRendered = Math.min(sx * sy, 1.0f); - ProtoLog.v(WM_DEBUG_TPL, "fractionRendered scale=%f", fractionRendered); - - float boundsOverSourceW = screenBounds.width() / (float) contentSize.getWidth(); - float boundsOverSourceH = screenBounds.height() / (float) contentSize.getHeight(); - fractionRendered *= boundsOverSourceW * boundsOverSourceH; - ProtoLog.v(WM_DEBUG_TPL, "fractionRendered boundsOverSource=%f", fractionRendered); - // Compute the size of all the rects since they may be disconnected. - float[] visibleSize = new float[1]; - RegionUtils.forEachRect(visibleRegion, rect -> { - float size = rect.width() * rect.height(); - visibleSize[0] += size; - }); - - fractionRendered *= visibleSize[0] / (screenBounds.width() * screenBounds.height()); - return fractionRendered; - } - - private static class TrustedPresentationInfo { - boolean mLastComputedTrustedPresentationState = false; - boolean mLastReportedTrustedPresentationState = false; - long mEnteredTrustedPresentationStateTime = -1; - final TrustedPresentationThresholds mThresholds; - - final ITrustedPresentationListener mListener; - final int mId; - - private TrustedPresentationInfo(TrustedPresentationThresholds thresholds, int id, - ITrustedPresentationListener listener) { - mThresholds = thresholds; - mId = id; - mListener = listener; - checkValid(thresholds); - } - - private void checkValid(TrustedPresentationThresholds thresholds) { - if (thresholds.mMinAlpha <= 0 || thresholds.mMinFractionRendered <= 0 - || thresholds.mStabilityRequirementMs < 1) { - throw new IllegalArgumentException( - "TrustedPresentationThresholds values are invalid"); - } - } - } -} diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index eaed3f94c5f5..dd2b48bb5a3d 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -303,11 +303,9 @@ import android.window.AddToSurfaceSyncGroupResult; import android.window.ClientWindowFrames; import android.window.ISurfaceSyncGroupCompletedListener; import android.window.ITaskFpsCallback; -import android.window.ITrustedPresentationListener; import android.window.ScreenCapture; import android.window.SystemPerformanceHinter; import android.window.TaskSnapshot; -import android.window.TrustedPresentationThresholds; import android.window.WindowContainerToken; import android.window.WindowContextInfo; @@ -766,9 +764,6 @@ public class WindowManagerService extends IWindowManager.Stub private final SurfaceSyncGroupController mSurfaceSyncGroupController = new SurfaceSyncGroupController(); - final TrustedPresentationListenerController mTrustedPresentationListenerController = - new TrustedPresentationListenerController(); - @VisibleForTesting final class SettingsObserver extends ContentObserver { private final Uri mDisplayInversionEnabledUri = @@ -7176,7 +7171,6 @@ public class WindowManagerService extends IWindowManager.Stub pw.println(separator); } mSystemPerformanceHinter.dump(pw, ""); - mTrustedPresentationListenerController.dump(pw); } } @@ -9768,17 +9762,4 @@ public class WindowManagerService extends IWindowManager.Stub Binder.restoreCallingIdentity(origId); } } - - @Override - public void registerTrustedPresentationListener(IBinder window, - ITrustedPresentationListener listener, - TrustedPresentationThresholds thresholds, int id) { - mTrustedPresentationListenerController.registerListener(window, listener, thresholds, id); - } - - @Override - public void unregisterTrustedPresentationListener(ITrustedPresentationListener listener, - int id) { - mTrustedPresentationListenerController.unregisterListener(listener, id); - } } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 7bc7e2cb780b..e1f1f662c5aa 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -1189,11 +1189,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP ProtoLog.v(WM_DEBUG_ADD_REMOVE, "Adding %s to %s", this, parentWindow); parentWindow.addChild(this, sWindowSubLayerComparator); } - - if (token.mRoundedCornerOverlay) { - mWmService.mTrustedPresentationListenerController.addIgnoredWindowTokens( - getWindowToken()); - } } @Override @@ -2398,9 +2393,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } mWmService.postWindowRemoveCleanupLocked(this); - - mWmService.mTrustedPresentationListenerController.removeIgnoredWindowTokens( - getWindowToken()); } @Override diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml index a8d3fa110844..c3074bb0fee8 100644 --- a/services/tests/wmtests/AndroidManifest.xml +++ b/services/tests/wmtests/AndroidManifest.xml @@ -99,7 +99,7 @@ android:theme="@style/WhiteBackgroundTheme" android:exported="true"/> - <activity android:name="com.android.server.wm.TrustedPresentationListenerTest$TestActivity" + <activity android:name="com.android.server.wm.TrustedPresentationCallbackTest$TestActivity" android:exported="true" android:showWhenLocked="true" android:turnScreenOn="true" /> diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java new file mode 100644 index 000000000000..c5dd447b5b0c --- /dev/null +++ b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationCallbackTest.java @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.wm; + +import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule; +import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import android.app.Activity; +import android.platform.test.annotations.Presubmit; +import android.server.wm.CtsWindowInfoUtils; +import android.view.SurfaceControl; +import android.view.SurfaceControl.TrustedPresentationThresholds; + +import androidx.annotation.GuardedBy; +import androidx.test.ext.junit.rules.ActivityScenarioRule; + +import com.android.server.wm.utils.CommonUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; + +import java.util.function.Consumer; + +/** + * TODO (b/287076178): Move these tests to + * {@link android.view.surfacecontrol.cts.TrustedPresentationCallbackTest} when API is made public + */ +@Presubmit +public class TrustedPresentationCallbackTest { + private static final String TAG = "TrustedPresentationCallbackTest"; + private static final int STABILITY_REQUIREMENT_MS = 500; + private static final long WAIT_TIME_MS = HW_TIMEOUT_MULTIPLIER * 2000L; + + private static final float FRACTION_VISIBLE = 0.1f; + + private final Object mResultsLock = new Object(); + @GuardedBy("mResultsLock") + private boolean mResult; + @GuardedBy("mResultsLock") + private boolean mReceivedResults; + + @Rule + public TestName mName = new TestName(); + + @Rule + public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule( + TestActivity.class); + + private TestActivity mActivity; + + @Before + public void setup() { + mActivityRule.getScenario().onActivity(activity -> mActivity = activity); + } + + @After + public void tearDown() { + CommonUtils.waitUntilActivityRemoved(mActivity); + } + + @Test + public void testAddTrustedPresentationListenerOnWindow() throws InterruptedException { + TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds( + 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS); + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds, + Runnable::run, inTrustedPresentationState -> { + synchronized (mResultsLock) { + mResult = inTrustedPresentationState; + mReceivedResults = true; + mResultsLock.notify(); + } + }); + t.apply(); + synchronized (mResultsLock) { + assertResults(); + } + } + + @Test + public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException { + TrustedPresentationThresholds thresholds = new TrustedPresentationThresholds( + 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS); + Consumer<Boolean> trustedPresentationCallback = inTrustedPresentationState -> { + synchronized (mResultsLock) { + mResult = inTrustedPresentationState; + mReceivedResults = true; + mResultsLock.notify(); + } + }; + SurfaceControl.Transaction t = new SurfaceControl.Transaction(); + mActivity.getWindow().getRootSurfaceControl().addTrustedPresentationCallback(t, thresholds, + Runnable::run, trustedPresentationCallback); + t.apply(); + + synchronized (mResultsLock) { + if (!mReceivedResults) { + mResultsLock.wait(WAIT_TIME_MS); + } + assertResults(); + // reset the state + mReceivedResults = false; + } + + mActivity.getWindow().getRootSurfaceControl().removeTrustedPresentationCallback(t, + trustedPresentationCallback); + t.apply(); + + synchronized (mResultsLock) { + if (!mReceivedResults) { + mResultsLock.wait(WAIT_TIME_MS); + } + // Ensure we waited the full time and never received a notify on the result from the + // callback. + assertFalse("Should never have received a callback", mReceivedResults); + // results shouldn't have changed. + assertTrue(mResult); + } + } + + @GuardedBy("mResultsLock") + private void assertResults() throws InterruptedException { + mResultsLock.wait(WAIT_TIME_MS); + + if (!mReceivedResults) { + CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, "test " + mName.getMethodName()); + } + // Make sure we received the results and not just timed out + assertTrue("Timed out waiting for results", mReceivedResults); + assertTrue(mResult); + } + + public static class TestActivity extends Activity { + } +} diff --git a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationListenerTest.java b/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationListenerTest.java deleted file mode 100644 index 96b66bfd3bc0..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/TrustedPresentationListenerTest.java +++ /dev/null @@ -1,267 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static android.server.wm.ActivityManagerTestBase.createFullscreenActivityScenarioRule; -import static android.server.wm.BuildUtils.HW_TIMEOUT_MULTIPLIER; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.fail; - -import android.app.Activity; -import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; -import android.server.wm.CtsWindowInfoUtils; -import android.util.AndroidRuntimeException; -import android.util.Log; -import android.view.SurfaceControl; -import android.view.SurfaceControlViewHost; -import android.view.View; -import android.view.WindowManager; -import android.window.TrustedPresentationThresholds; - -import androidx.annotation.GuardedBy; -import androidx.annotation.NonNull; -import androidx.test.ext.junit.rules.ActivityScenarioRule; - -import com.android.server.wm.utils.CommonUtils; - -import junit.framework.Assert; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TestName; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; - -/** - * TODO (b/287076178): Move these tests to - * {@link android.view.surfacecontrol.cts.TrustedPresentationListenerTest} when API is made public - */ -@Presubmit -public class TrustedPresentationListenerTest { - private static final String TAG = "TrustedPresentationListenerTest"; - private static final int STABILITY_REQUIREMENT_MS = 500; - private static final long WAIT_TIME_MS = HW_TIMEOUT_MULTIPLIER * 2000L; - - private static final float FRACTION_VISIBLE = 0.1f; - - private final List<Boolean> mResults = Collections.synchronizedList(new ArrayList<>()); - private CountDownLatch mReceivedResults = new CountDownLatch(1); - - private TrustedPresentationThresholds mThresholds = new TrustedPresentationThresholds( - 1 /* minAlpha */, FRACTION_VISIBLE, STABILITY_REQUIREMENT_MS); - - @Rule - public TestName mName = new TestName(); - - @Rule - public ActivityScenarioRule<TestActivity> mActivityRule = createFullscreenActivityScenarioRule( - TestActivity.class); - - private TestActivity mActivity; - - private SurfaceControlViewHost.SurfacePackage mSurfacePackage = null; - - @Before - public void setup() { - mActivityRule.getScenario().onActivity(activity -> mActivity = activity); - mDefaultListener = new Listener(mReceivedResults); - } - - @After - public void tearDown() { - if (mSurfacePackage != null) { - new SurfaceControl.Transaction().remove(mSurfacePackage.getSurfaceControl()).apply( - true); - mSurfacePackage.release(); - } - CommonUtils.waitUntilActivityRemoved(mActivity); - - } - - private class Listener implements Consumer<Boolean> { - final CountDownLatch mLatch; - - Listener(CountDownLatch latch) { - mLatch = latch; - } - - @Override - public void accept(Boolean inTrustedPresentationState) { - Log.d(TAG, "onTrustedPresentationChanged " + inTrustedPresentationState); - mResults.add(inTrustedPresentationState); - mLatch.countDown(); - } - } - - private Consumer<Boolean> mDefaultListener; - - @Test - public void testAddTrustedPresentationListenerOnWindow() { - WindowManager windowManager = mActivity.getSystemService(WindowManager.class); - windowManager.registerTrustedPresentationListener( - mActivity.getWindow().getDecorView().getWindowToken(), mThresholds, Runnable::run, - mDefaultListener); - assertResults(List.of(true)); - } - - @Test - public void testRemoveTrustedPresentationListenerOnWindow() throws InterruptedException { - WindowManager windowManager = mActivity.getSystemService(WindowManager.class); - windowManager.registerTrustedPresentationListener( - mActivity.getWindow().getDecorView().getWindowToken(), mThresholds, Runnable::run, - mDefaultListener); - assertResults(List.of(true)); - // reset the latch - mReceivedResults = new CountDownLatch(1); - - windowManager.unregisterTrustedPresentationListener(mDefaultListener); - mReceivedResults.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS); - // Ensure we waited the full time and never received a notify on the result from the - // callback. - assertEquals("Should never have received a callback", mReceivedResults.getCount(), 1); - // results shouldn't have changed. - assertEquals(mResults, List.of(true)); - } - - @Test - public void testRemovingUnknownListenerIsANoop() { - WindowManager windowManager = mActivity.getSystemService(WindowManager.class); - assertNotNull(windowManager); - windowManager.unregisterTrustedPresentationListener(mDefaultListener); - } - - @Test - public void testAddDuplicateListenerThrowsException() { - WindowManager windowManager = mActivity.getSystemService(WindowManager.class); - assertNotNull(windowManager); - windowManager.registerTrustedPresentationListener( - mActivity.getWindow().getDecorView().getWindowToken(), mThresholds, - Runnable::run, mDefaultListener); - assertThrows(AndroidRuntimeException.class, - () -> windowManager.registerTrustedPresentationListener( - mActivity.getWindow().getDecorView().getWindowToken(), mThresholds, - Runnable::run, mDefaultListener)); - } - - @Test - public void testAddDuplicateThresholds() { - mReceivedResults = new CountDownLatch(2); - mDefaultListener = new Listener(mReceivedResults); - WindowManager windowManager = mActivity.getSystemService(WindowManager.class); - windowManager.registerTrustedPresentationListener( - mActivity.getWindow().getDecorView().getWindowToken(), mThresholds, - Runnable::run, mDefaultListener); - - Consumer<Boolean> mNewListener = new Listener(mReceivedResults); - - windowManager.registerTrustedPresentationListener( - mActivity.getWindow().getDecorView().getWindowToken(), mThresholds, - Runnable::run, mNewListener); - assertResults(List.of(true, true)); - } - - private void waitForViewAttach(View view) { - final CountDownLatch viewAttached = new CountDownLatch(1); - view.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() { - @Override - public void onViewAttachedToWindow(@NonNull View v) { - viewAttached.countDown(); - } - - @Override - public void onViewDetachedFromWindow(@NonNull View v) { - - } - }); - try { - viewAttached.await(2000, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - if (!wait(viewAttached, 2000 /* waitTimeMs */)) { - fail("Couldn't attach view=" + view); - } - } - - @Test - public void testAddListenerToScvh() { - WindowManager windowManager = mActivity.getSystemService(WindowManager.class); - - var embeddedView = new View(mActivity); - mActivityRule.getScenario().onActivity(activity -> { - var attachedSurfaceControl = - mActivity.getWindow().getDecorView().getRootSurfaceControl(); - var scvh = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(), - attachedSurfaceControl.getHostToken()); - mSurfacePackage = scvh.getSurfacePackage(); - scvh.setView(embeddedView, mActivity.getWindow().getDecorView().getWidth(), - mActivity.getWindow().getDecorView().getHeight()); - attachedSurfaceControl.buildReparentTransaction( - mSurfacePackage.getSurfaceControl()); - }); - - waitForViewAttach(embeddedView); - windowManager.registerTrustedPresentationListener(embeddedView.getWindowToken(), - mThresholds, - Runnable::run, mDefaultListener); - - assertResults(List.of(true)); - } - - private boolean wait(CountDownLatch latch, long waitTimeMs) { - while (true) { - long now = SystemClock.uptimeMillis(); - try { - return latch.await(waitTimeMs, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - long elapsedTime = SystemClock.uptimeMillis() - now; - waitTimeMs = Math.max(0, waitTimeMs - elapsedTime); - } - } - - } - - @GuardedBy("mResultsLock") - private void assertResults(List<Boolean> results) { - if (!wait(mReceivedResults, WAIT_TIME_MS)) { - try { - CtsWindowInfoUtils.dumpWindowsOnScreen(TAG, "test " + mName.getMethodName()); - } catch (InterruptedException e) { - Log.d(TAG, "Couldn't dump windows", e); - } - Assert.fail("Timed out waiting for results mReceivedResults.count=" - + mReceivedResults.getCount() + "mReceivedResults=" + mReceivedResults); - } - - // Make sure we received the results - assertEquals(results.toArray(), mResults.toArray()); - } - - public static class TestActivity extends Activity { - } -} |