Merge "Fix Accessibility apps crash when adding view by" into sc-v2-dev am: 57d4674922 am: 375343f295
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/14580807 Change-Id: I9fbf087a36939ea1245d93c1be20f927e3e5dd07
This commit is contained in:
@@ -16,6 +16,8 @@
|
||||
|
||||
package android.accessibilityservice;
|
||||
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
|
||||
|
||||
import android.accessibilityservice.GestureDescription.MotionEventGenerator;
|
||||
import android.annotation.CallbackExecutor;
|
||||
import android.annotation.ColorInt;
|
||||
@@ -27,6 +29,7 @@ import android.annotation.TestApi;
|
||||
import android.app.Service;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.graphics.Bitmap;
|
||||
@@ -36,6 +39,7 @@ import android.graphics.Region;
|
||||
import android.hardware.HardwareBuffer;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
@@ -961,30 +965,31 @@ public abstract class AccessibilityService extends Service {
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Context createDisplayContext(Display display) {
|
||||
final Context context = super.createDisplayContext(display);
|
||||
final int displayId = display.getDisplayId();
|
||||
setDefaultTokenInternal(context, displayId);
|
||||
return context;
|
||||
return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
|
||||
}
|
||||
|
||||
private void setDefaultTokenInternal(Context context, int displayId) {
|
||||
final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE);
|
||||
final IAccessibilityServiceConnection connection =
|
||||
AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
|
||||
IBinder token = null;
|
||||
if (connection != null) {
|
||||
synchronized (mLock) {
|
||||
try {
|
||||
token = connection.getOverlayWindowToken(displayId);
|
||||
} catch (RemoteException re) {
|
||||
Log.w(LOG_TAG, "Failed to get window token", re);
|
||||
re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
wm.setDefaultToken(token);
|
||||
@NonNull
|
||||
@Override
|
||||
public Context createWindowContext(int type, @Nullable Bundle options) {
|
||||
final Context context = super.createWindowContext(type, options);
|
||||
if (type != TYPE_ACCESSIBILITY_OVERLAY) {
|
||||
return context;
|
||||
}
|
||||
return new AccessibilityContext(context, mConnectionId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Context createWindowContext(@NonNull Display display, int type,
|
||||
@Nullable Bundle options) {
|
||||
final Context context = super.createWindowContext(display, type, options);
|
||||
if (type != TYPE_ACCESSIBILITY_OVERLAY) {
|
||||
return context;
|
||||
}
|
||||
return new AccessibilityContext(context, mConnectionId);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2675,4 +2680,58 @@ public abstract class AccessibilityService extends Service {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class AccessibilityContext extends ContextWrapper {
|
||||
private final int mConnectionId;
|
||||
|
||||
private AccessibilityContext(Context base, int connectionId) {
|
||||
super(base);
|
||||
mConnectionId = connectionId;
|
||||
setDefaultTokenInternal(this, getDisplayId());
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Context createDisplayContext(Display display) {
|
||||
return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Context createWindowContext(int type, @Nullable Bundle options) {
|
||||
final Context context = super.createWindowContext(type, options);
|
||||
if (type != TYPE_ACCESSIBILITY_OVERLAY) {
|
||||
return context;
|
||||
}
|
||||
return new AccessibilityContext(context, mConnectionId);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Context createWindowContext(@NonNull Display display, int type,
|
||||
@Nullable Bundle options) {
|
||||
final Context context = super.createWindowContext(display, type, options);
|
||||
if (type != TYPE_ACCESSIBILITY_OVERLAY) {
|
||||
return context;
|
||||
}
|
||||
return new AccessibilityContext(context, mConnectionId);
|
||||
}
|
||||
|
||||
private void setDefaultTokenInternal(Context context, int displayId) {
|
||||
final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(
|
||||
WINDOW_SERVICE);
|
||||
final IAccessibilityServiceConnection connection =
|
||||
AccessibilityInteractionClient.getConnection(mConnectionId);
|
||||
IBinder token = null;
|
||||
if (connection != null) {
|
||||
try {
|
||||
token = connection.getOverlayWindowToken(displayId);
|
||||
} catch (RemoteException re) {
|
||||
Log.w(LOG_TAG, "Failed to get window token", re);
|
||||
re.rethrowFromSystemServer();
|
||||
}
|
||||
wm.setDefaultToken(token);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,18 +16,40 @@
|
||||
|
||||
package android.accessibilityservice;
|
||||
|
||||
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY;
|
||||
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.display.VirtualDisplay;
|
||||
import android.media.ImageReader;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.util.SparseArray;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.WindowManagerGlobal;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.window.WindowTokenClient;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -42,6 +64,8 @@ import org.mockito.MockitoAnnotations;
|
||||
public class AccessibilityServiceTest {
|
||||
private static final String TAG = "AccessibilityServiceTest";
|
||||
private static final int CONNECTION_ID = 1;
|
||||
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams(
|
||||
TYPE_ACCESSIBILITY_OVERLAY);
|
||||
|
||||
private static class AccessibilityServiceTestClass extends AccessibilityService {
|
||||
private IAccessibilityServiceClient mCallback;
|
||||
@@ -49,7 +73,11 @@ public class AccessibilityServiceTest {
|
||||
|
||||
AccessibilityServiceTestClass() {
|
||||
super();
|
||||
attachBaseContext(InstrumentationRegistry.getContext());
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
final Display display = context.getSystemService(DisplayManager.class)
|
||||
.getDisplay(DEFAULT_DISPLAY);
|
||||
|
||||
attachBaseContext(context.createTokenContext(new WindowTokenClient(), display));
|
||||
mLooper = InstrumentationRegistry.getContext().getMainLooper();
|
||||
}
|
||||
|
||||
@@ -78,14 +106,33 @@ public class AccessibilityServiceTest {
|
||||
private @Mock IBinder mMockIBinder;
|
||||
private IAccessibilityServiceClient mServiceInterface;
|
||||
private AccessibilityServiceTestClass mService;
|
||||
private final SparseArray<IBinder> mWindowTokens = new SparseArray<>();
|
||||
|
||||
@Before
|
||||
public void setUp() throws RemoteException {
|
||||
public void setUp() throws Exception {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mService = new AccessibilityServiceTestClass();
|
||||
mService.onCreate();
|
||||
mService.setupCallback(mMockClientForCallback);
|
||||
mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent());
|
||||
mServiceInterface.init(mMockConnection, CONNECTION_ID, mMockIBinder);
|
||||
doAnswer(invocation -> {
|
||||
Object[] args = invocation.getArguments();
|
||||
final int displayId = (int) args[0];
|
||||
final IBinder token = new Binder();
|
||||
WindowManagerGlobal.getWindowManagerService().addWindowToken(token,
|
||||
TYPE_ACCESSIBILITY_OVERLAY, displayId, null /* options */);
|
||||
mWindowTokens.put(displayId, token);
|
||||
return token;
|
||||
}).when(mMockConnection).getOverlayWindowToken(anyInt());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
for (int i = mWindowTokens.size() - 1; i >= 0; --i) {
|
||||
WindowManagerGlobal.getWindowManagerService().removeWindowToken(
|
||||
mWindowTokens.valueAt(i), mWindowTokens.keyAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -101,4 +148,79 @@ public class AccessibilityServiceTest {
|
||||
|
||||
verify(mMockConnection).getSystemActions();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddViewWithA11yServiceDerivedDisplayContext() throws Exception {
|
||||
try (VirtualDisplaySession session = new VirtualDisplaySession()) {
|
||||
final Context context = mService.createDisplayContext(session.getDisplay());
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(
|
||||
() -> context.getSystemService(WindowManager.class)
|
||||
.addView(new View(context), mParams)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddViewWithA11yServiceDerivedWindowContext() throws Exception {
|
||||
try (VirtualDisplaySession session = new VirtualDisplaySession()) {
|
||||
final Context context = mService.createDisplayContext(session.getDisplay())
|
||||
.createWindowContext(TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(
|
||||
() -> context.getSystemService(WindowManager.class)
|
||||
.addView(new View(context), mParams)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddViewWithA11yServiceDerivedWindowContextWithDisplay() throws Exception {
|
||||
try (VirtualDisplaySession session = new VirtualDisplaySession()) {
|
||||
final Context context = mService.createWindowContext(session.getDisplay(),
|
||||
TYPE_ACCESSIBILITY_OVERLAY, null /* options */);
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(
|
||||
() -> context.getSystemService(WindowManager.class)
|
||||
.addView(new View(context), mParams)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = WindowManager.BadTokenException.class)
|
||||
public void testAddViewWithA11yServiceDerivedWindowContextWithDifferentType()
|
||||
throws Exception {
|
||||
try (VirtualDisplaySession session = new VirtualDisplaySession()) {
|
||||
final Context context = mService.createWindowContext(session.getDisplay(),
|
||||
TYPE_APPLICATION_OVERLAY, null /* options */);
|
||||
InstrumentationRegistry.getInstrumentation().runOnMainSync(
|
||||
() -> context.getSystemService(WindowManager.class)
|
||||
.addView(new View(context), mParams)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class VirtualDisplaySession implements AutoCloseable {
|
||||
private final VirtualDisplay mVirtualDisplay;
|
||||
|
||||
VirtualDisplaySession() {
|
||||
final DisplayManager displayManager = ApplicationProvider.getApplicationContext()
|
||||
.getSystemService(DisplayManager.class);
|
||||
final int width = 800;
|
||||
final int height = 480;
|
||||
final int density = 160;
|
||||
ImageReader reader = ImageReader.newInstance(width, height, PixelFormat.RGBA_8888,
|
||||
2 /* maxImages */);
|
||||
mVirtualDisplay = displayManager.createVirtualDisplay(
|
||||
TAG, width, height, density, reader.getSurface(),
|
||||
VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY);
|
||||
}
|
||||
|
||||
private Display getDisplay() {
|
||||
return mVirtualDisplay.getDisplay();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws Exception {
|
||||
mVirtualDisplay.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user