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

Commit fd64b280 authored by Wilson Wu's avatar Wilson Wu
Browse files

Make IInputMethodManager to oneway (1/N)

Isolate the Completable class from the CancellationGroup
class because not all Completable objects could be cancelled.
So we don’t need to always hold a final CancellationGroup in
Completable class. If the Completable object could be cancelled,
pass it as parameter when doing await.

Bug: 163453493
Test: Manual test with keyboard
Test: atest CtsInputMethodTestCases
Change-Id: If79810d5acc36b5a8c6a2c7cfcabeb4d0ebed197
parent c44ef9e2
Loading
Loading
Loading
Loading
+4 −271
Original line number Diff line number Diff line
@@ -24,11 +24,10 @@ import com.android.internal.annotations.GuardedBy;

import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * A utility class, which works as both a factory class of completable objects and a cancellation
 * signal to cancel all the completable objects created by this object.
 * A utility class, which works as both a factory class of a cancellation signal to cancel
 * all the completable objects.
 */
public final class CancellationGroup {
    private final Object mLock = new Object();
@@ -46,274 +45,8 @@ public final class CancellationGroup {
    @GuardedBy("mLock")
    private boolean mCanceled = false;

    /**
     * An inner class to consolidate completable object types supported by
     * {@link CancellationGroup}.
     */
    public static final class Completable {

        /**
         * Not intended to be instantiated.
         */
        private Completable() {
        }

        /**
         * Base class of all the completable types supported by {@link CancellationGroup}.
         */
        protected static class ValueBase {
            /**
             * {@link CountDownLatch} to be signaled to unblock {@link #await(int, TimeUnit)}.
             */
            private final CountDownLatch mLatch = new CountDownLatch(1);

            /**
             * {@link CancellationGroup} to which this completable object belongs.
             */
            @NonNull
            private final CancellationGroup mParentGroup;

            /**
             * Lock {@link Object} to guard complete operations within this class.
             */
            protected final Object mValueLock = new Object();

            /**
             * {@code true} after {@link #onComplete()} gets called.
             */
            @GuardedBy("mValueLock")
            protected boolean mHasValue = false;

            /**
             * Base constructor.
             *
             * @param parentGroup {@link CancellationGroup} to which this completable object
             *                    belongs.
             */
            protected ValueBase(@NonNull CancellationGroup parentGroup) {
                mParentGroup = parentGroup;
            }

            /**
             * @return {@link true} if {@link #onComplete()} gets called already.
             */
            @AnyThread
            public boolean hasValue() {
                synchronized (mValueLock) {
                    return mHasValue;
                }
            }

            /**
             * Called by subclasses to signale {@link #mLatch}.
             */
            @AnyThread
            protected void onComplete() {
                mLatch.countDown();
            }

            /**
             * Blocks the calling thread until at least one of the following conditions is met.
             *
             * <p>
             *     <ol>
             *         <li>This object becomes ready to return the value.</li>
             *         <li>{@link CancellationGroup#cancelAll()} gets called.</li>
             *         <li>The given timeout period has passed.</li>
             *     </ol>
             * </p>
             *
             * <p>The caller can distinguish the case 1 and case 2 by calling {@link #hasValue()}.
             * Note that the return value of {@link #hasValue()} can change from {@code false} to
             * {@code true} at any time, even after this methods finishes with returning
             * {@code true}.</p>
             *
             * @param timeout length of the timeout.
             * @param timeUnit unit of {@code timeout}.
             * @return {@code false} if and only if the given timeout period has passed. Otherwise
             *         {@code true}.
             */
            @AnyThread
            public boolean await(int timeout, @NonNull TimeUnit timeUnit) {
                if (!mParentGroup.registerLatch(mLatch)) {
                    // Already canceled when this method gets called.
                    return false;
                }
                try {
                    return mLatch.await(timeout, timeUnit);
                } catch (InterruptedException e) {
                    return true;
                } finally {
                    mParentGroup.unregisterLatch(mLatch);
                }
            }
        }

        /**
         * Completable object of integer primitive.
         */
        public static final class Int extends ValueBase {
            @GuardedBy("mValueLock")
            private int mValue = 0;

            private Int(@NonNull CancellationGroup factory) {
                super(factory);
            }

            /**
             * Notify when a value is set to this completable object.
             *
             * @param value value to be set.
             */
            @AnyThread
            void onComplete(int value) {
                synchronized (mValueLock) {
                    if (mHasValue) {
                        throw new UnsupportedOperationException(
                                "onComplete() cannot be called multiple times");
                    }
                    mValue = value;
                    mHasValue = true;
                }
                onComplete();
            }

            /**
             * @return value associated with this object.
             * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
             *                                       {@code false}.
             */
            @AnyThread
            public int getValue() {
                synchronized (mValueLock) {
                    if (!mHasValue) {
                        throw new UnsupportedOperationException(
                                "getValue() is allowed only if hasValue() returns true");
                    }
                    return mValue;
                }
            }
        }

        /**
         * Base class of completable object types.
         *
         * @param <T> type associated with this completable object.
         */
        public static class Values<T> extends ValueBase {
            @GuardedBy("mValueLock")
            @Nullable
            private T mValue = null;

            protected Values(@NonNull CancellationGroup factory) {
                super(factory);
            }

            /**
             * Notify when a value is set to this completable value object.
             *
             * @param value value to be set.
             */
            @AnyThread
            void onComplete(@Nullable T value) {
                synchronized (mValueLock) {
                    if (mHasValue) {
                        throw new UnsupportedOperationException(
                                "onComplete() cannot be called multiple times");
                    }
                    mValue = value;
                    mHasValue = true;
                }
                onComplete();
            }

            /**
             * @return value associated with this object.
             * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
             *                                       {@code false}.
             */
            @AnyThread
            @Nullable
            public T getValue() {
                synchronized (mValueLock) {
                    if (!mHasValue) {
                        throw new UnsupportedOperationException(
                                "getValue() is allowed only if hasValue() returns true");
                    }
                    return mValue;
                }
            }
        }

        /**
         * Completable object of {@link java.lang.CharSequence}.
         */
        public static final class CharSequence extends Values<java.lang.CharSequence> {
            private CharSequence(@NonNull CancellationGroup factory) {
                super(factory);
            }
        }

        /**
         * Completable object of {@link android.view.inputmethod.ExtractedText}.
         */
        public static final class ExtractedText
                extends Values<android.view.inputmethod.ExtractedText> {
            private ExtractedText(@NonNull CancellationGroup factory) {
                super(factory);
            }
        }

        /**
         * Completable object of {@link android.view.inputmethod.SurroundingText}.
         */
        public static final class SurroundingText
                extends Values<android.view.inputmethod.SurroundingText> {
            private SurroundingText(@NonNull CancellationGroup factory) {
                super(factory);
            }
        }
    }

    /**
     * @return an instance of {@link Completable.Int} that is associated with this
     *         {@link CancellationGroup}.
     */
    @AnyThread
    public Completable.Int createCompletableInt() {
        return new Completable.Int(this);
    }

    /**
     * @return an instance of {@link Completable.CharSequence} that is associated with this
     *         {@link CancellationGroup}.
     */
    @AnyThread
    public Completable.CharSequence createCompletableCharSequence() {
        return new Completable.CharSequence(this);
    }

    /**
     * @return an instance of {@link Completable.ExtractedText} that is associated with this
     *         {@link CancellationGroup}.
     */
    @AnyThread
    public Completable.ExtractedText createCompletableExtractedText() {
        return new Completable.ExtractedText(this);
    }

    /**
     * @return an instance of {@link Completable.SurroundingText} that is associated with this
     *         {@link CancellationGroup}.
     */
    @AnyThread
    public Completable.SurroundingText createCompletableSurroundingText() {
        return new Completable.SurroundingText(this);
    }


    @AnyThread
    private boolean registerLatch(@NonNull CountDownLatch latch) {
    boolean registerLatch(@NonNull CountDownLatch latch) {
        synchronized (mLock) {
            if (mCanceled) {
                return false;
@@ -329,7 +62,7 @@ public final class CancellationGroup {
    }

    @AnyThread
    private void unregisterLatch(@NonNull CountDownLatch latch) {
    void unregisterLatch(@NonNull CountDownLatch latch) {
        synchronized (mLock) {
            if (mLatchList != null) {
                mLatchList.remove(latch);
+278 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.internal.inputmethod;

import android.annotation.AnyThread;
import android.annotation.NonNull;
import android.annotation.Nullable;

import com.android.internal.annotations.GuardedBy;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * An class to consolidate completable object types supported by
 * {@link CancellationGroup}.
 */
public final class Completable {

    /**
     * Not intended to be instantiated.
     */
    private Completable() {
    }

    /**
     * Base class of all the completable types supported by {@link CancellationGroup}.
     */
    protected static class ValueBase {
        /**
         * {@link CountDownLatch} to be signaled to unblock
         * {@link #await(int, TimeUnit, CancellationGroup)}.
         */
        private final CountDownLatch mLatch = new CountDownLatch(1);

        /**
         * Lock {@link Object} to guard complete operations within this class.
         */
        protected final Object mValueLock = new Object();

        /**
         * {@code true} after {@link #onComplete()} gets called.
         */
        @GuardedBy("mValueLock")
        protected boolean mHasValue = false;

        /**
         * @return {@link true} if {@link #onComplete()} gets called already.
         */
        @AnyThread
        public boolean hasValue() {
            synchronized (mValueLock) {
                return mHasValue;
            }
        }

        /**
         * Called by subclasses to signale {@link #mLatch}.
         */
        @AnyThread
        protected void onComplete() {
            mLatch.countDown();
        }

        /**
         * Blocks the calling thread until at least one of the following conditions is met.
         *
         * <p>
         *     <ol>
         *         <li>This object becomes ready to return the value.</li>
         *         <li>{@link CancellationGroup#cancelAll()} gets called.</li>
         *         <li>The given timeout period has passed.</li>
         *     </ol>
         * </p>
         *
         * <p>The caller can distinguish the case 1 and case 2 by calling {@link #hasValue()}.
         * Note that the return value of {@link #hasValue()} can change from {@code false} to
         * {@code true} at any time, even after this methods finishes with returning
         * {@code true}.</p>
         *
         * @param timeout length of the timeout.
         * @param timeUnit unit of {@code timeout}.
         * @param cancellationGroup {@link CancellationGroup} to cancel completable objects.
         * @return {@code false} if and only if the given timeout period has passed. Otherwise
         *         {@code true}.
         */
        @AnyThread
        public boolean await(int timeout, @NonNull TimeUnit timeUnit,
                @Nullable CancellationGroup cancellationGroup) {
            if (cancellationGroup == null) {
                return awaitInner(timeout, timeUnit);
            }

            if (!cancellationGroup.registerLatch(mLatch)) {
                // Already canceled when this method gets called.
                return false;
            }
            try {
                return awaitInner(timeout, timeUnit);
            } finally {
                cancellationGroup.unregisterLatch(mLatch);
            }
        }

        private boolean awaitInner(int timeout, @NonNull TimeUnit timeUnit) {
            try {
                return mLatch.await(timeout, timeUnit);
            } catch (InterruptedException e) {
                return true;
            }
        }
    }

    /**
     * Completable object of integer primitive.
     */
    public static final class Int extends ValueBase {
        @GuardedBy("mValueLock")
        private int mValue = 0;

        /**
         * Notify when a value is set to this completable object.
         *
         * @param value value to be set.
         */
        @AnyThread
        void onComplete(int value) {
            synchronized (mValueLock) {
                if (mHasValue) {
                    throw new UnsupportedOperationException(
                            "onComplete() cannot be called multiple times");
                }
                mValue = value;
                mHasValue = true;
            }
            onComplete();
        }

        /**
         * @return value associated with this object.
         * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
         *                                       {@code false}.
         */
        @AnyThread
        public int getValue() {
            synchronized (mValueLock) {
                if (!mHasValue) {
                    throw new UnsupportedOperationException(
                            "getValue() is allowed only if hasValue() returns true");
                }
                return mValue;
            }
        }
    }

    /**
     * Base class of completable object types.
     *
     * @param <T> type associated with this completable object.
     */
    public static class Values<T> extends ValueBase {
        @GuardedBy("mValueLock")
        @Nullable
        private T mValue = null;

        /**
         * Notify when a value is set to this completable value object.
         *
         * @param value value to be set.
         */
        @AnyThread
        void onComplete(@Nullable T value) {
            synchronized (mValueLock) {
                if (mHasValue) {
                    throw new UnsupportedOperationException(
                            "onComplete() cannot be called multiple times");
                }
                mValue = value;
                mHasValue = true;
            }
            onComplete();
        }

        /**
         * @return value associated with this object.
         * @throws UnsupportedOperationException when called while {@link #hasValue()} returns
         *                                       {@code false}.
         */
        @AnyThread
        @Nullable
        public T getValue() {
            synchronized (mValueLock) {
                if (!mHasValue) {
                    throw new UnsupportedOperationException(
                            "getValue() is allowed only if hasValue() returns true");
                }
                return mValue;
            }
        }
    }

    /**
     * @return an instance of {@link Completable.Int}.
     */
    public static Completable.Int createInt() {
        return new Completable.Int();
    }

    /**
     * @return an instance of {@link Completable.Boolean}.
     */
    public static Completable.Boolean createBoolean() {
        return new Completable.Boolean();
    }

    /**
     * @return an instance of {@link Completable.CharSequence}.
     */
    public static Completable.CharSequence createCharSequence() {
        return new Completable.CharSequence();
    }

    /**
     * @return an instance of {@link Completable.ExtractedText}.
     */
    public static Completable.ExtractedText createExtractedText() {
        return new Completable.ExtractedText();
    }

    /**
     * @return an instance of {@link Completable.SurroundingText}.
     */
    public static Completable.SurroundingText createSurroundingText() {
        return new Completable.SurroundingText();
    }

    /**
     * Completable object of {@link java.lang.Boolean}.
     */
    public static final class Boolean extends Values<java.lang.Boolean> { }

    /**
     * Completable object of {@link java.lang.CharSequence}.
     */
    public static final class CharSequence extends Values<java.lang.CharSequence> { }

    /**
     * Completable object of {@link android.view.inputmethod.ExtractedText}.
     */
    public static final class ExtractedText
            extends Values<android.view.inputmethod.ExtractedText> { }

    /**
     * Completable object of {@link android.view.inputmethod.SurroundingText}.
     */
    public static final class SurroundingText
            extends Values<android.view.inputmethod.SurroundingText> { }

    /**
     * Completable object of {@link com.android.internal.view.InputBindResult}.
     */
    public static final class InputBindResult
            extends Values<com.android.internal.view.InputBindResult> { }
}
+22 −25

File changed.

Preview size limit exceeded, changes collapsed.

+25 −28

File changed.

Preview size limit exceeded, changes collapsed.