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

Commit 64bbd4b1 authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

Block untrusted touches opt-in

The feature is disabled by default and can be in one of 3 modes:
disabled, permissive and block. In permissive we only log but
don't block the touch. This knob is implemented in a global setting
block_untrusted_touches. It can also be disabled per occluding app using
app-compat infrastructure, so if you disable for a certain app, overlays
of that app won't have the chance of blocking touches. More details
on these on go/try-cross-uid-touches.

Each window has 3 modes related to touch occlusion: ALLOW, USE_OPACITY
or BLOCK_UNTRUSTRED. Check code comments for the meaning of each. If the
feature is turned off for the app, then the mode is ALLOW. Else if it's
a SAW, then it's USE_OPACITY. Else it's BLOCK_UNTRUSTED. These states
are passed to InputDispatcher, who then perform the proper checks and
blocks or not the touch.

If input dispatcher deems the touch unsafe, depending on the feature
mode, we filter out such touches and log a message to logcat.

I also introduce a global (secure) setting
MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH which represents the maximum
opacity allowed per UID that's obscuring the touch-consuming window
according to some rules, which are discussed in topic CL for
InputDispatcher code. This maximum is initially set to 0.8, but we'll
be conducting local experiments to determine the final value.

Test: atest WindowUntrustedTouchTest
Test: atest inputflinger_tests inputflinger_benchmarks libinput_tests
Test: go/try-cross-uid-touches for manual testing
Bug: 158002302
Change-Id: I462858ad5f0d11b1261748489385e6409e38e4b1
parent abd78597
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -330,6 +330,7 @@ filegroup {
        ":gatekeeper_aidl",
        ":gsiservice_aidl",
        ":incidentcompanion_aidl",
        ":inputconstants_aidl",
        ":installd_aidl",
        ":keystore_aidl",
        ":libaudioclient_aidl",
+12 −0
Original line number Diff line number Diff line
@@ -1451,6 +1451,18 @@ package android.hardware.hdmi {

}

package android.hardware.input {

  public final class InputManager {
    method public int getBlockUntrustedTouchesMode(@NonNull android.content.Context);
    method public float getMaximumObscuringOpacityForTouch(@NonNull android.content.Context);
    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setBlockUntrustedTouchesMode(@NonNull android.content.Context, int);
    method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void setMaximumObscuringOpacityForTouch(@NonNull android.content.Context, float);
    field public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L; // 0x96aec7eL
  }

}

package android.hardware.lights {

  public final class Light implements android.os.Parcelable {
+137 −0
Original line number Diff line number Diff line
@@ -16,17 +16,22 @@

package android.hardware.input;

import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.ChangeId;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.media.AudioAttributes;
import android.os.Binder;
import android.os.BlockUntrustedTouchesMode;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -48,8 +53,10 @@ import android.view.InputMonitor;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.VerifiedInputEvent;
import android.view.WindowManager.LayoutParams;

import com.android.internal.os.SomeArgs;
import com.android.internal.util.ArrayUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -69,6 +76,13 @@ public final class InputManager {
    private static final int MSG_DEVICE_REMOVED = 2;
    private static final int MSG_DEVICE_CHANGED = 3;

    /** @hide */
    public static final int[] BLOCK_UNTRUSTED_TOUCHES_MODES = {
            BlockUntrustedTouchesMode.DISABLED,
            BlockUntrustedTouchesMode.PERMISSIVE,
            BlockUntrustedTouchesMode.BLOCK
    };

    private static InputManager sInstance;

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -167,6 +181,32 @@ public final class InputManager {
     */
    public static final int DEFAULT_POINTER_SPEED = 0;

    /**
     * The maximum allowed obscuring opacity by UID to propagate touches (0 <= x <= 1).
     * @hide
     */
    public static final float DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH = .8f;

    /**
     * Default mode of the block untrusted touches mode feature.
     * @hide
     */
    @BlockUntrustedTouchesMode
    public static final int DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE =
            BlockUntrustedTouchesMode.DISABLED;

    /**
     * Prevent touches from being consumed by apps if these touches passed through a non-trusted
     * window from a different UID and are considered unsafe.
     *
     * TODO(b/158002302): Turn the feature on by default
     *
     * @hide
     */
    @TestApi
    @ChangeId
    public static final long BLOCK_UNTRUSTED_TOUCHES = 158002302L;

    /**
     * Input Event Injection Synchronization Mode: None.
     * Never blocks.  Injection is asynchronous and is assumed always to be successful.
@@ -831,6 +871,103 @@ public final class InputManager {
        }
    }

    /**
     * Returns the maximum allowed obscuring opacity by UID to propagate touches.
     *
     * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
     * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
     * above the touch-consuming window.
     *
     * @see #setMaximumObscuringOpacityForTouch(Context, float)
     *
     * @hide
     */
    @TestApi
    public float getMaximumObscuringOpacityForTouch(@NonNull Context context) {
        return Settings.Global.getFloat(context.getContentResolver(),
                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH,
                DEFAULT_MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH);
    }

    /**
     * Sets the maximum allowed obscuring opacity by UID to propagate touches.
     *
     * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
     * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
     * above the touch-consuming window.
     *
     * For a certain UID:
     * <ul>
     *     <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
     *     the touch.
     *     <li>Otherwise take all its windows of eligible window types above the touch-consuming
     *     window, compute their combined obscuring opacity considering that {@code
     *     opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
     *     lesser than or equal to this setting and there are no other windows preventing the
     *     touch, allow the UID to propagate the touch.
     * </ul>
     *
     * This value should be between 0 (inclusive) and 1 (inclusive).
     *
     * @see #getMaximumObscuringOpacityForTouch(Context)
     *
     * @hide
     */
    @TestApi
    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
    public void setMaximumObscuringOpacityForTouch(@NonNull Context context, float opacity) {
        if (opacity < 0 || opacity > 1) {
            throw new IllegalArgumentException(
                    "Maximum obscuring opacity for touch should be >= 0 and <= 1");
        }
        Settings.Global.putFloat(context.getContentResolver(),
                Settings.Global.MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH, opacity);
    }

    /**
     * Returns the current mode of the block untrusted touches feature, one of:
     * <ul>
     *     <li>{@link BlockUntrustedTouchesMode#DISABLED}
     *     <li>{@link BlockUntrustedTouchesMode#PERMISSIVE}
     *     <li>{@link BlockUntrustedTouchesMode#BLOCK}
     * </ul>
     *
     * @hide
     */
    @TestApi
    @BlockUntrustedTouchesMode
    public int getBlockUntrustedTouchesMode(@NonNull Context context) {
        int mode = Settings.Global.getInt(context.getContentResolver(),
                Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE);
        if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) {
            Log.w(TAG, "Unknown block untrusted touches feature mode " + mode + ", using "
                    + "default " + DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE);
            return DEFAULT_BLOCK_UNTRUSTED_TOUCHES_MODE;
        }
        return mode;
    }

    /**
     * Sets the mode of the block untrusted touches feature to one of:
     * <ul>
     *     <li>{@link BlockUntrustedTouchesMode#DISABLED}
     *     <li>{@link BlockUntrustedTouchesMode#PERMISSIVE}
     *     <li>{@link BlockUntrustedTouchesMode#BLOCK}
     * </ul>
     *
     * @hide
     */
    @TestApi
    @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
    public void setBlockUntrustedTouchesMode(@NonNull Context context,
            @BlockUntrustedTouchesMode int mode) {
        if (!ArrayUtils.contains(BLOCK_UNTRUSTED_TOUCHES_MODES, mode)) {
            throw new IllegalArgumentException("Invalid feature mode " + mode);
        }
        Settings.Global.putInt(context.getContentResolver(),
                Settings.Global.BLOCK_UNTRUSTED_TOUCHES_MODE, mode);
    }

    /**
     * Queries the framework about whether any physical keys exist on the
     * any keyboard attached to the device that are capable of producing the given
+43 −0
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@ import android.util.ArraySet;
import android.util.Log;
import android.util.MemoryIntArray;
import android.view.Display;
import android.view.WindowManager.LayoutParams;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
@@ -14486,6 +14487,48 @@ public final class Settings {
         * @hide
         */
        public static final String SHOW_PEOPLE_SPACE = "show_people_space";
        /**
         * Block untrusted touches mode.
         *
         * Can be one of:
         * <ul>
         *      <li>0 = {@link BlockUntrustedTouchesMode#DISABLED}: Feature is off.
         *      <li>1 = {@link BlockUntrustedTouchesMode#PERMISSIVE}: Untrusted touches are flagged
         *          but not blocked
         *      <li>2 = {@link BlockUntrustedTouchesMode#BLOCK}: Untrusted touches are blocked
         * </ul>
         *
         * @hide
         */
        public static final String BLOCK_UNTRUSTED_TOUCHES_MODE = "block_untrusted_touches";
        /**
         * The maximum allowed obscuring opacity by UID to propagate touches.
         *
         * For certain window types (eg. SAWs), the decision of honoring {@link LayoutParams
         * #FLAG_NOT_TOUCHABLE} or not depends on the combined obscuring opacity of the windows
         * above the touch-consuming window.
         *
         * For a certain UID:
         * <ul>
         *     <li>If it's the same as the UID of the touch-consuming window, allow it to propagate
         *     the touch.
         *     <li>Otherwise take all its windows of eligible window types above the touch-consuming
         *     window, compute their combined obscuring opacity considering that {@code
         *     opacity(A, B) = 1 - (1 - opacity(A))*(1 - opacity(B))}. If the computed value is
         *     lesser than or equal to this setting and there are no other windows preventing the
         *     touch, allow the UID to propagate the touch.
         * </ul>
         *
         * @see android.hardware.input.InputManager#getMaximumObscuringOpacityForTouch(Context)
         * @see android.hardware.input.InputManager#setMaximumObscuringOpacityForTouch(Context,
         * float)
         *
         * @hide
         */
        public static final String MAXIMUM_OBSCURING_OPACITY_FOR_TOUCH =
                "maximum_obscuring_opacity_for_touch";
    }
    /**
+9 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.view.Display.INVALID_DISPLAY;
import android.annotation.Nullable;
import android.graphics.Region;
import android.os.IBinder;
import android.os.TouchOcclusionMode;

import java.lang.ref.WeakReference;

@@ -82,10 +83,18 @@ public final class InputWindowHandle {
    // Window is trusted overlay.
    public boolean trustedOverlay;

    // What effect this window has on touch occlusion if it lets touches pass through
    // By default windows will block touches if they are untrusted and from a different UID due to
    // security concerns
    public int touchOcclusionMode = TouchOcclusionMode.BLOCK_UNTRUSTED;

    // Id of process and user that owns the window.
    public int ownerPid;
    public int ownerUid;

    // Owner package of the window
    public String packageName;

    // Window input features.
    public int inputFeatures;

Loading