Merge "Fix Accessibility apps crash when adding view by" into sc-v2-dev
This commit is contained in:
@@ -16,6 +16,8 @@
|
|||||||
|
|
||||||
package android.accessibilityservice;
|
package android.accessibilityservice;
|
||||||
|
|
||||||
|
import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
|
||||||
|
|
||||||
import android.accessibilityservice.GestureDescription.MotionEventGenerator;
|
import android.accessibilityservice.GestureDescription.MotionEventGenerator;
|
||||||
import android.annotation.CallbackExecutor;
|
import android.annotation.CallbackExecutor;
|
||||||
import android.annotation.ColorInt;
|
import android.annotation.ColorInt;
|
||||||
@@ -27,6 +29,7 @@ import android.annotation.TestApi;
|
|||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.compat.annotation.UnsupportedAppUsage;
|
import android.compat.annotation.UnsupportedAppUsage;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.ContextWrapper;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.ParceledListSlice;
|
import android.content.pm.ParceledListSlice;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
@@ -36,6 +39,7 @@ import android.graphics.Region;
|
|||||||
import android.hardware.HardwareBuffer;
|
import android.hardware.HardwareBuffer;
|
||||||
import android.hardware.display.DisplayManager;
|
import android.hardware.display.DisplayManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@@ -961,30 +965,31 @@ public abstract class AccessibilityService extends Service {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public Context createDisplayContext(Display display) {
|
public Context createDisplayContext(Display display) {
|
||||||
final Context context = super.createDisplayContext(display);
|
return new AccessibilityContext(super.createDisplayContext(display), mConnectionId);
|
||||||
final int displayId = display.getDisplayId();
|
|
||||||
setDefaultTokenInternal(context, displayId);
|
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDefaultTokenInternal(Context context, int displayId) {
|
@NonNull
|
||||||
final WindowManagerImpl wm = (WindowManagerImpl) context.getSystemService(WINDOW_SERVICE);
|
@Override
|
||||||
final IAccessibilityServiceConnection connection =
|
public Context createWindowContext(int type, @Nullable Bundle options) {
|
||||||
AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
|
final Context context = super.createWindowContext(type, options);
|
||||||
IBinder token = null;
|
if (type != TYPE_ACCESSIBILITY_OVERLAY) {
|
||||||
if (connection != null) {
|
return context;
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
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;
|
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 static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
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.IBinder;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.os.RemoteException;
|
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.view.accessibility.AccessibilityEvent;
|
||||||
|
import android.window.WindowTokenClient;
|
||||||
|
|
||||||
import androidx.test.InstrumentationRegistry;
|
import androidx.test.InstrumentationRegistry;
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.filters.SmallTest;
|
import androidx.test.filters.SmallTest;
|
||||||
import androidx.test.runner.AndroidJUnit4;
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
@@ -42,6 +64,8 @@ import org.mockito.MockitoAnnotations;
|
|||||||
public class AccessibilityServiceTest {
|
public class AccessibilityServiceTest {
|
||||||
private static final String TAG = "AccessibilityServiceTest";
|
private static final String TAG = "AccessibilityServiceTest";
|
||||||
private static final int CONNECTION_ID = 1;
|
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 static class AccessibilityServiceTestClass extends AccessibilityService {
|
||||||
private IAccessibilityServiceClient mCallback;
|
private IAccessibilityServiceClient mCallback;
|
||||||
@@ -49,7 +73,11 @@ public class AccessibilityServiceTest {
|
|||||||
|
|
||||||
AccessibilityServiceTestClass() {
|
AccessibilityServiceTestClass() {
|
||||||
super();
|
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();
|
mLooper = InstrumentationRegistry.getContext().getMainLooper();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,14 +106,33 @@ public class AccessibilityServiceTest {
|
|||||||
private @Mock IBinder mMockIBinder;
|
private @Mock IBinder mMockIBinder;
|
||||||
private IAccessibilityServiceClient mServiceInterface;
|
private IAccessibilityServiceClient mServiceInterface;
|
||||||
private AccessibilityServiceTestClass mService;
|
private AccessibilityServiceTestClass mService;
|
||||||
|
private final SparseArray<IBinder> mWindowTokens = new SparseArray<>();
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws RemoteException {
|
public void setUp() throws Exception {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
mService = new AccessibilityServiceTestClass();
|
mService = new AccessibilityServiceTestClass();
|
||||||
|
mService.onCreate();
|
||||||
mService.setupCallback(mMockClientForCallback);
|
mService.setupCallback(mMockClientForCallback);
|
||||||
mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent());
|
mServiceInterface = (IAccessibilityServiceClient) mService.onBind(new Intent());
|
||||||
mServiceInterface.init(mMockConnection, CONNECTION_ID, mMockIBinder);
|
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
|
@Test
|
||||||
@@ -101,4 +148,79 @@ public class AccessibilityServiceTest {
|
|||||||
|
|
||||||
verify(mMockConnection).getSystemActions();
|
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