Loading core/java/android/view/SurfaceControl.java +111 −25 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; Loading Loading @@ -796,7 +797,7 @@ public final class SurfaceControl implements Parcelable { if (nativeObject != 0) { // Only add valid surface controls to the registry. This is called at the end of this // method since its information is dumped if the process threshold is reached. addToRegistry(); SurfaceControlRegistry.getProcessInstance().add(this); } } Loading Loading @@ -1501,7 +1502,7 @@ public final class SurfaceControl implements Parcelable { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } removeFromRegistry(); SurfaceControlRegistry.getProcessInstance().remove(this); } finally { super.finalize(); } Loading @@ -1519,6 +1520,10 @@ public final class SurfaceControl implements Parcelable { */ public void release() { if (mNativeObject != 0) { if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "release", null, this, null); } mFreeNativeResources.run(); mNativeObject = 0; mNativeHandle = 0; Loading @@ -1532,7 +1537,7 @@ public final class SurfaceControl implements Parcelable { mChoreographer = null; } } removeFromRegistry(); SurfaceControlRegistry.getProcessInstance().remove(this); } } Loading Loading @@ -2765,8 +2770,10 @@ public final class SurfaceControl implements Parcelable { private Transaction(long nativeObject) { mNativeObject = nativeObject; mFreeNativeResources = sRegistry.registerNativeAllocation(this, mNativeObject); mFreeNativeResources = sRegistry.registerNativeAllocation(this, mNativeObject); if (!SurfaceControlRegistry.sCallStackDebuggingInitialized) { SurfaceControlRegistry.initializeCallStackDebugging(); } } private Transaction(Parcel in) { Loading Loading @@ -2845,6 +2852,11 @@ public final class SurfaceControl implements Parcelable { applyResizedSurfaces(); notifyReparentedSurfaces(); nativeApplyTransaction(mNativeObject, sync, oneWay); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "apply", this, null, null); } } /** Loading Loading @@ -2920,6 +2932,10 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage public Transaction show(SurfaceControl sc) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "show", this, sc, null); } nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN); return this; } Loading @@ -2934,6 +2950,10 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage public Transaction hide(SurfaceControl sc) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "hide", this, sc, null); } nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN); return this; } Loading @@ -2950,6 +2970,10 @@ public final class SurfaceControl implements Parcelable { @NonNull public Transaction setPosition(@NonNull SurfaceControl sc, float x, float y) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setPosition", this, sc, "x=" + x + " y=" + y); } nativeSetPosition(mNativeObject, sc.mNativeObject, x, y); return this; } Loading @@ -2968,6 +2992,10 @@ public final class SurfaceControl implements Parcelable { checkPreconditions(sc); Preconditions.checkArgument(scaleX >= 0, "Negative value passed in for scaleX"); Preconditions.checkArgument(scaleY >= 0, "Negative value passed in for scaleY"); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setScale", this, sc, "sx=" + scaleX + " sy=" + scaleY); } nativeSetScale(mNativeObject, sc.mNativeObject, scaleX, scaleY); return this; } Loading @@ -2985,6 +3013,10 @@ public final class SurfaceControl implements Parcelable { public Transaction setBufferSize(@NonNull SurfaceControl sc, @IntRange(from = 0) int w, @IntRange(from = 0) int h) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setBufferSize", this, sc, "w=" + w + " h=" + h); } mResizedSurfaces.put(sc, new Point(w, h)); return this; } Loading @@ -3005,6 +3037,10 @@ public final class SurfaceControl implements Parcelable { public Transaction setFixedTransformHint(@NonNull SurfaceControl sc, @Surface.Rotation int transformHint) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setFixedTransformHint", this, sc, "hint=" + transformHint); } nativeSetFixedTransformHint(mNativeObject, sc.mNativeObject, transformHint); return this; } Loading @@ -3018,6 +3054,10 @@ public final class SurfaceControl implements Parcelable { @NonNull public Transaction unsetFixedTransformHint(@NonNull SurfaceControl sc) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "unsetFixedTransformHint", this, sc, null); } nativeSetFixedTransformHint(mNativeObject, sc.mNativeObject, -1/* INVALID_ROTATION */); return this; } Loading @@ -3035,6 +3075,10 @@ public final class SurfaceControl implements Parcelable { public Transaction setLayer(@NonNull SurfaceControl sc, @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int z) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setLayer", this, sc, "z=" + z); } nativeSetLayer(mNativeObject, sc.mNativeObject, z); return this; } Loading @@ -3044,6 +3088,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, int z) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setRelativeLayer", this, sc, "relTo=" + relativeTo + " z=" + z); } nativeSetRelativeLayer(mNativeObject, sc.mNativeObject, relativeTo.mNativeObject, z); return this; } Loading @@ -3053,6 +3101,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setTransparentRegionHint(SurfaceControl sc, Region transparentRegion) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "unsetFixedTransformHint", this, sc, "region=" + transparentRegion); } nativeSetTransparentRegionHint(mNativeObject, sc.mNativeObject, transparentRegion); return this; Loading @@ -3069,6 +3121,10 @@ public final class SurfaceControl implements Parcelable { public Transaction setAlpha(@NonNull SurfaceControl sc, @FloatRange(from = 0.0, to = 1.0) float alpha) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setAlpha", this, sc, "alpha=" + alpha); } nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha); return this; } Loading Loading @@ -3124,6 +3180,11 @@ public final class SurfaceControl implements Parcelable { public Transaction setMatrix(SurfaceControl sc, float dsdx, float dtdx, float dtdy, float dsdy) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setMatrix", this, sc, "dsdx=" + dsdx + " dtdx=" + dtdx + " dtdy=" + dtdy + " dsdy=" + dsdy); } nativeSetMatrix(mNativeObject, sc.mNativeObject, dsdx, dtdx, dtdy, dsdy); return this; Loading Loading @@ -3189,6 +3250,10 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage public Transaction setWindowCrop(SurfaceControl sc, Rect crop) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setWindowCrop", this, sc, "crop=" + crop); } if (crop != null) { nativeSetWindowCrop(mNativeObject, sc.mNativeObject, crop.left, crop.top, crop.right, crop.bottom); Loading @@ -3211,6 +3276,10 @@ public final class SurfaceControl implements Parcelable { */ public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setCrop", this, sc, "crop=" + crop); } if (crop != null) { Preconditions.checkArgument(crop.isValid(), "Crop isn't valid."); nativeSetWindowCrop(mNativeObject, sc.mNativeObject, Loading @@ -3233,6 +3302,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setWindowCrop(SurfaceControl sc, int width, int height) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setCornerRadius", this, sc, "w=" + width + " h=" + height); } nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, width, height); return this; } Loading @@ -3247,6 +3320,10 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setCornerRadius", this, sc, "cornerRadius=" + cornerRadius); } nativeSetCornerRadius(mNativeObject, sc.mNativeObject, cornerRadius); return this; Loading @@ -3262,6 +3339,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setBackgroundBlurRadius(SurfaceControl sc, int radius) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setBackgroundBlurRadius", this, sc, "radius=" + radius); } nativeSetBackgroundBlurRadius(mNativeObject, sc.mNativeObject, radius); return this; } Loading Loading @@ -3318,6 +3399,10 @@ public final class SurfaceControl implements Parcelable { public Transaction reparent(@NonNull SurfaceControl sc, @Nullable SurfaceControl newParent) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "reparent", this, sc, "newParent=" + newParent); } long otherObject = 0; if (newParent != null) { newParent.checkNotReleased(); Loading @@ -3337,6 +3422,11 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage public Transaction setColor(SurfaceControl sc, @Size(3) float[] color) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "reparent", this, sc, "r=" + color[0] + " g=" + color[1] + " b=" + color[2]); } nativeSetColor(mNativeObject, sc.mNativeObject, color); return this; } Loading @@ -3347,6 +3437,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction unsetColor(SurfaceControl sc) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "unsetColor", this, sc, null); } nativeSetColor(mNativeObject, sc.mNativeObject, INVALID_COLOR); return this; } Loading @@ -3358,6 +3452,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setSecure(SurfaceControl sc, boolean isSecure) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setSecure", this, sc, "secure=" + isSecure); } if (isSecure) { nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE); } else { Loading Loading @@ -3411,6 +3509,10 @@ public final class SurfaceControl implements Parcelable { @NonNull public Transaction setOpaque(@NonNull SurfaceControl sc, boolean isOpaque) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setOpaque", this, sc, "opaque=" + isOpaque); } if (isOpaque) { nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE); } else { Loading Loading @@ -3580,6 +3682,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setShadowRadius", this, sc, "radius=" + shadowRadius); } nativeSetShadowRadius(mNativeObject, sc.mNativeObject, shadowRadius); return this; } Loading Loading @@ -4463,26 +4569,6 @@ public final class SurfaceControl implements Parcelable { return -1; } /** * Adds this surface control to the registry for this process if it is created. */ private void addToRegistry() { final SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance(); if (registry != null) { registry.add(this); } } /** * Removes this surface control from the registry for this process. */ private void removeFromRegistry() { final SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance(); if (registry != null) { registry.remove(this); } } // Called by native private static void invokeReleaseCallback(Consumer<SyncFence> callback, long nativeFencePtr) { SyncFence fence = new SyncFence(nativeFencePtr); Loading core/java/android/view/SurfaceControlRegistry.java +133 −3 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.content.Context; import android.os.Build; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -104,6 +106,9 @@ public class SurfaceControlRegistry { // Number of surface controls to dump when the max threshold is exceeded private static final int DUMP_LIMIT = 256; // An instance of a registry that is a no-op private static final SurfaceControlRegistry NO_OP_REGISTRY = new NoOpRegistry(); // Static lock, must be held for all registry operations private static final Object sLock = new Object(); Loading @@ -113,6 +118,22 @@ public class SurfaceControlRegistry { // The registry for a given process private static volatile SurfaceControlRegistry sProcessRegistry; // Whether call stack debugging has been initialized. This is evaluated only once per process // instance when the first SurfaceControl.Transaction object is created static boolean sCallStackDebuggingInitialized; // Whether call stack debugging is currently enabled, ie. whether there is a valid match string // for either a specific surface control name or surface control transaction method static boolean sCallStackDebuggingEnabled; // The name of the surface control to log stack traces for. Always non-null if // sCallStackDebuggingEnabled is true. Can be combined with the match call. private static String sCallStackDebuggingMatchName; // The surface control transaction method name to log stack traces for. Always non-null if // sCallStackDebuggingEnabled is true. Can be combined with the match name. private static String sCallStackDebuggingMatchCall; // Mapping of the active SurfaceControls to the elapsed time when they were registered @GuardedBy("sLock") private final WeakHashMap<SurfaceControl, Long> mSurfaceControls; Loading Loading @@ -160,6 +181,12 @@ public class SurfaceControlRegistry { } } @VisibleForTesting public void setCallStackDebuggingParams(String matchName, String matchCall) { sCallStackDebuggingMatchName = matchName.toLowerCase(); sCallStackDebuggingMatchCall = matchCall.toLowerCase(); } /** * Creates and initializes the registry for all SurfaceControls in this process. The caller must * hold the READ_FRAME_BUFFER permission. Loading Loading @@ -196,11 +223,9 @@ public class SurfaceControlRegistry { * createProcessInstance(Context) was previously called from a valid caller. * @hide */ @Nullable @VisibleForTesting public static SurfaceControlRegistry getProcessInstance() { synchronized (sLock) { return sProcessRegistry; return sProcessRegistry != null ? sProcessRegistry : NO_OP_REGISTRY; } } Loading Loading @@ -247,6 +272,91 @@ public class SurfaceControlRegistry { } } /** * Initializes global call stack debugging if this is a debug build and a filter is specified. * This is a no-op if * * Usage: * adb shell setprop persist.wm.debug.sc.tx.log_match_call <call or \"\" to unset> * adb shell setprop persist.wm.debug.sc.tx.log_match_name <name or \"\" to unset> * adb reboot */ final static void initializeCallStackDebugging() { if (sCallStackDebuggingInitialized || !Build.IS_DEBUGGABLE) { // Return early if already initialized or this is not a debug build return; } sCallStackDebuggingInitialized = true; sCallStackDebuggingMatchCall = SystemProperties.get("persist.wm.debug.sc.tx.log_match_call", null) .toLowerCase(); sCallStackDebuggingMatchName = SystemProperties.get("persist.wm.debug.sc.tx.log_match_name", null) .toLowerCase(); // Only enable stack debugging if any of the match filters are set sCallStackDebuggingEnabled = (!sCallStackDebuggingMatchCall.isEmpty() || !sCallStackDebuggingMatchName.isEmpty()); if (sCallStackDebuggingEnabled) { Log.d(TAG, "Enabling transaction call stack debugging:" + " matchCall=" + sCallStackDebuggingMatchCall + " matchName=" + sCallStackDebuggingMatchName); } } /** * Dumps the callstack if it matches the global debug properties. Caller should first verify * {@link #sCallStackDebuggingEnabled} is true. * * @param call the name of the call * @param tx (optional) the transaction associated with this call * @param sc the affected surface * @param details additional details to print with the stack track */ final void checkCallStackDebugging(@NonNull String call, @Nullable SurfaceControl.Transaction tx, @Nullable SurfaceControl sc, @Nullable String details) { if (!sCallStackDebuggingEnabled) { return; } if (!matchesForCallStackDebugging(sc != null ? sc.getName() : null, call)) { return; } final String txMsg = tx != null ? "tx=" + tx.getId() + " ": ""; final String scMsg = sc != null ? " sc=" + sc.getName() + "": ""; final String msg = details != null ? call + " (" + txMsg + scMsg + ") " + details : call + " (" + txMsg + scMsg + ")"; Log.e(TAG, msg, new Throwable()); } /** * Tests whether the given surface control name/method call matches the filters set for the * call stack debugging. */ @VisibleForTesting public final boolean matchesForCallStackDebugging(@Nullable String name, @NonNull String call) { final boolean matchCall = !sCallStackDebuggingMatchCall.isEmpty(); if (matchCall && !call.toLowerCase().contains(sCallStackDebuggingMatchCall)) { // Skip if target call doesn't match requested caller return false; } final boolean matchName = !sCallStackDebuggingMatchName.isEmpty(); if (matchName && (name == null || !name.toLowerCase().contains(sCallStackDebuggingMatchName))) { // Skip if target surface doesn't match requested surface return false; } return true; } /** * Returns whether call stack debugging is enabled for this process. */ final static boolean isCallStackDebuggingEnabled() { return sCallStackDebuggingEnabled; } /** * Forces the gc and finalizers to run, used prior to dumping to ensure we only dump strongly * referenced surface controls. Loading @@ -272,7 +382,27 @@ public class SurfaceControlRegistry { synchronized (sLock) { if (sProcessRegistry != null) { sDefaultReporter.onMaxLayersExceeded(sProcessRegistry.mSurfaceControls, limit, pw); pw.println("sCallStackDebuggingInitialized=" + sCallStackDebuggingInitialized); pw.println("sCallStackDebuggingEnabled=" + sCallStackDebuggingEnabled); pw.println("sCallStackDebuggingMatchName=" + sCallStackDebuggingMatchName); pw.println("sCallStackDebuggingMatchCall=" + sCallStackDebuggingMatchCall); } } } /** * A no-op implementation of the registry. */ private static class NoOpRegistry extends SurfaceControlRegistry { @Override public void setReportingThresholds(int maxLayersReportingThreshold, int resetReportingThreshold, Reporter reporter) {} @Override void add(SurfaceControl sc) {} @Override void remove(SurfaceControl sc) {} } } core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java +23 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.eq; Loading Loading @@ -148,6 +149,28 @@ public class SurfaceControlRegistryTests { reporter.assertLastReportedSetEquals(sc5, sc6, sc7, sc8); } @Test public void testCallStackDebugging_matchesFilters() { SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance(); // Specific name, any call registry.setCallStackDebuggingParams("com.android.app1", ""); assertFalse(registry.matchesForCallStackDebugging("com.android.noMatchApp", "setAlpha")); assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha")); // Any name, specific call registry.setCallStackDebuggingParams("", "setAlpha"); assertFalse(registry.matchesForCallStackDebugging("com.android.app1", "setLayer")); assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha")); assertTrue(registry.matchesForCallStackDebugging("com.android.app2", "setAlpha")); // Specific name, specific call registry.setCallStackDebuggingParams("com.android.app1", "setAlpha"); assertFalse(registry.matchesForCallStackDebugging("com.android.app1", "setLayer")); assertFalse(registry.matchesForCallStackDebugging("com.android.app2", "setAlpha")); assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha")); } private SurfaceControl buildTestSurface() { return new SurfaceControl.Builder() .setContainerLayer() Loading Loading
core/java/android/view/SurfaceControl.java +111 −25 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.util.ArrayMap; import android.util.Log; import android.util.Slog; Loading Loading @@ -796,7 +797,7 @@ public final class SurfaceControl implements Parcelable { if (nativeObject != 0) { // Only add valid surface controls to the registry. This is called at the end of this // method since its information is dumped if the process threshold is reached. addToRegistry(); SurfaceControlRegistry.getProcessInstance().add(this); } } Loading Loading @@ -1501,7 +1502,7 @@ public final class SurfaceControl implements Parcelable { if (mCloseGuard != null) { mCloseGuard.warnIfOpen(); } removeFromRegistry(); SurfaceControlRegistry.getProcessInstance().remove(this); } finally { super.finalize(); } Loading @@ -1519,6 +1520,10 @@ public final class SurfaceControl implements Parcelable { */ public void release() { if (mNativeObject != 0) { if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "release", null, this, null); } mFreeNativeResources.run(); mNativeObject = 0; mNativeHandle = 0; Loading @@ -1532,7 +1537,7 @@ public final class SurfaceControl implements Parcelable { mChoreographer = null; } } removeFromRegistry(); SurfaceControlRegistry.getProcessInstance().remove(this); } } Loading Loading @@ -2765,8 +2770,10 @@ public final class SurfaceControl implements Parcelable { private Transaction(long nativeObject) { mNativeObject = nativeObject; mFreeNativeResources = sRegistry.registerNativeAllocation(this, mNativeObject); mFreeNativeResources = sRegistry.registerNativeAllocation(this, mNativeObject); if (!SurfaceControlRegistry.sCallStackDebuggingInitialized) { SurfaceControlRegistry.initializeCallStackDebugging(); } } private Transaction(Parcel in) { Loading Loading @@ -2845,6 +2852,11 @@ public final class SurfaceControl implements Parcelable { applyResizedSurfaces(); notifyReparentedSurfaces(); nativeApplyTransaction(mNativeObject, sync, oneWay); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "apply", this, null, null); } } /** Loading Loading @@ -2920,6 +2932,10 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage public Transaction show(SurfaceControl sc) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "show", this, sc, null); } nativeSetFlags(mNativeObject, sc.mNativeObject, 0, SURFACE_HIDDEN); return this; } Loading @@ -2934,6 +2950,10 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage public Transaction hide(SurfaceControl sc) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "hide", this, sc, null); } nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN); return this; } Loading @@ -2950,6 +2970,10 @@ public final class SurfaceControl implements Parcelable { @NonNull public Transaction setPosition(@NonNull SurfaceControl sc, float x, float y) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setPosition", this, sc, "x=" + x + " y=" + y); } nativeSetPosition(mNativeObject, sc.mNativeObject, x, y); return this; } Loading @@ -2968,6 +2992,10 @@ public final class SurfaceControl implements Parcelable { checkPreconditions(sc); Preconditions.checkArgument(scaleX >= 0, "Negative value passed in for scaleX"); Preconditions.checkArgument(scaleY >= 0, "Negative value passed in for scaleY"); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setScale", this, sc, "sx=" + scaleX + " sy=" + scaleY); } nativeSetScale(mNativeObject, sc.mNativeObject, scaleX, scaleY); return this; } Loading @@ -2985,6 +3013,10 @@ public final class SurfaceControl implements Parcelable { public Transaction setBufferSize(@NonNull SurfaceControl sc, @IntRange(from = 0) int w, @IntRange(from = 0) int h) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setBufferSize", this, sc, "w=" + w + " h=" + h); } mResizedSurfaces.put(sc, new Point(w, h)); return this; } Loading @@ -3005,6 +3037,10 @@ public final class SurfaceControl implements Parcelable { public Transaction setFixedTransformHint(@NonNull SurfaceControl sc, @Surface.Rotation int transformHint) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setFixedTransformHint", this, sc, "hint=" + transformHint); } nativeSetFixedTransformHint(mNativeObject, sc.mNativeObject, transformHint); return this; } Loading @@ -3018,6 +3054,10 @@ public final class SurfaceControl implements Parcelable { @NonNull public Transaction unsetFixedTransformHint(@NonNull SurfaceControl sc) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "unsetFixedTransformHint", this, sc, null); } nativeSetFixedTransformHint(mNativeObject, sc.mNativeObject, -1/* INVALID_ROTATION */); return this; } Loading @@ -3035,6 +3075,10 @@ public final class SurfaceControl implements Parcelable { public Transaction setLayer(@NonNull SurfaceControl sc, @IntRange(from = Integer.MIN_VALUE, to = Integer.MAX_VALUE) int z) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setLayer", this, sc, "z=" + z); } nativeSetLayer(mNativeObject, sc.mNativeObject, z); return this; } Loading @@ -3044,6 +3088,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setRelativeLayer(SurfaceControl sc, SurfaceControl relativeTo, int z) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setRelativeLayer", this, sc, "relTo=" + relativeTo + " z=" + z); } nativeSetRelativeLayer(mNativeObject, sc.mNativeObject, relativeTo.mNativeObject, z); return this; } Loading @@ -3053,6 +3101,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setTransparentRegionHint(SurfaceControl sc, Region transparentRegion) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "unsetFixedTransformHint", this, sc, "region=" + transparentRegion); } nativeSetTransparentRegionHint(mNativeObject, sc.mNativeObject, transparentRegion); return this; Loading @@ -3069,6 +3121,10 @@ public final class SurfaceControl implements Parcelable { public Transaction setAlpha(@NonNull SurfaceControl sc, @FloatRange(from = 0.0, to = 1.0) float alpha) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setAlpha", this, sc, "alpha=" + alpha); } nativeSetAlpha(mNativeObject, sc.mNativeObject, alpha); return this; } Loading Loading @@ -3124,6 +3180,11 @@ public final class SurfaceControl implements Parcelable { public Transaction setMatrix(SurfaceControl sc, float dsdx, float dtdx, float dtdy, float dsdy) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setMatrix", this, sc, "dsdx=" + dsdx + " dtdx=" + dtdx + " dtdy=" + dtdy + " dsdy=" + dsdy); } nativeSetMatrix(mNativeObject, sc.mNativeObject, dsdx, dtdx, dtdy, dsdy); return this; Loading Loading @@ -3189,6 +3250,10 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage public Transaction setWindowCrop(SurfaceControl sc, Rect crop) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setWindowCrop", this, sc, "crop=" + crop); } if (crop != null) { nativeSetWindowCrop(mNativeObject, sc.mNativeObject, crop.left, crop.top, crop.right, crop.bottom); Loading @@ -3211,6 +3276,10 @@ public final class SurfaceControl implements Parcelable { */ public @NonNull Transaction setCrop(@NonNull SurfaceControl sc, @Nullable Rect crop) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setCrop", this, sc, "crop=" + crop); } if (crop != null) { Preconditions.checkArgument(crop.isValid(), "Crop isn't valid."); nativeSetWindowCrop(mNativeObject, sc.mNativeObject, Loading @@ -3233,6 +3302,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setWindowCrop(SurfaceControl sc, int width, int height) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setCornerRadius", this, sc, "w=" + width + " h=" + height); } nativeSetWindowCrop(mNativeObject, sc.mNativeObject, 0, 0, width, height); return this; } Loading @@ -3247,6 +3320,10 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) public Transaction setCornerRadius(SurfaceControl sc, float cornerRadius) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setCornerRadius", this, sc, "cornerRadius=" + cornerRadius); } nativeSetCornerRadius(mNativeObject, sc.mNativeObject, cornerRadius); return this; Loading @@ -3262,6 +3339,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setBackgroundBlurRadius(SurfaceControl sc, int radius) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setBackgroundBlurRadius", this, sc, "radius=" + radius); } nativeSetBackgroundBlurRadius(mNativeObject, sc.mNativeObject, radius); return this; } Loading Loading @@ -3318,6 +3399,10 @@ public final class SurfaceControl implements Parcelable { public Transaction reparent(@NonNull SurfaceControl sc, @Nullable SurfaceControl newParent) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "reparent", this, sc, "newParent=" + newParent); } long otherObject = 0; if (newParent != null) { newParent.checkNotReleased(); Loading @@ -3337,6 +3422,11 @@ public final class SurfaceControl implements Parcelable { @UnsupportedAppUsage public Transaction setColor(SurfaceControl sc, @Size(3) float[] color) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "reparent", this, sc, "r=" + color[0] + " g=" + color[1] + " b=" + color[2]); } nativeSetColor(mNativeObject, sc.mNativeObject, color); return this; } Loading @@ -3347,6 +3437,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction unsetColor(SurfaceControl sc) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "unsetColor", this, sc, null); } nativeSetColor(mNativeObject, sc.mNativeObject, INVALID_COLOR); return this; } Loading @@ -3358,6 +3452,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setSecure(SurfaceControl sc, boolean isSecure) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setSecure", this, sc, "secure=" + isSecure); } if (isSecure) { nativeSetFlags(mNativeObject, sc.mNativeObject, SECURE, SECURE); } else { Loading Loading @@ -3411,6 +3509,10 @@ public final class SurfaceControl implements Parcelable { @NonNull public Transaction setOpaque(@NonNull SurfaceControl sc, boolean isOpaque) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setOpaque", this, sc, "opaque=" + isOpaque); } if (isOpaque) { nativeSetFlags(mNativeObject, sc.mNativeObject, SURFACE_OPAQUE, SURFACE_OPAQUE); } else { Loading Loading @@ -3580,6 +3682,10 @@ public final class SurfaceControl implements Parcelable { */ public Transaction setShadowRadius(SurfaceControl sc, float shadowRadius) { checkPreconditions(sc); if (SurfaceControlRegistry.sCallStackDebuggingEnabled) { SurfaceControlRegistry.getProcessInstance().checkCallStackDebugging( "setShadowRadius", this, sc, "radius=" + shadowRadius); } nativeSetShadowRadius(mNativeObject, sc.mNativeObject, shadowRadius); return this; } Loading Loading @@ -4463,26 +4569,6 @@ public final class SurfaceControl implements Parcelable { return -1; } /** * Adds this surface control to the registry for this process if it is created. */ private void addToRegistry() { final SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance(); if (registry != null) { registry.add(this); } } /** * Removes this surface control from the registry for this process. */ private void removeFromRegistry() { final SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance(); if (registry != null) { registry.remove(this); } } // Called by native private static void invokeReleaseCallback(Consumer<SyncFence> callback, long nativeFencePtr) { SyncFence fence = new SyncFence(nativeFencePtr); Loading
core/java/android/view/SurfaceControlRegistry.java +133 −3 Original line number Diff line number Diff line Loading @@ -23,7 +23,9 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.content.Context; import android.os.Build; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; import com.android.internal.annotations.GuardedBy; Loading Loading @@ -104,6 +106,9 @@ public class SurfaceControlRegistry { // Number of surface controls to dump when the max threshold is exceeded private static final int DUMP_LIMIT = 256; // An instance of a registry that is a no-op private static final SurfaceControlRegistry NO_OP_REGISTRY = new NoOpRegistry(); // Static lock, must be held for all registry operations private static final Object sLock = new Object(); Loading @@ -113,6 +118,22 @@ public class SurfaceControlRegistry { // The registry for a given process private static volatile SurfaceControlRegistry sProcessRegistry; // Whether call stack debugging has been initialized. This is evaluated only once per process // instance when the first SurfaceControl.Transaction object is created static boolean sCallStackDebuggingInitialized; // Whether call stack debugging is currently enabled, ie. whether there is a valid match string // for either a specific surface control name or surface control transaction method static boolean sCallStackDebuggingEnabled; // The name of the surface control to log stack traces for. Always non-null if // sCallStackDebuggingEnabled is true. Can be combined with the match call. private static String sCallStackDebuggingMatchName; // The surface control transaction method name to log stack traces for. Always non-null if // sCallStackDebuggingEnabled is true. Can be combined with the match name. private static String sCallStackDebuggingMatchCall; // Mapping of the active SurfaceControls to the elapsed time when they were registered @GuardedBy("sLock") private final WeakHashMap<SurfaceControl, Long> mSurfaceControls; Loading Loading @@ -160,6 +181,12 @@ public class SurfaceControlRegistry { } } @VisibleForTesting public void setCallStackDebuggingParams(String matchName, String matchCall) { sCallStackDebuggingMatchName = matchName.toLowerCase(); sCallStackDebuggingMatchCall = matchCall.toLowerCase(); } /** * Creates and initializes the registry for all SurfaceControls in this process. The caller must * hold the READ_FRAME_BUFFER permission. Loading Loading @@ -196,11 +223,9 @@ public class SurfaceControlRegistry { * createProcessInstance(Context) was previously called from a valid caller. * @hide */ @Nullable @VisibleForTesting public static SurfaceControlRegistry getProcessInstance() { synchronized (sLock) { return sProcessRegistry; return sProcessRegistry != null ? sProcessRegistry : NO_OP_REGISTRY; } } Loading Loading @@ -247,6 +272,91 @@ public class SurfaceControlRegistry { } } /** * Initializes global call stack debugging if this is a debug build and a filter is specified. * This is a no-op if * * Usage: * adb shell setprop persist.wm.debug.sc.tx.log_match_call <call or \"\" to unset> * adb shell setprop persist.wm.debug.sc.tx.log_match_name <name or \"\" to unset> * adb reboot */ final static void initializeCallStackDebugging() { if (sCallStackDebuggingInitialized || !Build.IS_DEBUGGABLE) { // Return early if already initialized or this is not a debug build return; } sCallStackDebuggingInitialized = true; sCallStackDebuggingMatchCall = SystemProperties.get("persist.wm.debug.sc.tx.log_match_call", null) .toLowerCase(); sCallStackDebuggingMatchName = SystemProperties.get("persist.wm.debug.sc.tx.log_match_name", null) .toLowerCase(); // Only enable stack debugging if any of the match filters are set sCallStackDebuggingEnabled = (!sCallStackDebuggingMatchCall.isEmpty() || !sCallStackDebuggingMatchName.isEmpty()); if (sCallStackDebuggingEnabled) { Log.d(TAG, "Enabling transaction call stack debugging:" + " matchCall=" + sCallStackDebuggingMatchCall + " matchName=" + sCallStackDebuggingMatchName); } } /** * Dumps the callstack if it matches the global debug properties. Caller should first verify * {@link #sCallStackDebuggingEnabled} is true. * * @param call the name of the call * @param tx (optional) the transaction associated with this call * @param sc the affected surface * @param details additional details to print with the stack track */ final void checkCallStackDebugging(@NonNull String call, @Nullable SurfaceControl.Transaction tx, @Nullable SurfaceControl sc, @Nullable String details) { if (!sCallStackDebuggingEnabled) { return; } if (!matchesForCallStackDebugging(sc != null ? sc.getName() : null, call)) { return; } final String txMsg = tx != null ? "tx=" + tx.getId() + " ": ""; final String scMsg = sc != null ? " sc=" + sc.getName() + "": ""; final String msg = details != null ? call + " (" + txMsg + scMsg + ") " + details : call + " (" + txMsg + scMsg + ")"; Log.e(TAG, msg, new Throwable()); } /** * Tests whether the given surface control name/method call matches the filters set for the * call stack debugging. */ @VisibleForTesting public final boolean matchesForCallStackDebugging(@Nullable String name, @NonNull String call) { final boolean matchCall = !sCallStackDebuggingMatchCall.isEmpty(); if (matchCall && !call.toLowerCase().contains(sCallStackDebuggingMatchCall)) { // Skip if target call doesn't match requested caller return false; } final boolean matchName = !sCallStackDebuggingMatchName.isEmpty(); if (matchName && (name == null || !name.toLowerCase().contains(sCallStackDebuggingMatchName))) { // Skip if target surface doesn't match requested surface return false; } return true; } /** * Returns whether call stack debugging is enabled for this process. */ final static boolean isCallStackDebuggingEnabled() { return sCallStackDebuggingEnabled; } /** * Forces the gc and finalizers to run, used prior to dumping to ensure we only dump strongly * referenced surface controls. Loading @@ -272,7 +382,27 @@ public class SurfaceControlRegistry { synchronized (sLock) { if (sProcessRegistry != null) { sDefaultReporter.onMaxLayersExceeded(sProcessRegistry.mSurfaceControls, limit, pw); pw.println("sCallStackDebuggingInitialized=" + sCallStackDebuggingInitialized); pw.println("sCallStackDebuggingEnabled=" + sCallStackDebuggingEnabled); pw.println("sCallStackDebuggingMatchName=" + sCallStackDebuggingMatchName); pw.println("sCallStackDebuggingMatchCall=" + sCallStackDebuggingMatchCall); } } } /** * A no-op implementation of the registry. */ private static class NoOpRegistry extends SurfaceControlRegistry { @Override public void setReportingThresholds(int maxLayersReportingThreshold, int resetReportingThreshold, Reporter reporter) {} @Override void add(SurfaceControl sc) {} @Override void remove(SurfaceControl sc) {} } }
core/tests/coretests/src/android/view/SurfaceControlRegistryTests.java +23 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.eq; Loading Loading @@ -148,6 +149,28 @@ public class SurfaceControlRegistryTests { reporter.assertLastReportedSetEquals(sc5, sc6, sc7, sc8); } @Test public void testCallStackDebugging_matchesFilters() { SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance(); // Specific name, any call registry.setCallStackDebuggingParams("com.android.app1", ""); assertFalse(registry.matchesForCallStackDebugging("com.android.noMatchApp", "setAlpha")); assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha")); // Any name, specific call registry.setCallStackDebuggingParams("", "setAlpha"); assertFalse(registry.matchesForCallStackDebugging("com.android.app1", "setLayer")); assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha")); assertTrue(registry.matchesForCallStackDebugging("com.android.app2", "setAlpha")); // Specific name, specific call registry.setCallStackDebuggingParams("com.android.app1", "setAlpha"); assertFalse(registry.matchesForCallStackDebugging("com.android.app1", "setLayer")); assertFalse(registry.matchesForCallStackDebugging("com.android.app2", "setAlpha")); assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha")); } private SurfaceControl buildTestSurface() { return new SurfaceControl.Builder() .setContainerLayer() Loading