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

Commit a484baa8 authored by Cosmin Băieș's avatar Cosmin Băieș
Browse files

Make ImeTrackerService pending check asynchronous

This modifies hasPendingImeVisibilityRequests from the ImeTrackerService
from a blocking binder call that returns a boolean, to an asynchronous
binder call that reports back to the caller using an AndroidFuture. This
method is only used in tests, which can now replace the existing
PollingCheck on a blocking call with a single call that signals as soon
as its done. This change allows marking the IImeTracker interface as a
whole oneway.

Flag: EXEMPT bugfix
Test: atest InputMethodStatsTest
  MultiDisplayImeTests#testFallbackImmMaintainsParameters
Bug: 342111149
Change-Id: I51bdfec2965d8b01f181d6505e3df9dbb95e7190
parent 9b50b2b2
Loading
Loading
Loading
Loading
+2 −2
Original line number Original line Diff line number Diff line
@@ -4246,19 +4246,19 @@ package android.view.inputmethod {


  public final class InputMethodManager {
  public final class InputMethodManager {
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void addVirtualStylusIdForTestSession();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void addVirtualStylusIdForTestSession();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void finishTrackingPendingImeVisibilityRequests();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void finishTrackingPendingRequests();
    method public int getDisplayId();
    method public int getDisplayId();
    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull android.os.UserHandle);
    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getEnabledInputMethodListAsUser(@NonNull android.os.UserHandle);
    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(@NonNull String, boolean, @NonNull android.os.UserHandle);
    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public java.util.List<android.view.inputmethod.InputMethodInfo> getInputMethodListAsUser(int);
    method public boolean hasActiveInputConnection(@Nullable android.view.View);
    method public boolean hasActiveInputConnection(@Nullable android.view.View);
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean hasPendingImeVisibilityRequests();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void hideSoftInputFromServerForTest();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void hideSoftInputFromServerForTest();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View);
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isCurrentRootView(@NonNull android.view.View);
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean isInputMethodPickerShown();
    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
    method @NonNull @RequiresPermission(value=android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional=true) public boolean isStylusHandwritingAvailableAsUser(@NonNull android.os.UserHandle);
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void setStylusWindowIdleTimeoutForTest(long);
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void setStylusWindowIdleTimeoutForTest(long);
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean shouldShowImeSwitcherButtonForTest();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public boolean shouldShowImeSwitcherButtonForTest();
    method @RequiresPermission(android.Manifest.permission.TEST_INPUT_METHOD) public void waitUntilNoPendingRequests(long);
  }
  }


  public final class InsertModeGesture extends android.view.inputmethod.CancellableHandwritingGesture implements android.os.Parcelable {
  public final class InsertModeGesture extends android.view.inputmethod.CancellableHandwritingGesture implements android.os.Parcelable {
+12 −8
Original line number Original line Diff line number Diff line
@@ -712,32 +712,36 @@ final class IInputMethodManagerGlobalInvoker {
        }
        }
    }
    }


    /** @see com.android.server.inputmethod.ImeTrackerService#hasPendingImeVisibilityRequests */
    /** @see com.android.server.inputmethod.ImeTrackerService#waitUntilNoPendingRequests */
    @AnyThread
    @AnyThread
    @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
    @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
    static boolean hasPendingImeVisibilityRequests() {
    static void waitUntilNoPendingRequests(long timeoutMs) {
        final var service = getImeTrackerService();
        final var service = getImeTrackerService();
        if (service == null) {
        if (service == null) {
            return true;
            return;
        }
        }
        try {
        try {
            return service.hasPendingImeVisibilityRequests();
            final var future = new AndroidFuture<Void>();
            service.waitUntilNoPendingRequests(future, timeoutMs);
            future.get(timeoutMs, TimeUnit.MILLISECONDS);
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            throw e.rethrowFromSystemServer();
        } catch (Exception e) {
            throw ExceptionUtils.propagate(e);
        }
        }
    }
    }


    @AnyThread
    @AnyThread
    @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
    @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
    static void finishTrackingPendingImeVisibilityRequests() {
    static void finishTrackingPendingRequests() {
        final var service = getImeTrackerService();
        final var service = getImeTrackerService();
        if (service == null) {
        if (service == null) {
            return;
            return;
        }
        }
        try {
        try {
            final var completionSignal = new AndroidFuture<Void>();
            final var future = new AndroidFuture<Void>();
            service.finishTrackingPendingImeVisibilityRequests(completionSignal);
            service.finishTrackingPendingRequests(future);
            completionSignal.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
            future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
        } catch (RemoteException e) {
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            throw e.rethrowFromSystemServer();
        } catch (Exception e) {
        } catch (Exception e) {
+10 −6
Original line number Original line Diff line number Diff line
@@ -4611,15 +4611,19 @@ public final class InputMethodManager {
    }
    }


    /**
    /**
     * A test API for CTS to check whether there are any pending IME visibility requests.
     * A test API for CTS to wait until there are no more pending IME visibility requests, up to the
     * given timeout. This will throw a {@link java.util.concurrent.TimeoutException} if the wait
     * times out.
     *
     * @param timeoutMs the timeout in milliseconds.
     *
     *
     * @return {@code true} iff there are pending IME visibility requests.
     * @hide
     * @hide
     */
     */
    @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
    @TestApi
    @TestApi
    @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
    @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
    public boolean hasPendingImeVisibilityRequests() {
    public void waitUntilNoPendingRequests(long timeoutMs) {
        return IInputMethodManagerGlobalInvoker.hasPendingImeVisibilityRequests();
        IInputMethodManagerGlobalInvoker.waitUntilNoPendingRequests(timeoutMs);
    }
    }


    /**
    /**
@@ -4631,8 +4635,8 @@ public final class InputMethodManager {
    @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
    @SuppressLint("UnflaggedApi") // @TestApi without associated feature.
    @TestApi
    @TestApi
    @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
    @RequiresPermission(Manifest.permission.TEST_INPUT_METHOD)
    public void finishTrackingPendingImeVisibilityRequests() {
    public void finishTrackingPendingRequests() {
        IInputMethodManagerGlobalInvoker.finishTrackingPendingImeVisibilityRequests();
        IInputMethodManagerGlobalInvoker.finishTrackingPendingRequests();
    }
    }


    /**
    /**
+17 −16
Original line number Original line Diff line number Diff line
@@ -24,7 +24,7 @@ import com.android.internal.infra.AndroidFuture;
 * Interface to the global IME tracker service, used by all client applications.
 * Interface to the global IME tracker service, used by all client applications.
 * {@hide}
 * {@hide}
 */
 */
interface IImeTracker {
oneway interface IImeTracker {


    /**
    /**
     * Called when an IME request is started.
     * Called when an IME request is started.
@@ -37,7 +37,7 @@ interface IImeTracker {
     * @param fromUser whether this request was created directly from user interaction.
     * @param fromUser whether this request was created directly from user interaction.
     * @param startTime the time in milliseconds when the request was started.
     * @param startTime the time in milliseconds when the request was started.
     */
     */
    oneway void onStart(in ImeTracker.Token statsToken, int uid, int type, int origin, int reason,
    void onStart(in ImeTracker.Token statsToken, int uid, int type, int origin, int reason,
        boolean fromUser, long startTime);
        boolean fromUser, long startTime);


    /**
    /**
@@ -46,7 +46,7 @@ interface IImeTracker {
     * @param statsToken the token tracking the request.
     * @param statsToken the token tracking the request.
     * @param phase the new phase the request reached.
     * @param phase the new phase the request reached.
     */
     */
    oneway void onProgress(in ImeTracker.Token statsToken, int phase);
    void onProgress(in ImeTracker.Token statsToken, int phase);


    /**
    /**
     * Called when the IME request fails.
     * Called when the IME request fails.
@@ -54,7 +54,7 @@ interface IImeTracker {
     * @param statsToken the token tracking the request.
     * @param statsToken the token tracking the request.
     * @param phase the phase the request failed at.
     * @param phase the phase the request failed at.
     */
     */
    oneway void onFailed(in ImeTracker.Token statsToken, int phase);
    void onFailed(in ImeTracker.Token statsToken, int phase);


    /**
    /**
     * Called when the IME request is cancelled.
     * Called when the IME request is cancelled.
@@ -62,21 +62,21 @@ interface IImeTracker {
     * @param statsToken the token tracking the request.
     * @param statsToken the token tracking the request.
     * @param phase the phase the request was cancelled at.
     * @param phase the phase the request was cancelled at.
     */
     */
    oneway void onCancelled(in ImeTracker.Token statsToken, int phase);
    void onCancelled(in ImeTracker.Token statsToken, int phase);


    /**
    /**
     * Called when the show IME request is successful.
     * Called when the show IME request is successful.
     *
     *
     * @param statsToken the token tracking the request.
     * @param statsToken the token tracking the request.
     */
     */
    oneway void onShown(in ImeTracker.Token statsToken);
    void onShown(in ImeTracker.Token statsToken);


    /**
    /**
     * Called when the hide IME request is successful.
     * Called when the hide IME request is successful.
     *
     *
     * @param statsToken the token tracking the request.
     * @param statsToken the token tracking the request.
     */
     */
    oneway void onHidden(in ImeTracker.Token statsToken);
    void onHidden(in ImeTracker.Token statsToken);


    /**
    /**
     * Called when the user-controlled IME request was dispatched to the requesting app. The
     * Called when the user-controlled IME request was dispatched to the requesting app. The
@@ -84,27 +84,28 @@ interface IImeTracker {
     *
     *
     * @param statsToken the token tracking the request.
     * @param statsToken the token tracking the request.
     */
     */
    oneway void onDispatched(in ImeTracker.Token statsToken);
    void onDispatched(in ImeTracker.Token statsToken);


    /**
    /**
     * Checks whether there are any pending IME visibility requests.
     * Waits until there are no more pending IME visibility requests, up to a given timeout, and
     * notifies the given future.
     *
     *
     * @return {@code true} iff there are pending IME visibility requests.
     * @param future    the future to notify.
     * @param timeoutMs the timeout in milliseconds.
     */
     */
    @EnforcePermission("TEST_INPUT_METHOD")
    @EnforcePermission("TEST_INPUT_METHOD")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
            + "android.Manifest.permission.TEST_INPUT_METHOD)")
            + "android.Manifest.permission.TEST_INPUT_METHOD)")
    boolean hasPendingImeVisibilityRequests();
    void waitUntilNoPendingRequests(in AndroidFuture<void> future, long timeoutMs);


    /**
    /**
     * Finishes the tracking of any pending IME visibility requests. This won't stop the actual
     * Finishes the tracking of any pending IME visibility requests and notifies the given future.
     * requests, but allows resetting the state when starting up test runs.
     * This won't stop the actual requests, but allows resetting the state when starting test runs.
     *
     *
     * @param completionSignal used to signal when the tracking has been finished.
     * @param future the future to notify.
     */
     */
    @EnforcePermission("TEST_INPUT_METHOD")
    @EnforcePermission("TEST_INPUT_METHOD")
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
            + "android.Manifest.permission.TEST_INPUT_METHOD)")
            + "android.Manifest.permission.TEST_INPUT_METHOD)")
    oneway void finishTrackingPendingImeVisibilityRequests(
    void finishTrackingPendingRequests(in AndroidFuture<void> future);
        in AndroidFuture completionSignal /* T=Void */);
}
}
+29 −10
Original line number Original line Diff line number Diff line
@@ -41,11 +41,13 @@ import java.io.PrintWriter;
import java.time.Instant;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Locale;
import java.util.Map;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicInteger;


/**
/**
@@ -77,6 +79,10 @@ public final class ImeTrackerService extends IImeTracker.Stub {
    /** Recorder for metrics data from entries. */
    /** Recorder for metrics data from entries. */
    private final MetricsRecorder mMetricsRecorder;
    private final MetricsRecorder mMetricsRecorder;


    /** Collection of listeners waiting until there are no more pending requests. */
    @GuardedBy("mLock")
    private final ArrayList<AndroidFuture<Void>> mPendingRequestsListeners = new ArrayList<>();

    /** Interface for recording metrics data from entries. */
    /** Interface for recording metrics data from entries. */
    @FunctionalInterface
    @FunctionalInterface
    interface MetricsRecorder {
    interface MetricsRecorder {
@@ -334,27 +340,32 @@ public final class ImeTrackerService extends IImeTracker.Stub {


    @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
    @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
    @Override
    @Override
    public boolean hasPendingImeVisibilityRequests() {
    public void waitUntilNoPendingRequests(@NonNull AndroidFuture<Void> future, long timeoutMs) {
        super.hasPendingImeVisibilityRequests_enforcePermission();
        super.waitUntilNoPendingRequests_enforcePermission();
        synchronized (mLock) {
        synchronized (mLock) {
            return !mHistory.activeEntries().isEmpty();
            if (mHistory.activeEntries().isEmpty()) {
                future.complete(null);
            } else {
                mPendingRequestsListeners.add(future);
                mHandler.postDelayed(() -> {
                    future.completeExceptionally(new TimeoutException());
                    mPendingRequestsListeners.remove(future);
                }, future, timeoutMs);
            }
        }
        }
    }
    }


    @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
    @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
    @Override
    @Override
    public void finishTrackingPendingImeVisibilityRequests(
    public void finishTrackingPendingRequests(@NonNull AndroidFuture<Void> future) {
            @NonNull AndroidFuture completionSignal /* T=Void */) {
        super.finishTrackingPendingRequests_enforcePermission();
        super.finishTrackingPendingImeVisibilityRequests_enforcePermission();
        @SuppressWarnings("unchecked") final AndroidFuture<Void> typedCompletionSignal =
                completionSignal;
        try {
        try {
            synchronized (mLock) {
            synchronized (mLock) {
                mHistory.activeEntries().clear();
                mHistory.activeEntries().clear();
            }
            }
            typedCompletionSignal.complete(null);
            future.complete(null);
        } catch (Throwable e) {
        } catch (Throwable e) {
            typedCompletionSignal.completeExceptionally(e);
            future.completeExceptionally(e);
        }
        }
    }
    }


@@ -371,6 +382,14 @@ public final class ImeTrackerService extends IImeTracker.Stub {
        mHandler.removeCallbacksAndMessages(entry /* token */);
        mHandler.removeCallbacksAndMessages(entry /* token */);
        mHistory.complete(id, entry);
        mHistory.complete(id, entry);
        mMetricsRecorder.record(entry);
        mMetricsRecorder.record(entry);
        if (!mPendingRequestsListeners.isEmpty() && mHistory.mActiveEntries.isEmpty()) {
            for (int i = 0; i < mPendingRequestsListeners.size(); i++) {
                final var listener = mPendingRequestsListeners.get(i);
                listener.complete(null);
                mHandler.removeCallbacksAndMessages(listener);
            }
            mPendingRequestsListeners.clear();
        }
    }
    }


    /**
    /**