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

Commit a95881f6 authored by Hai Zhang's avatar Hai Zhang
Browse files

Use API for binding service with custom user and executor.

There is actually already a public API overload for
Context.bindService() which allows binding the service with the user
of Context.getUser() and a custom Executor.

We can construct an Executor from our Handler, but this executor is
required to remain the same instance for different calls to
bindService() for the same ServiceConnection by the API, so we should
keep the Executor instance, as well as the Handler instance, instead
of calling getJobHandler() every time. In fact getJobHandler() is
already required to return the same instance, or bindServiceAsUser()
would have thrown the same exception for different Handlers.

Also removed a check in ContextImpl.bindService() to allow mainline
modules to use the bindService() API with Context multi-user.

Bug: 158736025
Test: presubmit
Change-Id: I95717a4a74267dff4fbe1898a82cdfa7f195cc06
parent bb45c563
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -1795,7 +1795,6 @@ class ContextImpl extends Context {
    @Override
    public boolean bindService(
            Intent service, int flags, Executor executor, ServiceConnection conn) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, null, null, executor, getUser());
    }

+19 −15
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Looper;
@@ -40,6 +41,7 @@ import java.util.Objects;
import java.util.Queue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
import java.util.function.Function;
@@ -220,11 +222,13 @@ public interface ServiceConnector<I extends IInterface> {
        private final @NonNull ServiceConnection mServiceConnection = this;
        private final @NonNull Runnable mTimeoutDisconnect = this;

        // This context contains the user information.
        protected final @NonNull Context mContext;
        private final @NonNull Intent mIntent;
        private final int mBindingFlags;
        private final int mUserId;
        private final @Nullable Function<IBinder, I> mBinderAsInterface;
        private final @NonNull Handler mHandler;
        private final @NonNull Executor mExecutor;

        private volatile I mService = null;
        private boolean mBinding = false;
@@ -249,11 +253,13 @@ public interface ServiceConnector<I extends IInterface> {
         */
        public Impl(@NonNull Context context, @NonNull Intent intent, int bindingFlags,
                @UserIdInt int userId, @Nullable Function<IBinder, I> binderAsInterface) {
            mContext = context;
            mContext = context.createContextAsUser(UserHandle.of(userId), 0);
            mIntent = intent;
            mBindingFlags = bindingFlags;
            mUserId = userId;
            mBinderAsInterface = binderAsInterface;

            mHandler = getJobHandler();
            mExecutor = new HandlerExecutor(mHandler);
        }

        /**
@@ -292,14 +298,12 @@ public interface ServiceConnector<I extends IInterface> {
         * <p>
         * If overridden, implementation must use at least the provided {@link ServiceConnection}
         */
        protected boolean bindService(
                @NonNull ServiceConnection serviceConnection, @NonNull Handler handler) {
        protected boolean bindService(@NonNull ServiceConnection serviceConnection) {
            if (DEBUG) {
                logTrace();
            }
            return mContext.bindServiceAsUser(mIntent, serviceConnection,
                    Context.BIND_AUTO_CREATE | mBindingFlags,
                    handler, UserHandle.of(mUserId));
            return mContext.bindService(mIntent, Context.BIND_AUTO_CREATE | mBindingFlags,
                    mExecutor, serviceConnection);
        }

        /**
@@ -381,13 +385,13 @@ public interface ServiceConnector<I extends IInterface> {
            if (!enqueue((Job<I, ?>) task)) {
                task.completeExceptionally(new IllegalStateException(
                        "Failed to post a job to handler. Likely "
                                + getJobHandler().getLooper() + " is exiting"));
                                + mHandler.getLooper() + " is exiting"));
            }
        }

        private boolean enqueue(@NonNull Job<I, ?> job) {
            cancelTimeout();
            return getJobHandler().post(() -> enqueueJobThread(job));
            return mHandler.post(() -> enqueueJobThread(job));
        }

        void enqueueJobThread(@NonNull Job<I, ?> job) {
@@ -404,7 +408,7 @@ public interface ServiceConnector<I extends IInterface> {
            } else if (isBound()) {
                processQueue();
            } else if (!mBinding) {
                if (bindService(mServiceConnection, getJobHandler())) {
                if (bindService(mServiceConnection)) {
                    mBinding = true;
                } else {
                    completeExceptionally(job,
@@ -497,7 +501,7 @@ public interface ServiceConnector<I extends IInterface> {
                logTrace();
            }
            mUnbinding = true;
            getJobHandler().post(this::unbindJobThread);
            mHandler.post(this::unbindJobThread);
        }

        void unbindJobThread() {
@@ -606,7 +610,7 @@ public interface ServiceConnector<I extends IInterface> {
        public String toString() {
            StringBuilder sb = new StringBuilder("ServiceConnector@")
                    .append(System.identityHashCode(this) % 1000).append("(")
                    .append(mIntent).append(", user: ").append(mUserId)
                    .append(mIntent).append(", user: ").append(mContext.getUser().getIdentifier())
                    .append(")[").append(stateToString());
            if (!mQueue.isEmpty()) {
                sb.append(", ").append(mQueue.size()).append(" pending job(s)");
@@ -624,8 +628,8 @@ public interface ServiceConnector<I extends IInterface> {
            String tab = "  ";
            pw.append(prefix).append("ServiceConnector:").println();
            pw.append(prefix).append(tab).append(String.valueOf(mIntent)).println();
            pw.append(prefix).append(tab)
                    .append("userId: ").append(String.valueOf(mUserId)).println();
            pw.append(prefix).append(tab).append("userId: ")
                    .append(String.valueOf(mContext.getUser().getIdentifier())).println();
            pw.append(prefix).append(tab)
                    .append("State: ").append(stateToString()).println();
            pw.append(prefix).append(tab)