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

Commit 64d3acf7 authored by Winson Chung's avatar Winson Chung
Browse files

Key input consumers by token and not by name

- Currently input consumers are singletons keyed by static name, which
  is problematic because the per-user Launcher process registers and
  unregisters the input consumer when SysUI binds to the current user's
  TouchInteractionService.  However, the ordering of service create
  and destroy when switching users is not guaranteed, so you can end up
  with user 2's service being created before user 1's service destroy is
  called.  Currently this crashes because we expect singletons.
- This change changes the tracking of input consumers to be by token
  (which is already passed by the caller), and uses the token for
  identifying the input consumer to destroy.

  In addition, it only enforces a singleton check for input consumers
  registered for the same user.  And when fetching an input consumer by
  name, the last registered consumer with that name takes precedence and
  is returned.  This allows multiple Launcher processes to register
  a consumer for the current user without preventing the closing Launcher
  service from removing its previously registered consumer.

Fixes: 300973954
Test: Verify with multiple users that there's no race between registering
      and destroying the recents input consumer



Change-Id: I64d2453c3671747b5799f26b628448fdf03bdb77
parent a6cb68cf
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -525,11 +525,11 @@ interface IWindowManager
        out InputChannel inputChannel);

    /**
     * Destroy an input consumer by name and display id.
     * Destroy an input consumer by token and display id.
     * This method will also dispose the input channels associated with that InputConsumer.
     */
    @UnsupportedAppUsage
    boolean destroyInputConsumer(String name, int displayId);
    boolean destroyInputConsumer(IBinder token, int displayId);

    /**
     * Return the touch region for the current IME window, or an empty region if there is none.
+2 −2
Original line number Diff line number Diff line
@@ -139,7 +139,7 @@ public class PipInputConsumer {
        final InputChannel inputChannel = new InputChannel();
        try {
            // TODO(b/113087003): Support Picture-in-picture in multi-display.
            mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
            mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
            mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
        } catch (RemoteException e) {
            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -163,7 +163,7 @@ public class PipInputConsumer {
        }
        try {
            // TODO(b/113087003): Support Picture-in-picture in multi-display.
            mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
            mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
        } catch (RemoteException e) {
            ProtoLog.e(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
                    "%s: Failed to destroy input consumer, %s", TAG, e);
+2 −2
Original line number Diff line number Diff line
@@ -139,7 +139,7 @@ public class InputConsumerController {
        if (mInputEventReceiver == null) {
            final InputChannel inputChannel = new InputChannel();
            try {
                mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
                mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
                mWindowManager.createInputConsumer(mToken, mName, DEFAULT_DISPLAY, inputChannel);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to create input consumer", e);
@@ -158,7 +158,7 @@ public class InputConsumerController {
    public void unregisterInputConsumer() {
        if (mInputEventReceiver != null) {
            try {
                mWindowManager.destroyInputConsumer(mName, DEFAULT_DISPLAY);
                mWindowManager.destroyInputConsumer(mToken, DEFAULT_DISPLAY);
            } catch (RemoteException e) {
                Log.e(TAG, "Failed to destroy input consumer", e);
            }
+1 −1
Original line number Diff line number Diff line
@@ -160,7 +160,7 @@ class InputConsumerImpl implements IBinder.DeathRecipient {
            if (dc == null) {
                return;
            }
            dc.getInputMonitor().destroyInputConsumer(mName);
            dc.getInputMonitor().destroyInputConsumer(mToken);
            unlinkFromDeathRecipient();
        }
    }
+31 −25
Original line number Diff line number Diff line
@@ -73,6 +73,7 @@ import com.android.server.inputmethod.InputMethodManagerInternal;

import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Set;
import java.util.function.Consumer;

@@ -104,7 +105,7 @@ final class InputMonitor {
     * The set of input consumer added to the window manager by name, which consumes input events
     * for the windows below it.
     */
    private final ArrayMap<String, InputConsumerImpl> mInputConsumers = new ArrayMap();
    private final ArrayList<InputConsumerImpl> mInputConsumers = new ArrayList<>();

    /**
     * Set when recents (overview) is active as part of a shell transition. While set, any focus
@@ -164,31 +165,35 @@ final class InputMonitor {
        mDisplayRemoved = true;
    }

    private void addInputConsumer(String name, InputConsumerImpl consumer) {
        mInputConsumers.put(name, consumer);
    private void addInputConsumer(InputConsumerImpl consumer) {
        mInputConsumers.add(consumer);
        consumer.linkToDeathRecipient();
        consumer.layout(mInputTransaction, mDisplayWidth, mDisplayHeight);
        updateInputWindowsLw(true /* force */);
    }

    boolean destroyInputConsumer(String name) {
        if (disposeInputConsumer(mInputConsumers.remove(name))) {
    boolean destroyInputConsumer(IBinder token) {
        for (int i = 0; i < mInputConsumers.size(); i++) {
            final InputConsumerImpl consumer = mInputConsumers.get(i);
            if (consumer != null && consumer.mToken == token) {
                consumer.disposeChannelsLw(mInputTransaction);
                mInputConsumers.remove(consumer);
                updateInputWindowsLw(true /* force */);
                return true;
            }
        return false;
    }

    private boolean disposeInputConsumer(InputConsumerImpl consumer) {
        if (consumer != null) {
            consumer.disposeChannelsLw(mInputTransaction);
            return true;
        }
        return false;
    }

    InputConsumerImpl getInputConsumer(String name) {
        return mInputConsumers.get(name);
        // Search in reverse order as the latest input consumer with the name takes precedence
        for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
            final InputConsumerImpl consumer = mInputConsumers.get(i);
            if (consumer.mName.equals(name)) {
                return consumer;
            }
        }
        return null;
    }

    void layoutInputConsumers(int dw, int dh) {
@@ -200,7 +205,7 @@ final class InputMonitor {
        try {
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "layoutInputConsumer");
            for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
                mInputConsumers.valueAt(i).layout(mInputTransaction, dw, dh);
                mInputConsumers.get(i).layout(mInputTransaction, dw, dh);
            }
        } finally {
            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
@@ -212,15 +217,16 @@ final class InputMonitor {
    // (set so by this function) and must meet some condition for visibility on each update.
    void resetInputConsumers(SurfaceControl.Transaction t) {
        for (int i = mInputConsumers.size() - 1; i >= 0; i--) {
            mInputConsumers.valueAt(i).hide(t);
            mInputConsumers.get(i).hide(t);
        }
    }

    void createInputConsumer(IBinder token, String name, InputChannel inputChannel, int clientPid,
            UserHandle clientUser) {
        if (mInputConsumers.containsKey(name)) {
        final InputConsumerImpl existingConsumer = getInputConsumer(name);
        if (existingConsumer != null && existingConsumer.mClientUser.equals(clientUser)) {
            throw new IllegalStateException("Existing input consumer found with name: " + name
                    + ", display: " + mDisplayId);
                    + ", display: " + mDisplayId + ", user: " + clientUser);
        }

        final InputConsumerImpl consumer = new InputConsumerImpl(mService, token, name,
@@ -239,7 +245,7 @@ final class InputMonitor {
                throw new IllegalArgumentException("Illegal input consumer : " + name
                        + ", display: " + mDisplayId);
        }
        addInputConsumer(name, consumer);
        addInputConsumer(consumer);
    }

    @VisibleForTesting
@@ -541,11 +547,11 @@ final class InputMonitor {
    }

    void dump(PrintWriter pw, String prefix) {
        final Set<String> inputConsumerKeys = mInputConsumers.keySet();
        if (!inputConsumerKeys.isEmpty()) {
        if (!mInputConsumers.isEmpty()) {
            pw.println(prefix + "InputConsumers:");
            for (String key : inputConsumerKeys) {
                mInputConsumers.get(key).dump(pw, key, prefix);
            for (int i = 0; i < mInputConsumers.size(); i++) {
                final InputConsumerImpl consumer = mInputConsumers.get(i);
                consumer.dump(pw, consumer.mName, prefix);
            }
        }
    }
Loading