Loading motiontoollib/src/com/android/app/motiontool/MotionToolManager.kt +2 −5 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.view.Choreographer import android.view.View import android.view.WindowManagerGlobal import androidx.annotation.VisibleForTesting import com.android.app.viewcapture.SimpleViewCapture import com.android.app.viewcapture.ViewCapture import com.android.app.viewcapture.data.MotionWindowData Loading @@ -43,7 +44,7 @@ import com.android.app.viewcapture.data.MotionWindowData * @see [DdmHandleMotionTool] */ class MotionToolManager private constructor(private val windowManagerGlobal: WindowManagerGlobal) { private val viewCapture: ViewCapture = SimpleViewCapture() private val viewCapture: ViewCapture = SimpleViewCapture("MTViewCapture") companion object { private const val TAG = "MotionToolManager" Loading Loading @@ -135,10 +136,6 @@ class MotionToolManager private constructor(private val windowManagerGlobal: Win private fun getRootView(windowId: String): View? { return windowManagerGlobal.getRootView(windowId) } class SimpleViewCapture : ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE, MAIN_EXECUTOR.submit { Choreographer.getInstance() }.get(), createAndStartNewLooperExecutor("MTViewCapture", Process.THREAD_PRIORITY_FOREGROUND)) } private data class TraceMetadata( Loading viewcapturelib/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt +10 −1 Original line number Diff line number Diff line Loading @@ -24,12 +24,15 @@ import android.os.Looper import android.os.ParcelFileDescriptor import android.os.Process import android.provider.Settings import android.util.Log import android.view.Choreographer import android.window.IDumpCallback import androidx.annotation.AnyThread import androidx.annotation.VisibleForTesting import java.util.concurrent.Executor private val TAG = SettingsAwareViewCapture::class.java.simpleName /** * ViewCapture that listens to system updates and enables / disables attached ViewCapture * WindowListeners accordingly. The Settings toggle is currently controlled by the Winscope Loading @@ -41,7 +44,13 @@ internal constructor(private val context: Context, choreographer: Choreographer, : ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE, choreographer, executor) { /** Dumps all the active view captures to the wm trace directory via LauncherAppService */ private val mDumpCallback: IDumpCallback.Stub = object : IDumpCallback.Stub() { override fun onDump(out: ParcelFileDescriptor) = dumpTo(out, context) override fun onDump(out: ParcelFileDescriptor) { try { ParcelFileDescriptor.AutoCloseOutputStream(out).use { os -> dumpTo(os, context) } } catch (e: Exception) { Log.e(TAG, "failed to dump data to wm trace", e) } } } init { Loading viewcapturelib/src/com/android/app/viewcapture/SimpleViewCapture.kt 0 → 100644 +8 −0 Original line number Diff line number Diff line package com.android.app.viewcapture import android.os.Process import android.view.Choreographer open class SimpleViewCapture(threadName: String) : ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE, MAIN_EXECUTOR.submit { Choreographer.getInstance() }.get(), createAndStartNewLooperExecutor(threadName, Process.THREAD_PRIORITY_FOREGROUND)) No newline at end of file viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java +36 −17 Original line number Diff line number Diff line Loading @@ -21,10 +21,8 @@ import android.content.res.Resources; import android.media.permission.SafeCloseable; import android.os.HandlerThread; import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.Trace; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.view.Choreographer; import android.view.View; Loading @@ -33,8 +31,10 @@ import android.view.ViewTreeObserver; import android.view.Window; import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; import com.android.app.viewcapture.data.ExportedData; Loading Loading @@ -120,6 +120,7 @@ public abstract class ViewCapture { /** * Attaches the ViewCapture to the provided window and returns a handle to detach the listener */ @NonNull public SafeCloseable startCapture(Window window) { String title = window.getAttributes().getTitle().toString(); String name = TextUtils.isEmpty(title) ? window.toString() : title; Loading @@ -130,6 +131,7 @@ public abstract class ViewCapture { * Attaches the ViewCapture to the provided window and returns a handle to detach the listener. * Verifies that ViewCapture is enabled before actually attaching an onDrawListener. */ @NonNull public SafeCloseable startCapture(View view, String name) { WindowListener listener = new WindowListener(view, name); if (mIsEnabled) MAIN_EXECUTOR.execute(listener::attachToRoot); Loading @@ -140,6 +142,25 @@ public abstract class ViewCapture { }; } /** * Launcher checks for leaks in many spots during its instrumented tests. The WindowListeners * appear to have leaks because they store mRoot views. In reality, attached views close their * respective window listeners when they are destroyed. * <p> * This method deletes detaches and deletes mRoot views from windowListeners. This makes the * WindowListeners unusable for anything except dumping previously captured information. They * are still technically enabled to allow for dumping. */ @VisibleForTesting public void stopCapture(@NonNull View rootView) { mListeners.forEach(it -> { if (rootView == it.mRoot) { it.mRoot.getViewTreeObserver().removeOnDrawListener(it); it.mRoot = null; } }); } @UiThread protected void enableOrDisableWindowListeners(boolean isEnabled) { mIsEnabled = isEnabled; Loading @@ -148,23 +169,18 @@ public abstract class ViewCapture { } @AnyThread protected void dumpTo(ParcelFileDescriptor outFd, Context context) { public void dumpTo(OutputStream os, Context context) throws InterruptedException, ExecutionException, IOException { if (!mIsEnabled) { return; } ArrayList<Class> classList = new ArrayList<>(); try (OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(outFd)) { ExportedData.newBuilder() .setPackage(context.getPackageName()) .addAllWindowData(getWindowData(context, classList, l -> l.mIsActive).get()) .addAllClassname(toStringList(classList)) .build() .writeTo(os); } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "failed to get window data", e); } catch (IOException e) { Log.e(TAG, "failed to output data to wm trace", e); } } private static List<String> toStringList(List<Class> classList) { Loading Loading @@ -232,7 +248,8 @@ public abstract class ViewCapture { */ private class WindowListener implements ViewTreeObserver.OnDrawListener { public final View mRoot; @Nullable // Nullable in tests only public View mRoot; public final String name; private final ViewRef mViewRef = new ViewRef(); Loading Loading @@ -378,8 +395,10 @@ public abstract class ViewCapture { void detachFromRoot() { mIsActive = false; if (mRoot != null) { mRoot.getViewTreeObserver().removeOnDrawListener(this); } } private void safelyEnableOnDrawListener() { mRoot.getViewTreeObserver().removeOnDrawListener(this); Loading Loading
motiontoollib/src/com/android/app/motiontool/MotionToolManager.kt +2 −5 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.view.Choreographer import android.view.View import android.view.WindowManagerGlobal import androidx.annotation.VisibleForTesting import com.android.app.viewcapture.SimpleViewCapture import com.android.app.viewcapture.ViewCapture import com.android.app.viewcapture.data.MotionWindowData Loading @@ -43,7 +44,7 @@ import com.android.app.viewcapture.data.MotionWindowData * @see [DdmHandleMotionTool] */ class MotionToolManager private constructor(private val windowManagerGlobal: WindowManagerGlobal) { private val viewCapture: ViewCapture = SimpleViewCapture() private val viewCapture: ViewCapture = SimpleViewCapture("MTViewCapture") companion object { private const val TAG = "MotionToolManager" Loading Loading @@ -135,10 +136,6 @@ class MotionToolManager private constructor(private val windowManagerGlobal: Win private fun getRootView(windowId: String): View? { return windowManagerGlobal.getRootView(windowId) } class SimpleViewCapture : ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE, MAIN_EXECUTOR.submit { Choreographer.getInstance() }.get(), createAndStartNewLooperExecutor("MTViewCapture", Process.THREAD_PRIORITY_FOREGROUND)) } private data class TraceMetadata( Loading
viewcapturelib/src/com/android/app/viewcapture/SettingsAwareViewCapture.kt +10 −1 Original line number Diff line number Diff line Loading @@ -24,12 +24,15 @@ import android.os.Looper import android.os.ParcelFileDescriptor import android.os.Process import android.provider.Settings import android.util.Log import android.view.Choreographer import android.window.IDumpCallback import androidx.annotation.AnyThread import androidx.annotation.VisibleForTesting import java.util.concurrent.Executor private val TAG = SettingsAwareViewCapture::class.java.simpleName /** * ViewCapture that listens to system updates and enables / disables attached ViewCapture * WindowListeners accordingly. The Settings toggle is currently controlled by the Winscope Loading @@ -41,7 +44,13 @@ internal constructor(private val context: Context, choreographer: Choreographer, : ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE, choreographer, executor) { /** Dumps all the active view captures to the wm trace directory via LauncherAppService */ private val mDumpCallback: IDumpCallback.Stub = object : IDumpCallback.Stub() { override fun onDump(out: ParcelFileDescriptor) = dumpTo(out, context) override fun onDump(out: ParcelFileDescriptor) { try { ParcelFileDescriptor.AutoCloseOutputStream(out).use { os -> dumpTo(os, context) } } catch (e: Exception) { Log.e(TAG, "failed to dump data to wm trace", e) } } } init { Loading
viewcapturelib/src/com/android/app/viewcapture/SimpleViewCapture.kt 0 → 100644 +8 −0 Original line number Diff line number Diff line package com.android.app.viewcapture import android.os.Process import android.view.Choreographer open class SimpleViewCapture(threadName: String) : ViewCapture(DEFAULT_MEMORY_SIZE, DEFAULT_INIT_POOL_SIZE, MAIN_EXECUTOR.submit { Choreographer.getInstance() }.get(), createAndStartNewLooperExecutor(threadName, Process.THREAD_PRIORITY_FOREGROUND)) No newline at end of file
viewcapturelib/src/com/android/app/viewcapture/ViewCapture.java +36 −17 Original line number Diff line number Diff line Loading @@ -21,10 +21,8 @@ import android.content.res.Resources; import android.media.permission.SafeCloseable; import android.os.HandlerThread; import android.os.Looper; import android.os.ParcelFileDescriptor; import android.os.Trace; import android.text.TextUtils; import android.util.Log; import android.util.SparseArray; import android.view.Choreographer; import android.view.View; Loading @@ -33,8 +31,10 @@ import android.view.ViewTreeObserver; import android.view.Window; import androidx.annotation.AnyThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; import com.android.app.viewcapture.data.ExportedData; Loading Loading @@ -120,6 +120,7 @@ public abstract class ViewCapture { /** * Attaches the ViewCapture to the provided window and returns a handle to detach the listener */ @NonNull public SafeCloseable startCapture(Window window) { String title = window.getAttributes().getTitle().toString(); String name = TextUtils.isEmpty(title) ? window.toString() : title; Loading @@ -130,6 +131,7 @@ public abstract class ViewCapture { * Attaches the ViewCapture to the provided window and returns a handle to detach the listener. * Verifies that ViewCapture is enabled before actually attaching an onDrawListener. */ @NonNull public SafeCloseable startCapture(View view, String name) { WindowListener listener = new WindowListener(view, name); if (mIsEnabled) MAIN_EXECUTOR.execute(listener::attachToRoot); Loading @@ -140,6 +142,25 @@ public abstract class ViewCapture { }; } /** * Launcher checks for leaks in many spots during its instrumented tests. The WindowListeners * appear to have leaks because they store mRoot views. In reality, attached views close their * respective window listeners when they are destroyed. * <p> * This method deletes detaches and deletes mRoot views from windowListeners. This makes the * WindowListeners unusable for anything except dumping previously captured information. They * are still technically enabled to allow for dumping. */ @VisibleForTesting public void stopCapture(@NonNull View rootView) { mListeners.forEach(it -> { if (rootView == it.mRoot) { it.mRoot.getViewTreeObserver().removeOnDrawListener(it); it.mRoot = null; } }); } @UiThread protected void enableOrDisableWindowListeners(boolean isEnabled) { mIsEnabled = isEnabled; Loading @@ -148,23 +169,18 @@ public abstract class ViewCapture { } @AnyThread protected void dumpTo(ParcelFileDescriptor outFd, Context context) { public void dumpTo(OutputStream os, Context context) throws InterruptedException, ExecutionException, IOException { if (!mIsEnabled) { return; } ArrayList<Class> classList = new ArrayList<>(); try (OutputStream os = new ParcelFileDescriptor.AutoCloseOutputStream(outFd)) { ExportedData.newBuilder() .setPackage(context.getPackageName()) .addAllWindowData(getWindowData(context, classList, l -> l.mIsActive).get()) .addAllClassname(toStringList(classList)) .build() .writeTo(os); } catch (InterruptedException | ExecutionException e) { Log.e(TAG, "failed to get window data", e); } catch (IOException e) { Log.e(TAG, "failed to output data to wm trace", e); } } private static List<String> toStringList(List<Class> classList) { Loading Loading @@ -232,7 +248,8 @@ public abstract class ViewCapture { */ private class WindowListener implements ViewTreeObserver.OnDrawListener { public final View mRoot; @Nullable // Nullable in tests only public View mRoot; public final String name; private final ViewRef mViewRef = new ViewRef(); Loading Loading @@ -378,8 +395,10 @@ public abstract class ViewCapture { void detachFromRoot() { mIsActive = false; if (mRoot != null) { mRoot.getViewTreeObserver().removeOnDrawListener(this); } } private void safelyEnableOnDrawListener() { mRoot.getViewTreeObserver().removeOnDrawListener(this); Loading