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); } /**