Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d91b8225 authored by Daniel Norman's avatar Daniel Norman
Browse files

fix: Provide copied SurfaceControl from app for A11yService window screenshots

This should help prevent use-after-release of this SurfaceControl if the
app quits during/after the screenshot is requested.

Bug: 431752508
Flag: android.view.accessibility.copy_surface_control_for_window_screenshots
Test: AccessibilityInteractionControllerTest
Change-Id: Id77cafd3355db181397a68f16198e0e3ead1f44b
parent 18d73a38
Loading
Loading
Loading
Loading
+14 −2
Original line number Diff line number Diff line
@@ -649,8 +649,20 @@ public final class AccessibilityInteractionController {

    private void getWindowSurfaceInfoUiThread(IWindowSurfaceInfoCallback callback) {
        try {
            if (Flags.copySurfaceControlForWindowScreenshots()) {
                SurfaceControl sc = mViewRootImpl.getSurfaceControl();
                if (sc.isValid()) {
                    SurfaceControl copiedSc = new SurfaceControl(sc,
                            "AccessibilityInteractionController"
                                    + "#getWindowSurfaceInfoUiThread");
                    callback.provideWindowSurfaceInfo(mViewRootImpl.getWindowFlags(),
                            Process.myUid(),
                            copiedSc);
                }
            } else {
                callback.provideWindowSurfaceInfo(mViewRootImpl.getWindowFlags(), Process.myUid(),
                        mViewRootImpl.getSurfaceControl());
            }
        } catch (RemoteException re) {
            // ignore - the other side will time out
        }
+11 −0
Original line number Diff line number Diff line
@@ -108,6 +108,17 @@ flag {
    bug: "280130713"
}

flag {
    name: "copy_surface_control_for_window_screenshots"
    namespace: "accessibility"
    description: "Copies and sends the copied SurfaceControl when an A11yService requests a single-window app screenshot"
    bug: "431752508"
    metadata {
        purpose: PURPOSE_BUGFIX
    }
}


flag {
    name: "deprecate_accessibility_announcement_apis"
    namespace: "accessibility"
+23 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package android.view;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import static org.mockito.ArgumentMatchers.eq;

import android.accessibilityservice.AccessibilityServiceInfo;
import android.app.Activity;
import android.app.Instrumentation;
@@ -52,6 +54,7 @@ import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;

import java.util.List;
@@ -145,6 +148,7 @@ public class AccessibilityInteractionControllerTest {
    }

    @Test
    @DisableFlags(android.view.accessibility.Flags.FLAG_COPY_SURFACE_CONTROL_FOR_WINDOW_SCREENSHOTS)
    public void getWindowSurfaceInfo_shouldCallCallbackWithWindowSurfaceDataFromVri()
            throws Exception {
        final ViewRootImpl vri = mButton.getRootView().getViewRootImpl();
@@ -158,6 +162,25 @@ public class AccessibilityInteractionControllerTest {
                vri.getWindowFlags(), Process.myUid(), vri.getSurfaceControl());
    }

    @Test
    @EnableFlags(android.view.accessibility.Flags.FLAG_COPY_SURFACE_CONTROL_FOR_WINDOW_SCREENSHOTS)
    public void getWindowSurfaceInfo_shouldCallCallbackWithWindowSurfaceData_CopiedFromVri()
            throws Exception {
        final ViewRootImpl vri = mButton.getRootView().getViewRootImpl();
        IWindowSurfaceInfoCallback callback = Mockito.mock(IWindowSurfaceInfoCallback.class);

        sInstrumentation.runOnMainSync(() ->
                mAccessibilityInteractionController.getWindowSurfaceInfoClientThread(callback));
        sInstrumentation.waitForIdleSync();

        ArgumentCaptor<SurfaceControl> scCaptor = ArgumentCaptor.forClass(SurfaceControl.class);
        Mockito.verify(callback).provideWindowSurfaceInfo(
                eq(vri.getWindowFlags()), eq(Process.myUid()), scCaptor.capture());
        SurfaceControl providedSc = scCaptor.getValue();
        assertThat(providedSc).isNotSameInstanceAs(vri.getSurfaceControl());
        assertThat(providedSc.isSameSurface(vri.getSurfaceControl())).isTrue();
    }

    @Test
    @EnableFlags(android.view.accessibility.Flags.FLAG_IGNORE_UNIMPORTANT_ROOT)
    public void getRootView_isUnimportant_returnsNull() {
+4 −0
Original line number Diff line number Diff line
@@ -1503,6 +1503,10 @@ abstract class AbstractAccessibilityServiceConnection extends IAccessibilityServ
                                    // ignore - the other side will time out
                                }
                            }
                            if (android.view.accessibility.Flags
                                    .copySurfaceControlForWindowScreenshots()) {
                                surfaceControl.release();
                            }
                        }
                    };
            connection.getRemote().getWindowSurfaceInfo(infoCallback);