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

Commit 22b4f81b authored by Daniel Norman's avatar Daniel Norman
Browse files

Stop blocking preinstalled a11y tools from taking FLAG_SECURE screenshots.

Requires moving the caller of ScreenCapture's screenshot method from the
app process to the system_server process since capturing FLAG_SECURE
content, even for an app capturing its own content, requires permission
CAPTURE_BLACKOUT_CONTENT which is a signature permission.
system_server was already used for full-display accessibility screenshots.

Bug: 373705911
Test: atest AbstractAccessibilityServiceConnectionTest
Test: atest AccessibilityInteractionControllerTest
Test: atest AccessibilityTakeScreenshotTest
Flag: com.android.server.accessibility.allow_secure_screenshots
Change-Id: Ifb711222a42fa18abcabefafdef22daf46402955
parent c42e473b
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import android.view.accessibility.AccessibilityNodeProvider;
import android.view.accessibility.AccessibilityRequestPreparer;
import android.view.accessibility.Flags;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.IWindowSurfaceInfoCallback;
import android.window.ScreenCapture;

import com.android.internal.R;
@@ -631,6 +632,25 @@ public final class AccessibilityInteractionController {
        }
    }

    /**
     * Provide info for taking a screenshot of this app window.
     */
    public void getWindowSurfaceInfoClientThread(IWindowSurfaceInfoCallback callback) {
        Message message = PooledLambda.obtainMessage(
                AccessibilityInteractionController::getWindowSurfaceInfoUiThread,
                this, callback);
        mHandler.sendMessage(message);
    }

    private void getWindowSurfaceInfoUiThread(IWindowSurfaceInfoCallback callback) {
        try {
            callback.provideWindowSurfaceInfo(mViewRootImpl.getWindowFlags(), Process.myUid(),
                    mViewRootImpl.getSurfaceControl());
        } catch (RemoteException re) {
            // ignore - the other side will time out
        }
    }

    public void findFocusClientThread(long accessibilityNodeId, int focusType,
            Region interactiveRegion, int interactionId,
            IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
+10 −0
Original line number Diff line number Diff line
@@ -254,6 +254,7 @@ import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityEmbeddedConnection;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.IWindowSurfaceInfoCallback;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.autofill.AutofillManager;
@@ -12285,6 +12286,15 @@ public final class ViewRootImpl implements ViewParent,
            }
        }
        @Override
        public void getWindowSurfaceInfo(IWindowSurfaceInfoCallback callback) {
            ViewRootImpl viewRootImpl = mViewRootImpl.get();
            if (viewRootImpl != null && viewRootImpl.mView != null) {
                viewRootImpl.getAccessibilityInteractionController()
                        .getWindowSurfaceInfoClientThread(callback);
            }
        }
        public void attachAccessibilityOverlayToWindow(
                SurfaceControl sc,
                int interactionId,
+3 −0
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@ import android.view.MagnificationSpec;
import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
import android.view.accessibility.IWindowSurfaceInfoCallback;
import android.window.ScreenCapture;

/**
@@ -67,5 +68,7 @@ oneway interface IAccessibilityInteractionConnection {
        in ScreenCapture.ScreenCaptureListener listener,
        IAccessibilityInteractionConnectionCallback callback);

    void getWindowSurfaceInfo(IWindowSurfaceInfoCallback callback);

    void attachAccessibilityOverlayToWindow(in SurfaceControl sc, int interactionId, in IAccessibilityInteractionConnectionCallback callback);
}
+39 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.view.accessibility;

import android.view.SurfaceControl;

/**
 * Callback for an app to provide AbstractAccessibilityServiceConnection with window and surface
 * information necessary for system_server to take a screenshot of the app window.
 * @hide
 */
oneway interface IWindowSurfaceInfoCallback {

    /**
     * Provide info from ViewRootImpl for taking a screenshot of this app window.
     *
     * @param windowFlags the window flags of ViewRootImpl
     * @param processUid the process (kernel) uid, NOT the user ID, required for
                         SurfaceFlinger screenshots
     * @param surfaceControl the surface of ViewRootImpl
     */
    @RequiresNoPermission
    void provideWindowSurfaceInfo(int windowFlags, int processUid,
            in SurfaceControl surfaceControl);
}
+17 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.app.Instrumentation;
import android.app.Service;
import android.app.UiAutomation;
import android.graphics.Rect;
import android.os.Process;
import android.os.SystemClock;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityEvent;
@@ -32,6 +33,7 @@ import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityTestActivity;
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IWindowSurfaceInfoCallback;

import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -47,6 +49,7 @@ import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;

import java.util.List;
import java.util.concurrent.TimeoutException;
@@ -136,6 +139,20 @@ public class AccessibilityInteractionControllerTest {
        }
    }

    @Test
    public void getWindowSurfaceInfo_shouldCallCallbackWithWindowSurfaceDataFromVri()
            throws Exception {
        final ViewRootImpl vri = mButton.getRootView().getViewRootImpl();
        IWindowSurfaceInfoCallback callback = Mockito.mock(IWindowSurfaceInfoCallback.class);

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

        Mockito.verify(callback).provideWindowSurfaceInfo(
                vri.getWindowFlags(), Process.myUid(), vri.getSurfaceControl());
    }

    private void launchActivity() {
        final Object waitObject = new Object();
        final int[] location = new int[2];
Loading