From c49cac73864ad4b6e5c1f3f352f5ba5e461d9cf8 Mon Sep 17 00:00:00 2001 From: Hongming Jin Date: Mon, 8 Mar 2021 19:16:49 -0800 Subject: [PATCH] Add trace for AccessibilityInteractionClient callback interfaces. In an effort to reduce unnecessary String operations and Binder calls from AccessibilityInteractionClient, also add channels to propagate tracing state to AccessibilityInteractionClient through AccessibilityManager. Bug: 157601519 Test: adb shell cmd accessibility start-trace adb shell cmd accessibility stop-trace Change-Id: Idfe220bc64a9c83679201b9a9a36b1c492f9d6cc --- .../AccessibilityService.java | 81 +++++++------ .../IAccessibilityServiceConnection.aidl | 3 + .../AccessibilityInteractionClient.java | 113 +++++++++++++++++- .../accessibility/AccessibilityManager.java | 30 +++++ .../AccessibilityServiceConnectionImpl.java | 3 + ...bstractAccessibilityServiceConnection.java | 12 ++ .../AccessibilityManagerService.java | 33 ++++- .../AccessibilityShellCommand.java | 8 +- .../accessibility/AccessibilityTrace.java | 25 ++++ .../accessibility/AccessibilityUserState.java | 5 +- .../server/wm/AccessibilityController.java | 75 ++++++++---- .../server/wm/WindowManagerInternal.java | 16 +++ 12 files changed, 333 insertions(+), 71 deletions(-) diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java index d5368213be087..483defab43c90 100644 --- a/core/java/android/accessibilityservice/AccessibilityService.java +++ b/core/java/android/accessibilityservice/AccessibilityService.java @@ -886,7 +886,7 @@ public abstract class AccessibilityService extends Service { * them, otherwise an empty list. */ public List getWindows() { - return AccessibilityInteractionClient.getInstance().getWindows(mConnectionId); + return AccessibilityInteractionClient.getInstance(this).getWindows(mConnectionId); } /** @@ -914,7 +914,8 @@ public abstract class AccessibilityService extends Service { */ @NonNull public final SparseArray> getWindowsOnAllDisplays() { - return AccessibilityInteractionClient.getInstance().getWindowsOnAllDisplays(mConnectionId); + return AccessibilityInteractionClient.getInstance(this).getWindowsOnAllDisplays( + mConnectionId); } /** @@ -940,7 +941,8 @@ public abstract class AccessibilityService extends Service { * @return The root node if this service can retrieve window content. */ public AccessibilityNodeInfo getRootInActiveWindow() { - return AccessibilityInteractionClient.getInstance().getRootInActiveWindow(mConnectionId); + return AccessibilityInteractionClient.getInstance(this).getRootInActiveWindow( + mConnectionId); } /** @@ -949,7 +951,7 @@ public abstract class AccessibilityService extends Service { */ public final void disableSelf() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { connection.disableSelf(); @@ -970,7 +972,7 @@ public abstract class AccessibilityService extends Service { private void setDefaultTokenInternal(Context context, int displayId) { final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE); final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); IBinder token = null; if (connection != null) { synchronized (mLock) { @@ -1042,7 +1044,7 @@ public abstract class AccessibilityService extends Service { public final @NonNull FingerprintGestureController getFingerprintGestureController() { if (mFingerprintGestureController == null) { mFingerprintGestureController = new FingerprintGestureController( - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId)); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId)); } return mFingerprintGestureController; } @@ -1074,8 +1076,7 @@ public abstract class AccessibilityService extends Service { @Nullable GestureResultCallback callback, @Nullable Handler handler) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( - mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection == null) { return false; } @@ -1287,7 +1288,7 @@ public abstract class AccessibilityService extends Service { private void setMagnificationCallbackEnabled(boolean enabled) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1348,7 +1349,7 @@ public abstract class AccessibilityService extends Service { */ public float getScale() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1377,7 +1378,7 @@ public abstract class AccessibilityService extends Service { */ public float getCenterX() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1406,7 +1407,7 @@ public abstract class AccessibilityService extends Service { */ public float getCenterY() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1440,7 +1441,7 @@ public abstract class AccessibilityService extends Service { @NonNull public Region getMagnificationRegion() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1469,7 +1470,7 @@ public abstract class AccessibilityService extends Service { */ public boolean reset(boolean animate) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1497,7 +1498,7 @@ public abstract class AccessibilityService extends Service { */ public boolean setScale(float scale, boolean animate) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1529,7 +1530,7 @@ public abstract class AccessibilityService extends Service { */ public boolean setCenter(float centerX, float centerY, boolean animate) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1701,7 +1702,7 @@ public abstract class AccessibilityService extends Service { private void setSoftKeyboardCallbackEnabled(boolean enabled) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1760,7 +1761,7 @@ public abstract class AccessibilityService extends Service { @SoftKeyboardShowMode public int getShowMode() { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1789,7 +1790,7 @@ public abstract class AccessibilityService extends Service { */ public boolean setShowMode(@SoftKeyboardShowMode int showMode) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1835,7 +1836,7 @@ public abstract class AccessibilityService extends Service { */ public boolean switchToInputMethod(@NonNull String imeId) { final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( + AccessibilityInteractionClient.getInstance(mService).getConnection( mService.mConnectionId); if (connection != null) { try { @@ -1888,7 +1889,7 @@ public abstract class AccessibilityService extends Service { displayId); if (controller == null) { controller = new AccessibilityButtonController( - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId)); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId)); mAccessibilityButtonControllers.put(displayId, controller); } return controller; @@ -1922,7 +1923,7 @@ public abstract class AccessibilityService extends Service { */ public final @NonNull List getSystemActions() { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { return connection.getSystemActions(); @@ -1955,7 +1956,7 @@ public abstract class AccessibilityService extends Service { */ public final boolean performGlobalAction(int action) { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { return connection.performGlobalAction(action); @@ -1996,7 +1997,7 @@ public abstract class AccessibilityService extends Service { * @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY */ public AccessibilityNodeInfo findFocus(int focus) { - return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, + return AccessibilityInteractionClient.getInstance(this).findFocus(mConnectionId, AccessibilityWindowInfo.ANY_WINDOW_ID, AccessibilityNodeInfo.ROOT_NODE_ID, focus); } @@ -2012,7 +2013,7 @@ public abstract class AccessibilityService extends Service { */ public final AccessibilityServiceInfo getServiceInfo() { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { return connection.getServiceInfo(); @@ -2044,12 +2045,12 @@ public abstract class AccessibilityService extends Service { */ private void sendServiceInfo() { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (mInfo != null && connection != null) { try { connection.setServiceInfo(mInfo); mInfo = null; - AccessibilityInteractionClient.getInstance().clearCache(); + AccessibilityInteractionClient.getInstance(this).clearCache(); } catch (RemoteException re) { Log.w(LOG_TAG, "Error while setting AccessibilityServiceInfo", re); re.rethrowFromSystemServer(); @@ -2096,8 +2097,7 @@ public abstract class AccessibilityService extends Service { Preconditions.checkNotNull(executor, "executor cannot be null"); Preconditions.checkNotNull(callback, "callback cannot be null"); final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection( - mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection == null) { sendScreenshotFailure(ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR, executor, callback); return; @@ -2136,7 +2136,7 @@ public abstract class AccessibilityService extends Service { */ public void setAccessibilityFocusAppearance(int strokeWidth, @ColorInt int color) { IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { connection.setFocusAppearance(strokeWidth, color); @@ -2261,12 +2261,14 @@ public abstract class AccessibilityService extends Service { private final HandlerCaller mCaller; private final Callbacks mCallback; + private final Context mContext; private int mConnectionId = AccessibilityInteractionClient.NO_ID; public IAccessibilityServiceClientWrapper(Context context, Looper looper, Callbacks callback) { mCallback = callback; + mContext = context; mCaller = new HandlerCaller(context, looper, this, true /*asyncHandler*/); } @@ -2366,7 +2368,8 @@ public abstract class AccessibilityService extends Service { boolean serviceWantsEvent = message.arg1 != 0; if (event != null) { // Send the event to AccessibilityCache via AccessibilityInteractionClient - AccessibilityInteractionClient.getInstance().onAccessibilityEvent(event); + AccessibilityInteractionClient.getInstance(mContext).onAccessibilityEvent( + event); if (serviceWantsEvent && (mConnectionId != AccessibilityInteractionClient.NO_ID)) { // Send the event to AccessibilityService @@ -2395,15 +2398,15 @@ public abstract class AccessibilityService extends Service { IBinder windowToken = (IBinder) args.arg2; args.recycle(); if (connection != null) { - AccessibilityInteractionClient.getInstance().addConnection(mConnectionId, - connection); + AccessibilityInteractionClient.getInstance(mContext).addConnection( + mConnectionId, connection); mCallback.init(mConnectionId, windowToken); mCallback.onServiceConnected(); } else { - AccessibilityInteractionClient.getInstance().removeConnection( + AccessibilityInteractionClient.getInstance(mContext).removeConnection( mConnectionId); mConnectionId = AccessibilityInteractionClient.NO_ID; - AccessibilityInteractionClient.getInstance().clearCache(); + AccessibilityInteractionClient.getInstance(mContext).clearCache(); mCallback.init(AccessibilityInteractionClient.NO_ID, null); } return; @@ -2415,14 +2418,14 @@ public abstract class AccessibilityService extends Service { return; } case DO_CLEAR_ACCESSIBILITY_CACHE: { - AccessibilityInteractionClient.getInstance().clearCache(); + AccessibilityInteractionClient.getInstance(mContext).clearCache(); return; } case DO_ON_KEY_EVENT: { KeyEvent event = (KeyEvent) message.obj; try { IAccessibilityServiceConnection connection = AccessibilityInteractionClient - .getInstance().getConnection(mConnectionId); + .getInstance(mContext).getConnection(mConnectionId); if (connection != null) { final boolean result = mCallback.onKeyEvent(event); final int sequence = message.arg1; @@ -2637,7 +2640,7 @@ public abstract class AccessibilityService extends Service { public void setGestureDetectionPassthroughRegion(int displayId, @NonNull Region region) { Preconditions.checkNotNull(region, "region cannot be null"); final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { connection.setGestureDetectionPassthroughRegion(displayId, region); @@ -2663,7 +2666,7 @@ public abstract class AccessibilityService extends Service { public void setTouchExplorationPassthroughRegion(int displayId, @NonNull Region region) { Preconditions.checkNotNull(region, "region cannot be null"); final IAccessibilityServiceConnection connection = - AccessibilityInteractionClient.getInstance().getConnection(mConnectionId); + AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId); if (connection != null) { try { connection.setTouchExplorationPassthroughRegion(displayId, region); diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index ab21dc9f14ade..923b6f41414a9 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -117,4 +117,7 @@ interface IAccessibilityServiceConnection { void setTouchExplorationPassthroughRegion(int displayId, in Region region); void setFocusAppearance(int strokeWidth, int color); + + oneway void logTrace(long timestamp, String where, String callingParams, int processId, + long threadId, int callingUid, in Bundle serializedCallingStackInBundle); } diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index f63749be6df26..e64157734fdb3 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -20,6 +20,7 @@ import android.accessibilityservice.IAccessibilityServiceConnection; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Context; import android.os.Binder; import android.os.Build; import android.os.Bundle; @@ -39,6 +40,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.ArrayUtils; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; @@ -83,6 +85,8 @@ public final class AccessibilityInteractionClient public static final int NO_ID = -1; + public static final String CALL_STACK = "call_stack"; + private static final String LOG_TAG = "AccessibilityInteractionClient"; private static final boolean DEBUG = false; @@ -113,7 +117,10 @@ public final class AccessibilityInteractionClient private final Object mInstanceLock = new Object(); + private final AccessibilityManager mAccessibilityManager; + private volatile int mInteractionId = -1; + private volatile int mCallingUid = Process.INVALID_UID; private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult; @@ -151,6 +158,37 @@ public final class AccessibilityInteractionClient } } + /** + * @return The client for the current thread. + */ + public static AccessibilityInteractionClient getInstance(Context context) { + final long threadId = Thread.currentThread().getId(); + if (context != null) { + return getInstanceForThread(threadId, context); + } + return getInstanceForThread(threadId); + } + + /** + * Note: We keep one instance per interrogating thread since + * the instance contains state which can lead to undesired thread interleavings. + * We do not have a thread local variable since other threads should be able to + * look up the correct client knowing a thread id. See ViewRootImpl for details. + * + * @return The client for a given threadId. + */ + public static AccessibilityInteractionClient getInstanceForThread( + long threadId, Context context) { + synchronized (sStaticLock) { + AccessibilityInteractionClient client = sClients.get(threadId); + if (client == null) { + client = new AccessibilityInteractionClient(context); + sClients.put(threadId, client); + } + return client; + } + } + /** * Gets a cached accessibility service connection. * @@ -197,6 +235,11 @@ public final class AccessibilityInteractionClient private AccessibilityInteractionClient() { /* reducing constructor visibility */ + mAccessibilityManager = null; + } + + private AccessibilityInteractionClient(Context context) { + mAccessibilityManager = context.getSystemService(AccessibilityManager.class); } /** @@ -453,6 +496,15 @@ public final class AccessibilityInteractionClient if (packageNames != null) { List infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "findAccessibilityNodeInfoByAccessibilityId", + "InteractionId:" + interactionId + ";Result: " + infos + + ";connectionId=" + connectionId + ";accessibilityWindowId=" + + accessibilityWindowId + ";accessibilityNodeId=" + + accessibilityNodeId + ";bypassCache=" + bypassCache + + ";prefetchFlags=" + prefetchFlags + ";arguments=" + arguments); + } finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, bypassCache, packageNames); if (infos != null && !infos.isEmpty()) { @@ -514,6 +566,14 @@ public final class AccessibilityInteractionClient if (packageNames != null) { List infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "findAccessibilityNodeInfosByViewId", "InteractionId=" + + interactionId + ":Result: " + infos + ";connectionId=" + + connectionId + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";viewId=" + + viewId); + } if (infos != null) { finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false, packageNames); @@ -568,6 +628,13 @@ public final class AccessibilityInteractionClient if (packageNames != null) { List infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "findAccessibilityNodeInfosByText", "InteractionId=" + + interactionId + ":Result: " + infos + ";connectionId=" + + connectionId + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";text=" + text); + } if (infos != null) { finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, false, packageNames); @@ -621,6 +688,14 @@ public final class AccessibilityInteractionClient if (packageNames != null) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "findFocus", "InteractionId=" + interactionId + + ":Result: " + info + ";connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";focusType=" + + focusType); + } finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); return info; } @@ -671,6 +746,14 @@ public final class AccessibilityInteractionClient AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); finalizeAndCacheAccessibilityNodeInfo(info, connectionId, false, packageNames); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "focusSearch", "InteractionId=" + interactionId + + ":Result: " + info + ";connectionId=" + connectionId + + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";direction=" + + direction); + } return info; } } else { @@ -716,7 +799,17 @@ public final class AccessibilityInteractionClient } if (success) { - return getPerformAccessibilityActionResultAndClear(interactionId); + final boolean result = + getPerformAccessibilityActionResultAndClear(interactionId); + if (mAccessibilityManager != null + && mAccessibilityManager.isAccessibilityTracingEnabled()) { + logTrace(connection, "performAccessibilityAction", "InteractionId=" + + interactionId + ":Result: " + result + ";connectionId=" + + connectionId + ";accessibilityWindowId=" + accessibilityWindowId + + ";accessibilityNodeId=" + accessibilityNodeId + ";action=" + + action + ";arguments=" + arguments); + } + return result; } } else { if (DEBUG) { @@ -774,6 +867,7 @@ public final class AccessibilityInteractionClient if (interactionId > mInteractionId) { mFindAccessibilityNodeInfoResult = info; mInteractionId = interactionId; + mCallingUid = Binder.getCallingUid(); } mInstanceLock.notifyAll(); } @@ -823,6 +917,7 @@ public final class AccessibilityInteractionClient mFindAccessibilityNodeInfosResult = Collections.emptyList(); } mInteractionId = interactionId; + mCallingUid = Binder.getCallingUid(); } mInstanceLock.notifyAll(); } @@ -851,6 +946,7 @@ public final class AccessibilityInteractionClient if (interactionId > mInteractionId) { mPerformAccessibilityActionResult = succeeded; mInteractionId = interactionId; + mCallingUid = Binder.getCallingUid(); } mInstanceLock.notifyAll(); } @@ -1059,4 +1155,19 @@ public final class AccessibilityInteractionClient } return true; } + + private void logTrace( + IAccessibilityServiceConnection connection, String method, String params) { + try { + Bundle b = new Bundle(); + ArrayList callStack = new ArrayList( + Arrays.asList(Thread.currentThread().getStackTrace())); + b.putSerializable(CALL_STACK, callStack); + connection.logTrace(SystemClock.elapsedRealtimeNanos(), + LOG_TAG + ".callback for " + method, params, Process.myPid(), + Thread.currentThread().getId(), mCallingUid, b); + } catch (RemoteException e) { + Log.e(LOG_TAG, "Failed to log trace. " + e); + } + } } diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 56dcd5951e5e0..fc9e5e2ef04e0 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -110,6 +110,9 @@ public final class AccessibilityManager { /** @hide */ public static final int STATE_FLAG_REQUEST_MULTI_FINGER_GESTURES = 0x00000010; + /** @hide */ + public static final int STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED = 0x00000020; + /** @hide */ public static final int DALTONIZER_DISABLED = -1; @@ -232,6 +235,9 @@ public final class AccessibilityManager { @UnsupportedAppUsage(trackingBug = 123768939L) boolean mIsHighTextContrastEnabled; + // Whether accessibility tracing is enabled or not + boolean mIsAccessibilityTracingEnabled = false; + AccessibilityPolicy mAccessibilityPolicy; private int mPerformingAction = 0; @@ -1003,6 +1009,17 @@ public final class AccessibilityManager { } } + /** + * Gets accessibility tracing enabled state. + * + * @hide + */ + public boolean isAccessibilityTracingEnabled() { + synchronized (mLock) { + return mIsAccessibilityTracingEnabled; + } + } + /** * Get the preparers that are registered for an accessibility ID * @@ -1197,6 +1214,8 @@ public final class AccessibilityManager { (stateFlags & STATE_FLAG_TOUCH_EXPLORATION_ENABLED) != 0; final boolean highTextContrastEnabled = (stateFlags & STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED) != 0; + final boolean accessibilityTracingEnabled = + (stateFlags & STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED) != 0; final boolean wasEnabled = isEnabled(); final boolean wasTouchExplorationEnabled = mIsTouchExplorationEnabled; @@ -1218,6 +1237,8 @@ public final class AccessibilityManager { if (wasHighTextContrastEnabled != highTextContrastEnabled) { notifyHighTextContrastStateChanged(); } + + updateAccessibilityTracingState(accessibilityTracingEnabled); } /** @@ -1674,6 +1695,15 @@ public final class AccessibilityManager { } } + /** + * Update mIsAccessibilityTracingEnabled. + */ + private void updateAccessibilityTracingState(boolean enabled) { + synchronized (mLock) { + mIsAccessibilityTracingEnabled = enabled; + } + } + /** * Update interactive and non-interactive UI timeout. * diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java index cfdb2b769a08e..8643a37bba8dc 100644 --- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java +++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java @@ -163,4 +163,7 @@ public class AccessibilityServiceConnectionImpl extends IAccessibilityServiceCon public void setGestureDetectionPassthroughRegion(int displayId, Region region) {} public void setFocusAppearance(int strokeWidth, int color) {} + + public void logTrace(long timestamp, String where, String callingParams, int processId, + long threadId, int callingUid, Bundle callingStack) {} } diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java index 88e6b66e23a3d..e9c9899023b17 100644 --- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java +++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java @@ -22,6 +22,7 @@ import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILIT import static android.accessibilityservice.AccessibilityService.KEY_ACCESSIBILITY_SCREENSHOT_TIMESTAMP; import static android.accessibilityservice.AccessibilityServiceInfo.DEFAULT; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY; +import static android.view.accessibility.AccessibilityInteractionClient.CALL_STACK; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS; import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK; @@ -1979,4 +1980,15 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ "strokeWidth=" + strokeWidth + ";color=" + color); } } + + @Override + public void logTrace(long timestamp, String where, String callingParams, int processId, + long threadId, int callingUid, Bundle callingStack) { + if (mTrace.isA11yTracingEnabled()) { + ArrayList list = + (ArrayList) callingStack.getSerializable(CALL_STACK); + mTrace.logTrace(timestamp, where, callingParams, processId, threadId, callingUid, + list.toArray(new StackTraceElement[list.size()])); + } + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index e7ffb1a64d4f0..8e803b3619fbe 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -1409,7 +1409,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private int getClientStateLocked(AccessibilityUserState userState) { - return userState.getClientStateLocked(mUiAutomationManager.isUiAutomationRunningLocked()); + return userState.getClientStateLocked( + mUiAutomationManager.isUiAutomationRunningLocked(), + mA11yController.isAccessibilityTracingEnabled()); } private InteractionBridge getInteractionBridge() { @@ -3322,7 +3324,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mConnectionId = service.mId; - mClient = AccessibilityInteractionClient.getInstance(); + mClient = AccessibilityInteractionClient.getInstance(mContext); mClient.addConnection(mConnectionId, service); //TODO: (multi-display) We need to support multiple displays. @@ -3865,6 +3867,24 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub return mA11yController.isAccessibilityTracingEnabled(); } + @Override + public void startTrace() { + if (!mA11yController.isAccessibilityTracingEnabled()) { + mA11yController.startTrace(); + final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); + scheduleUpdateClientsIfNeededLocked(userState); + } + } + + @Override + public void stopTrace() { + if (mA11yController.isAccessibilityTracingEnabled()) { + mA11yController.stopTrace(); + final AccessibilityUserState userState = getUserStateLocked(UserHandle.USER_SYSTEM); + scheduleUpdateClientsIfNeededLocked(userState); + } + } + @Override public void logTrace(String where) { logTrace(where, ""); @@ -3875,4 +3895,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mA11yController.logTrace(where, callingParams, "".getBytes(), Binder.getCallingUid(), Thread.currentThread().getStackTrace()); } + + @Override + public void logTrace(long timestamp, String where, String callingParams, int processId, + long threadId, int callingUid, StackTraceElement[] callStack) { + if (mA11yController.isAccessibilityTracingEnabled()) { + mA11yController.logTrace(where, callingParams, "".getBytes(), callingUid, callStack, + timestamp, processId, threadId); + } + } } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java index 0d323fb186c9c..16ce177c56cfd 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityShellCommand.java @@ -108,16 +108,12 @@ final class AccessibilityShellCommand extends ShellCommand { } private int startTrace() { - WindowManagerInternal.AccessibilityControllerInternal ac = - mWindowManagerService.getAccessibilityController(); - ac.startTrace(); + mService.startTrace(); return 0; } private int stopTrace() { - WindowManagerInternal.AccessibilityControllerInternal ac = - mWindowManagerService.getAccessibilityController(); - ac.stopTrace(); + mService.stopTrace(); return 0; } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java index 0c03877d6e441..03914138cd429 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityTrace.java @@ -24,6 +24,16 @@ public interface AccessibilityTrace { */ boolean isA11yTracingEnabled(); + /** + * Start tracing. + */ + void startTrace(); + + /** + * Stop tracing. + */ + void stopTrace(); + /** * Log one trace entry. * @param where A string to identify this log entry, which can be used to filter/search @@ -38,4 +48,19 @@ public interface AccessibilityTrace { * @param callingParams The parameters for the method to be logged. */ void logTrace(String where, String callingParams); + + /** + * Log one trace entry. Accessibility services using AccessibilityInteractionClient to + * make screen content related requests use this API to log entry when receive callback. + * @param timestamp The timestamp when a callback is received. + * @param where A string to identify this log entry, which can be used to filter/search + * through the tracing file. + * @param callingParams The parameters for the callback. + * @param processId The process id of the calling component. + * @param threadId The threadId of the calling component. + * @param callingUid The calling uid of the callback. + * @param callStack The call stack of the callback. + */ + void logTrace(long timestamp, String where, String callingParams, int processId, + long threadId, int callingUid, StackTraceElement[] callStack); } diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java index eb30fdeddd909..df349c863128e 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityUserState.java @@ -391,7 +391,7 @@ class AccessibilityUserState { return mBoundServices; } - int getClientStateLocked(boolean isUiAutomationRunning) { + int getClientStateLocked(boolean isUiAutomationRunning, boolean isTracingEnabled) { int clientState = 0; final boolean a11yEnabled = isUiAutomationRunning || isHandlingAccessibilityEventsLocked(); @@ -407,6 +407,9 @@ class AccessibilityUserState { if (mIsTextHighContrastEnabled) { clientState |= AccessibilityManager.STATE_FLAG_HIGH_TEXT_CONTRAST_ENABLED; } + if (isTracingEnabled) { + clientState |= AccessibilityManager.STATE_FLAG_ACCESSIBILITY_TRACING_ENABLED; + } return clientState; } diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 16692e169a3d3..d23579a1da89f 100644 --- a/services/core/java/com/android/server/wm/AccessibilityController.java +++ b/services/core/java/com/android/server/wm/AccessibilityController.java @@ -1903,6 +1903,14 @@ final class AccessibilityController { StackTraceElement[] stackTrace) { mTracing.logState(where, callingParams, a11yDump, callingUid, stackTrace); } + + @Override + public void logTrace( + String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] callStack, long timeStamp, int processId, long threadId) { + mTracing.logState(where, callingParams, a11yDump, callingUid, callStack, timeStamp, + processId, threadId); + } } private static final class AccessibilityTracing { @@ -1916,7 +1924,7 @@ final class AccessibilityController { } } - private static final int BUFFER_CAPACITY = 4096 * 1024; + private static final int BUFFER_CAPACITY = 1024 * 1024 * 12; private static final String TRACE_FILENAME = "/data/misc/a11ytrace/a11y_trace.pb"; private static final String TRACE_DIRECTORY = "/data/misc/a11ytrace/"; private static final String TAG = "AccessibilityTracing"; @@ -2034,7 +2042,22 @@ final class AccessibilityController { return; } - log(where, callingParams, a11yDump, callingUid, stackTrace); + log(where, callingParams, a11yDump, callingUid, stackTrace, + SystemClock.elapsedRealtimeNanos(), + Process.myPid() + ":" + Application.getProcessName(), + Thread.currentThread().getId() + ":" + Thread.currentThread().getName()); + } + + /** + * Write an accessibility trace log entry. + */ + void logState(String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] callingStack, long timeStamp, int processId, long threadId) { + if (!mEnabled) { + return; + } + log(where, callingParams, a11yDump, callingUid, callingStack, timeStamp, + String.valueOf(processId), String.valueOf(threadId)); } private String toStackTraceString(StackTraceElement[] stackTraceElements) { @@ -2058,18 +2081,17 @@ final class AccessibilityController { * Write the current state to the buffer */ private void log(String where, String callingParams, byte[] a11yDump, int callingUid, - StackTraceElement[] stackTrace) { - SimpleDateFormat fm = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + StackTraceElement[] callingStack, long timeStamp, String processName, + String threadName) { SomeArgs args = SomeArgs.obtain(); - args.arg1 = SystemClock.elapsedRealtimeNanos(); - args.arg2 = fm.format(new Date()).toString(); - args.arg3 = where; - args.arg4 = Process.myPid() + ":" + Application.getProcessName(); - args.arg5 = Thread.currentThread().getId() + ":" + Thread.currentThread().getName(); - args.arg6 = callingUid; - args.arg7 = callingParams; - args.arg8 = stackTrace; - args.arg9 = a11yDump; + args.arg1 = timeStamp; + args.arg2 = where; + args.arg3 = processName; + args.arg4 = threadName; + args.arg5 = callingUid; + args.arg6 = callingParams; + args.arg7 = callingStack; + args.arg8 = a11yDump; mHandler.obtainMessage(LogHandler.MESSAGE_LOG_TRACE_ENTRY, args).sendToTarget(); } @@ -2100,17 +2122,26 @@ final class AccessibilityController { long tokenOuter = os.start(ENTRY); String callingStack = - toStackTraceString((StackTraceElement[]) args.arg8); + toStackTraceString((StackTraceElement[]) args.arg7); - os.write(ELAPSED_REALTIME_NANOS, (long) args.arg1); - os.write(CALENDAR_TIME, (String) args.arg2); - os.write(WHERE, (String) args.arg3); - os.write(PROCESS_NAME, (String) args.arg4); - os.write(THREAD_ID_NAME, (String) args.arg5); - os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg6)); - os.write(CALLING_PARAMS, (String) args.arg7); + long reportedTimeStampNanos = (long) args.arg1; + long currentElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos(); + long timeDiffNanos = + currentElapsedRealtimeNanos - reportedTimeStampNanos; + long currentTimeMillis = (new Date()).getTime(); + long reportedTimeMillis = + currentTimeMillis - (long) (timeDiffNanos / 1000000); + SimpleDateFormat fm = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); + + os.write(ELAPSED_REALTIME_NANOS, reportedTimeStampNanos); + os.write(CALENDAR_TIME, fm.format(reportedTimeMillis).toString()); + os.write(WHERE, (String) args.arg2); + os.write(PROCESS_NAME, (String) args.arg3); + os.write(THREAD_ID_NAME, (String) args.arg4); + os.write(CALLING_PKG, pmInternal.getNameForUid((int) args.arg5)); + os.write(CALLING_PARAMS, (String) args.arg6); os.write(CALLING_STACKS, callingStack); - os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg9); + os.write(ACCESSIBILITY_SERVICE, (byte[]) args.arg8); long tokenInner = os.start(WINDOW_MANAGER_SERVICE); synchronized (mService.mGlobalLock) { diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java index ab515d448f303..47087cfbd147b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerInternal.java +++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java @@ -81,6 +81,22 @@ public abstract class WindowManagerInternal { void logTrace( String where, String callingParams, byte[] a11yDump, int callingUid, StackTraceElement[] stackTrace); + + /** + * Add an accessibility trace entry. + * + * @param where A string to identify this log entry, which can be used to filter/search + * through the tracing file. + * @param callingParams The parameters for the method to be logged. + * @param a11yDump The proto byte array for a11y state when the entry is generated. + * @param callingUid The calling uid. + * @param callStack The call stack of the method to be logged. + * @param timeStamp The time when the method to be logged is called. + * @param processId The calling process Id. + * @param threadId The calling thread Id. + */ + void logTrace(String where, String callingParams, byte[] a11yDump, int callingUid, + StackTraceElement[] callStack, long timeStamp, int processId, long threadId); } /**