From e08ceb575c0b84b8209ad036be84ff0f488b5913 Mon Sep 17 00:00:00 2001 From: Diwas Sharma Date: Fri, 1 Sep 2023 00:41:22 +0000 Subject: [PATCH] Revert "Remove Winscope tracing QS development tile" This reverts commit 513e0149634e9af5a4517f2a0a45e07a415ab9e2. Reason for revert: 298512106 Change-Id: I5efa947b17b8d33cfe8ca3350c2279903e5239af --- AndroidManifest.xml | 14 + res/drawable/tile_icon_winscope_trace.xml | 29 ++ res/values/strings.xml | 3 + .../development/qstile/DevelopmentTiles.java | 172 ++++++++++++ .../development/qstile/WinscopeTraceTest.java | 259 ++++++++++++++++++ 5 files changed, 477 insertions(+) create mode 100644 res/drawable/tile_icon_winscope_trace.xml create mode 100644 tests/robotests/src/com/android/settings/development/qstile/WinscopeTraceTest.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index a036ed92d85..b147fff310e 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -4389,6 +4389,20 @@ android:value="true"/> + + + + + + + + + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 7def69075f5..4033c3988bc 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -9939,6 +9939,9 @@ Disable automatic revocation of adb authorizations for systems that have not reconnected within the default (7 days) or user-configured (minimum 1 day) amount of time. + + Winscope Trace + Sensors Off diff --git a/src/com/android/settings/development/qstile/DevelopmentTiles.java b/src/com/android/settings/development/qstile/DevelopmentTiles.java index 8571c1995d1..4662149d3db 100644 --- a/src/com/android/settings/development/qstile/DevelopmentTiles.java +++ b/src/com/android/settings/development/qstile/DevelopmentTiles.java @@ -29,7 +29,9 @@ import android.database.ContentObserver; import android.hardware.SensorPrivacyManager; import android.net.Uri; import android.os.Handler; +import android.os.IBinder; import android.os.Looper; +import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; @@ -43,7 +45,10 @@ import android.view.ThreadedRenderer; import android.view.WindowManagerGlobal; import android.widget.Toast; +import androidx.annotation.VisibleForTesting; + import com.android.internal.app.LocalePicker; +import com.android.internal.inputmethod.ImeTracing; import com.android.internal.statusbar.IStatusBarService; import com.android.settings.development.WirelessDebuggingPreferenceController; import com.android.settings.overlay.FeatureFactory; @@ -198,6 +203,173 @@ public abstract class DevelopmentTiles extends TileService { } } + /** + * Tile to toggle Winscope trace which consists of Window and Layer traces. + */ + public static class WinscopeTrace extends DevelopmentTiles { + @VisibleForTesting + static final int SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE = 1025; + @VisibleForTesting + static final int SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE = 1026; + private static final String VIEW_CAPTURE_ENABLED = "view_capture_enabled"; + private IBinder mSurfaceFlinger; + private IWindowManager mWindowManager; + private ImeTracing mImeTracing; + private Toast mToast; + + @Override + public void onCreate() { + super.onCreate(); + mWindowManager = WindowManagerGlobal.getWindowManagerService(); + mSurfaceFlinger = ServiceManager.getService("SurfaceFlinger"); + mImeTracing = ImeTracing.getInstance(); + Context context = getApplicationContext(); + CharSequence text = "Trace files written to /data/misc/wmtrace"; + mToast = Toast.makeText(context, text, Toast.LENGTH_LONG); + } + + private boolean isWindowTraceEnabled() { + try { + return mWindowManager.isWindowTraceEnabled(); + } catch (RemoteException e) { + Log.e(TAG, + "Could not get window trace status, defaulting to false." + e.toString()); + } + return false; + } + + private boolean isLayerTraceEnabled() { + boolean layerTraceEnabled = false; + Parcel reply = null; + Parcel data = null; + try { + if (mSurfaceFlinger != null) { + reply = Parcel.obtain(); + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + mSurfaceFlinger.transact(SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE, + data, reply, 0 /* flags */); + layerTraceEnabled = reply.readBoolean(); + } + } catch (RemoteException e) { + Log.e(TAG, "Could not get layer trace status, defaulting to false." + e.toString()); + } finally { + if (data != null) { + data.recycle(); + reply.recycle(); + } + } + return layerTraceEnabled; + } + + private boolean isSystemUiTracingEnabled() { + try { + final IStatusBarService statusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.checkService(Context.STATUS_BAR_SERVICE)); + if (statusBarService != null) { + return statusBarService.isTracing(); + } + } catch (RemoteException e) { + Log.e(TAG, "Could not get system ui tracing status." + e.toString()); + } + return false; + } + + private boolean isImeTraceEnabled() { + return mImeTracing.isEnabled(); + } + + private boolean isViewCaptureEnabled() { + // Add null checking to avoid test case failure. + if (getApplicationContext() != null) { + return Settings.Global.getInt(getApplicationContext().getContentResolver(), + VIEW_CAPTURE_ENABLED, 0) != 0; + } + return false; + } + + @Override + protected boolean isEnabled() { + return isWindowTraceEnabled() || isLayerTraceEnabled() || isSystemUiTracingEnabled() + || isImeTraceEnabled() || isViewCaptureEnabled(); + } + + private void setWindowTraceEnabled(boolean isEnabled) { + try { + if (isEnabled) { + mWindowManager.startWindowTrace(); + } else { + mWindowManager.stopWindowTrace(); + } + } catch (RemoteException e) { + Log.e(TAG, "Could not set window trace status." + e.toString()); + } + } + + private void setLayerTraceEnabled(boolean isEnabled) { + Parcel data = null; + try { + if (mSurfaceFlinger != null) { + data = Parcel.obtain(); + data.writeInterfaceToken("android.ui.ISurfaceComposer"); + data.writeInt(isEnabled ? 1 : 0); + mSurfaceFlinger.transact(SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE, + data, null, 0 /* flags */); + } + } catch (RemoteException e) { + Log.e(TAG, "Could not set layer tracing." + e.toString()); + } finally { + if (data != null) { + data.recycle(); + } + } + } + + private void setSystemUiTracing(boolean isEnabled) { + try { + final IStatusBarService statusBarService = IStatusBarService.Stub.asInterface( + ServiceManager.checkService(Context.STATUS_BAR_SERVICE)); + if (statusBarService != null) { + if (isEnabled) { + statusBarService.startTracing(); + } else { + statusBarService.stopTracing(); + } + } + } catch (RemoteException e) { + Log.e(TAG, "Could not set system ui tracing." + e.toString()); + } + } + + private void setImeTraceEnabled(boolean isEnabled) { + if (isEnabled) { + mImeTracing.startImeTrace(); + } else { + mImeTracing.stopImeTrace(); + } + } + + private void setViewCaptureEnabled(boolean isEnabled) { + // Add null checking to avoid test case failure. + if (getApplicationContext() != null) { + Settings.Global.putInt(getApplicationContext() + .getContentResolver(), VIEW_CAPTURE_ENABLED, isEnabled ? 1 : 0); + } + } + + @Override + protected void setIsEnabled(boolean isEnabled) { + setWindowTraceEnabled(isEnabled); + setLayerTraceEnabled(isEnabled); + setSystemUiTracing(isEnabled); + setImeTraceEnabled(isEnabled); + setViewCaptureEnabled(isEnabled); + if (!isEnabled) { + mToast.show(); + } + } + } + /** * Tile to toggle sensors off to control camera, mic, and sensors managed by the SensorManager. */ diff --git a/tests/robotests/src/com/android/settings/development/qstile/WinscopeTraceTest.java b/tests/robotests/src/com/android/settings/development/qstile/WinscopeTraceTest.java new file mode 100644 index 00000000000..ad495c740f3 --- /dev/null +++ b/tests/robotests/src/com/android/settings/development/qstile/WinscopeTraceTest.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2018 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.settings.development.qstile; + +import static com.android.settings.development.qstile.DevelopmentTiles.WinscopeTrace.SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE; +import static com.android.settings.development.qstile.DevelopmentTiles.WinscopeTrace.SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE; + +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.ArgumentMatchers.isNull; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +import android.os.IBinder; +import android.os.RemoteException; +import android.view.IWindowManager; +import android.widget.Toast; + +import androidx.test.core.app.ApplicationProvider; + +import com.android.internal.inputmethod.ImeTracing; +import com.android.settings.testutils.shadow.ShadowParcel; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.annotation.Config; +import org.robolectric.util.ReflectionHelpers; + +@RunWith(RobolectricTestRunner.class) +public class WinscopeTraceTest { + + @Mock + private IWindowManager mWindowManager; + @Mock + private ImeTracing mImeTracing; + @Mock + private IBinder mSurfaceFlinger; + @Mock + private Toast mToast; + + private DevelopmentTiles.WinscopeTrace mWinscopeTrace; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mWinscopeTrace = spy(new DevelopmentTiles.WinscopeTrace()); + doReturn(ApplicationProvider.getApplicationContext()).when( + mWinscopeTrace).getApplicationContext(); + + ReflectionHelpers.setField(mWinscopeTrace, "mWindowManager", mWindowManager); + ReflectionHelpers.setField(mWinscopeTrace, "mImeTracing", mImeTracing); + ReflectionHelpers.setField(mWinscopeTrace, "mSurfaceFlinger", mSurfaceFlinger); + ReflectionHelpers.setField(mWinscopeTrace, "mToast", mToast); + } + + @After + public void teardown() { + verifyNoMoreInteractions(mToast); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void wmReturnsTraceEnabled_shouldReturnEnabled() throws RemoteException { + // Assume Surface Trace and Input Method Manager are disabled. + ShadowParcel.sReadBoolResult = false; + doReturn(true).when(mWindowManager).isWindowTraceEnabled(); + assertThat(mWinscopeTrace.isEnabled()).isTrue(); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void sfReturnsTraceEnabled_shouldReturnEnabled() throws RemoteException { + // Assume Window Trace and Input Method Manager are disabled. + doReturn(false).when(mWindowManager).isWindowTraceEnabled(); + doReturn(false).when(mImeTracing).isEnabled(); + ShadowParcel.sReadBoolResult = true; + assertThat(mWinscopeTrace.isEnabled()).isTrue(); + verify(mSurfaceFlinger) + .transact(eq(SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE), any(), any(), + eq(0 /* flags */)); + verifyNoMoreInteractions(mSurfaceFlinger); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void sfAndWmReturnsTraceEnabled_shouldReturnEnabled() throws RemoteException { + ShadowParcel.sReadBoolResult = true; + doReturn(true).when(mWindowManager).isWindowTraceEnabled(); + assertThat(mWinscopeTrace.isEnabled()).isTrue(); + } + + @Test + public void wmAndSfAndImmReturnTraceDisabled_shouldReturnDisabled() throws RemoteException { + ShadowParcel.sReadBoolResult = false; + doReturn(false).when(mWindowManager).isWindowTraceEnabled(); + doReturn(false).when(mImeTracing).isEnabled(); + assertThat(mWinscopeTrace.isEnabled()).isFalse(); + verify(mSurfaceFlinger) + .transact(eq(SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE), any(), any(), + eq(0 /* flags */)); + verifyNoMoreInteractions(mSurfaceFlinger); + } + + @Test + public void wmAndSfReturnTraceDisabled_immReturnsTraceEnabled_shouldReturnEnabled() + throws RemoteException { + ShadowParcel.sReadBoolResult = false; + doReturn(false).when(mWindowManager).isWindowTraceEnabled(); + doReturn(true).when(mImeTracing).isEnabled(); + assertThat(mWinscopeTrace.isEnabled()).isTrue(); + verify(mSurfaceFlinger) + .transact(eq(SURFACE_FLINGER_LAYER_TRACE_STATUS_CODE), any(), any(), + eq(0 /* flags */)); + verifyNoMoreInteractions(mSurfaceFlinger); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void immReturnsTraceEnabled_shouldReturnEnabled() throws RemoteException { + // Assume Window Manager and Surface Trace are disabled. + ShadowParcel.sReadBoolResult = false; + doReturn(true).when(mImeTracing).isEnabled(); + assertThat(mWinscopeTrace.isEnabled()).isTrue(); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void immReturnsTraceDisabled_shouldReturnDisabled() throws RemoteException { + // Assume Window Manager and Surface Trace are disabled. + ShadowParcel.sReadBoolResult = false; + doReturn(false).when(mImeTracing).isEnabled(); + assertThat(mWinscopeTrace.isEnabled()).isFalse(); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void wmThrowsRemoteExAndSfReturnsTraceDisabled_shouldReturnDisabled() + throws RemoteException { + ShadowParcel.sReadBoolResult = false; + doThrow(new RemoteException("Unknown")) + .when(mWindowManager).isWindowTraceEnabled(); + assertThat(mWinscopeTrace.isEnabled()).isFalse(); + } + + @Test + public void sfUnavailableAndWmAndImmReturnTraceDisabled_shouldReturnDisabled() + throws RemoteException { + doReturn(false).when(mWindowManager).isWindowTraceEnabled(); + doReturn(false).when(mImeTracing).isEnabled(); + ReflectionHelpers.setField(mWinscopeTrace, "mSurfaceFlinger", null); + assertThat(mWinscopeTrace.isEnabled()).isFalse(); + } + + @Test + public void setIsEnableTrue_shouldEnableWindowTrace() throws RemoteException { + mWinscopeTrace.setIsEnabled(true); + verify(mWindowManager).startWindowTrace(); + verifyNoMoreInteractions(mWindowManager); + } + + @Test + public void setIsEnableTrue_shouldEnableImeTrace() throws RemoteException { + mWinscopeTrace.setIsEnabled(true); + verify(mImeTracing).startImeTrace(); + verifyNoMoreInteractions(mImeTracing); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void setIsEnableTrue_shouldEnableLayerTrace() throws RemoteException { + mWinscopeTrace.setIsEnabled(true); + assertThat(ShadowParcel.sWriteIntResult).isEqualTo(1); + verify(mSurfaceFlinger) + .transact(eq(SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE), any(), isNull(), + eq(0 /* flags */)); + verifyNoMoreInteractions(mSurfaceFlinger); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void setIsEnableFalse_shouldDisableWindowTrace() throws RemoteException { + mWinscopeTrace.setIsEnabled(false); + verify(mWindowManager).stopWindowTrace(); + verifyNoMoreInteractions(mWindowManager); + verify(mToast).show(); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void setIsEnableFalse_shouldDisableImeTrace() throws RemoteException { + mWinscopeTrace.setIsEnabled(false); + verify(mImeTracing).stopImeTrace(); + verifyNoMoreInteractions(mImeTracing); + verify(mToast).show(); + } + + @Test + @Config(shadows = ShadowParcel.class) + public void setIsEnableFalse_shouldDisableLayerTrace() throws RemoteException { + mWinscopeTrace.setIsEnabled(false); + assertThat(ShadowParcel.sWriteIntResult).isEqualTo(0); + verify(mSurfaceFlinger) + .transact(eq(SURFACE_FLINGER_LAYER_TRACE_CONTROL_CODE), any(), isNull(), + eq(0 /* flags */)); + verifyNoMoreInteractions(mSurfaceFlinger); + verify(mToast).show(); + } + + @Test + public void setIsEnableFalse_shouldShowToast() { + mWinscopeTrace.setIsEnabled(false); + verify(mToast).show(); + } + + /** + * Verify when window manager call throws a remote exception, it is handled without + * re-throwing the exception. + */ + @Test + public void setIsEnableAndWmThrowsRemoteException_shouldFailGracefully() + throws RemoteException { + doThrow(new RemoteException("Unknown")).when(mWindowManager).isWindowTraceEnabled(); + mWinscopeTrace.setIsEnabled(true); + } + + /** + * Verify is surface flinger is not available not calls are made to it. + */ + @Test + public void setIsEnableAndSfUnavailable_shouldFailGracefully() { + ReflectionHelpers.setField(mWinscopeTrace, "mSurfaceFlinger", null); + mWinscopeTrace.setIsEnabled(true); + verifyNoMoreInteractions(mSurfaceFlinger); + } +}