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

Commit 3ee9e923 authored by Eugene Susla's avatar Eugene Susla
Browse files

Migrate RoleControllerManager to ServiceConnector

Saves some boilerplate and ensures callbacks always get called

Test: atest CtsRoleTestCases
Change-Id: Ieab680743cfb358a78a873d795ceac2b46cab483
parent 4549c56d
Loading
Loading
Loading
Loading
+98 −402
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@ import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.annotation.UserIdInt;
import android.app.ActivityThread;
import android.content.ComponentName;
import android.content.Context;
@@ -29,18 +28,18 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.util.Log;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.infra.AbstractRemoteService;
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;

import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;

/**
@@ -53,6 +52,8 @@ public class RoleControllerManager {

    private static final String LOG_TAG = RoleControllerManager.class.getSimpleName();

    private static final long REQUEST_TIMEOUT_MILLIS = 15 * 1000;

    private static volatile ComponentName sRemoteServiceComponentName;

    private static final Object sRemoteServicesLock = new Object();
@@ -61,10 +62,11 @@ public class RoleControllerManager {
     * Global remote services (per user) used by all {@link RoleControllerManager managers}.
     */
    @GuardedBy("sRemoteServicesLock")
    private static final SparseArray<RemoteService> sRemoteServices = new SparseArray<>();
    private static final SparseArray<ServiceConnector<IRoleController>> sRemoteServices =
            new SparseArray<>();

    @NonNull
    private final RemoteService mRemoteService;
    private final ServiceConnector<IRoleController> mRemoteService;

    /**
     * Initialize the remote service component name once so that we can avoid acquiring the
@@ -92,10 +94,19 @@ public class RoleControllerManager {
            @NonNull Handler handler, @NonNull Context context) {
        synchronized (sRemoteServicesLock) {
            int userId = context.getUserId();
            RemoteService remoteService = sRemoteServices.get(userId);
            ServiceConnector<IRoleController> remoteService = sRemoteServices.get(userId);
            if (remoteService == null) {
                remoteService = new RemoteService(ActivityThread.currentApplication(),
                        remoteServiceComponentName, handler, userId);
                remoteService = new ServiceConnector.Impl<IRoleController>(
                        ActivityThread.currentApplication(),
                        new Intent(RoleControllerService.SERVICE_INTERFACE)
                                .setComponent(remoteServiceComponentName),
                        0 /* bindingFlags */, userId, IRoleController.Stub::asInterface) {

                    @Override
                    protected Handler getJobHandler() {
                        return handler;
                    }
                };
                sRemoteServices.put(userId, remoteService);
            }
            mRemoteService = remoteService;
@@ -120,8 +131,12 @@ public class RoleControllerManager {
     */
    public void grantDefaultRoles(@NonNull @CallbackExecutor Executor executor,
            @NonNull Consumer<Boolean> callback) {
        mRemoteService.scheduleRequest(new GrantDefaultRolesRequest(mRemoteService, executor,
                callback));
        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
            AndroidFuture<Bundle> future = new AndroidFuture<>();
            service.grantDefaultRoles(new RemoteCallback(future::complete));
            return future;
        });
        propagateCallback(operation, "grantDefaultRoles", executor, callback);
    }

    /**
@@ -129,8 +144,13 @@ public class RoleControllerManager {
     */
    public void onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
            @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
        mRemoteService.scheduleRequest(new OnAddRoleHolderRequest(mRemoteService, roleName,
                packageName, flags, callback));
        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
            AndroidFuture<Bundle> future = new AndroidFuture<>();
            service.onAddRoleHolder(roleName, packageName, flags,
                    new RemoteCallback(future::complete));
            return future;
        });
        propagateCallback(operation, "onAddRoleHolder", callback);
    }

    /**
@@ -138,8 +158,13 @@ public class RoleControllerManager {
     */
    public void onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
            @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
        mRemoteService.scheduleRequest(new OnRemoveRoleHolderRequest(mRemoteService, roleName,
                packageName, flags, callback));
        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
            AndroidFuture<Bundle> future = new AndroidFuture<>();
            service.onRemoveRoleHolder(roleName, packageName, flags,
                    new RemoteCallback(future::complete));
            return future;
        });
        propagateCallback(operation, "onRemoveRoleHolder", callback);
    }

    /**
@@ -147,8 +172,13 @@ public class RoleControllerManager {
     */
    public void onClearRoleHolders(@NonNull String roleName,
            @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
        mRemoteService.scheduleRequest(new OnClearRoleHoldersRequest(mRemoteService, roleName,
                flags, callback));
        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
            AndroidFuture<Bundle> future = new AndroidFuture<>();
            service.onClearRoleHolders(roleName, flags,
                    new RemoteCallback(future::complete));
            return future;
        });
        propagateCallback(operation, "onClearRoleHolders", callback);
    }

    /**
@@ -157,8 +187,13 @@ public class RoleControllerManager {
    @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
    public void isApplicationQualifiedForRole(@NonNull String roleName, @NonNull String packageName,
            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
        mRemoteService.scheduleRequest(new IsApplicationQualifiedForRoleRequest(mRemoteService,
                roleName, packageName, executor, callback));
        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
            AndroidFuture<Bundle> future = new AndroidFuture<>();
            service.isApplicationQualifiedForRole(roleName, packageName,
                    new RemoteCallback(future::complete));
            return future;
        });
        propagateCallback(operation, "isApplicationQualifiedForRole", executor, callback);
    }

    /**
@@ -167,387 +202,48 @@ public class RoleControllerManager {
    @RequiresPermission(Manifest.permission.MANAGE_ROLE_HOLDERS)
    public void isRoleVisible(@NonNull String roleName,
            @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
        mRemoteService.scheduleRequest(new IsRoleVisibleRequest(mRemoteService, roleName, executor,
                callback));
    }

    /**
     * Connection to the remote service.
     */
    private static final class RemoteService extends AbstractMultiplePendingRequestsRemoteService<
            RemoteService, IRoleController> {

        private static final long UNBIND_DELAY_MILLIS = 15 * 1000;
        private static final long REQUEST_TIMEOUT_MILLIS = 15 * 1000;

        /**
         * Create a connection to the remote service
         *
         * @param context the context to use
         * @param componentName the component of the service to connect to
         * @param handler the handler for binding service and callbacks
         * @param userId the user whom remote service should be connected as
         */
        RemoteService(@NonNull Context context, @NonNull ComponentName componentName,
                @NonNull Handler handler, @UserIdInt int userId) {
            super(context, RoleControllerService.SERVICE_INTERFACE, componentName, userId,
                    service -> Log.e(LOG_TAG, "RemoteService " + service + " died"), handler, 0,
                    false, 1);
        }

        /**
         * @return The default handler used by this service.
         */
        @NonNull
        public Handler getHandler() {
            return mHandler;
        }

        @Override
        protected @NonNull IRoleController getServiceInterface(@NonNull IBinder binder) {
            return IRoleController.Stub.asInterface(binder);
        }

        @Override
        protected long getTimeoutIdleBindMillis() {
            return UNBIND_DELAY_MILLIS;
        }

        @Override
        protected long getRemoteRequestMillis() {
            return REQUEST_TIMEOUT_MILLIS;
        }

        @Override
        public void scheduleRequest(
                @NonNull BasePendingRequest<RemoteService, IRoleController> pendingRequest) {
            super.scheduleRequest(pendingRequest);
        }

        @Override
        public void scheduleAsyncRequest(@NonNull AsyncRequest<IRoleController> request) {
            super.scheduleAsyncRequest(request);
        }
    }

    /**
     * Request for {@link #grantDefaultRoles(Executor, Consumer)}.
     */
    private static final class GrantDefaultRolesRequest
            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {

        @NonNull
        private final Executor mExecutor;
        @NonNull
        private final Consumer<Boolean> mCallback;

        @NonNull
        private final RemoteCallback mRemoteCallback;

        private GrantDefaultRolesRequest(@NonNull RemoteService service,
                @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Boolean> callback) {
            super(service);

            mExecutor = executor;
            mCallback = callback;

            mRemoteCallback = new RemoteCallback(result -> mExecutor.execute(() -> {
                long token = Binder.clearCallingIdentity();
                try {
                    boolean successful = result != null;
                    mCallback.accept(successful);
                } finally {
                    Binder.restoreCallingIdentity(token);
                    finish();
                }
            }));
        }

        @Override
        protected void onTimeout(@NonNull RemoteService remoteService) {
            mExecutor.execute(() -> mCallback.accept(false));
        }

        @Override
        public void run() {
            try {
                getService().getServiceInterface().grantDefaultRoles(mRemoteCallback);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Error calling grantDefaultRoles()", e);
            }
        }

        @Override
        protected void onFailed() {
            mRemoteCallback.sendResult(null);
        }
    }

    /**
     * Request for {@link #onAddRoleHolder(String, String, int, RemoteCallback)}.
     */
    private static final class OnAddRoleHolderRequest
            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {

        @NonNull
        private final String mRoleName;
        @NonNull
        private final String mPackageName;
        @RoleManager.ManageHoldersFlags
        private final int mFlags;
        @NonNull
        private final RemoteCallback mCallback;

        @NonNull
        private final RemoteCallback mRemoteCallback;

        private OnAddRoleHolderRequest(@NonNull RemoteService service, @NonNull String roleName,
                @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags,
                @NonNull RemoteCallback callback) {
            super(service);

            mRoleName = roleName;
            mPackageName = packageName;
            mFlags = flags;
            mCallback = callback;

            mRemoteCallback = new RemoteCallback(result -> {
                long token = Binder.clearCallingIdentity();
                try {
                    mCallback.sendResult(result);
                } finally {
                    Binder.restoreCallingIdentity(token);
                    finish();
                }
            });
        }

        @Override
        protected void onTimeout(@NonNull RemoteService remoteService) {
            mCallback.sendResult(null);
        }

        @Override
        public void run() {
            try {
                getService().getServiceInterface().onAddRoleHolder(mRoleName, mPackageName, mFlags,
                        mRemoteCallback);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Error calling onAddRoleHolder()", e);
            }
        }
    }

    /**
     * Request for {@link #onRemoveRoleHolder(String, String, int, RemoteCallback)}.
     */
    private static final class OnRemoveRoleHolderRequest
            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {

        @NonNull
        private final String mRoleName;
        @NonNull
        private final String mPackageName;
        @RoleManager.ManageHoldersFlags
        private final int mFlags;
        @NonNull
        private final RemoteCallback mCallback;

        @NonNull
        private final RemoteCallback mRemoteCallback;

        private OnRemoveRoleHolderRequest(@NonNull RemoteService service, @NonNull String roleName,
                @NonNull String packageName, @RoleManager.ManageHoldersFlags int flags,
                @NonNull RemoteCallback callback) {
            super(service);

            mRoleName = roleName;
            mPackageName = packageName;
            mFlags = flags;
            mCallback = callback;

            mRemoteCallback = new RemoteCallback(result -> {
                long token = Binder.clearCallingIdentity();
                try {
                    mCallback.sendResult(result);
                } finally {
                    Binder.restoreCallingIdentity(token);
                    finish();
                }
        AndroidFuture<Bundle> operation = mRemoteService.postAsync(service -> {
            AndroidFuture<Bundle> future = new AndroidFuture<>();
            service.isRoleVisible(roleName, new RemoteCallback(future::complete));
            return future;
        });
        propagateCallback(operation, "isRoleVisible", executor, callback);
    }

        @Override
        protected void onTimeout(@NonNull RemoteService remoteService) {
            mCallback.sendResult(null);
        }

        @Override
        public void run() {
            try {
                getService().getServiceInterface().onRemoveRoleHolder(mRoleName, mPackageName,
                        mFlags, mRemoteCallback);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Error calling onRemoveRoleHolder()", e);
            }
        }
    }

    /**
     * Request for {@link #onClearRoleHolders(String, int, RemoteCallback)}.
     */
    private static final class OnClearRoleHoldersRequest
            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {

        @NonNull
        private final String mRoleName;
        @RoleManager.ManageHoldersFlags
        private final int mFlags;
        @NonNull
        private final RemoteCallback mCallback;

        @NonNull
        private final RemoteCallback mRemoteCallback;

        private OnClearRoleHoldersRequest(@NonNull RemoteService service, @NonNull String roleName,
                @RoleManager.ManageHoldersFlags int flags, @NonNull RemoteCallback callback) {
            super(service);

            mRoleName = roleName;
            mFlags = flags;
            mCallback = callback;

            mRemoteCallback = new RemoteCallback(result -> {
    private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
            @CallbackExecutor @NonNull Executor executor,
            Consumer<Boolean> destination) {
        operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .whenComplete((res, err) -> executor.execute(() -> {
                    long token = Binder.clearCallingIdentity();
                    try {
                    mCallback.sendResult(result);
                } finally {
                    Binder.restoreCallingIdentity(token);
                    finish();
                }
            });
        }

        @Override
        protected void onTimeout(@NonNull RemoteService remoteService) {
            mCallback.sendResult(null);
        }

        @Override
        public void run() {
            try {
                getService().getServiceInterface().onClearRoleHolders(mRoleName, mFlags,
                        mRemoteCallback);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Error calling onClearRoleHolders()", e);
                        if (err != null) {
                            Log.e(LOG_TAG, "Error calling " + opName + "()", err);
                            destination.accept(false);
                        } else {
                            destination.accept(res != null);
                        }
        }
    }

    /**
     * Request for {@link #isApplicationQualifiedForRole(String, String, Executor, Consumer)}
     */
    private static final class IsApplicationQualifiedForRoleRequest extends
            AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {

        @NonNull
        private final String mRoleName;
        @NonNull
        private final String mPackageName;
        @NonNull
        private final Executor mExecutor;
        @NonNull
        private final Consumer<Boolean> mCallback;

        @NonNull
        private final RemoteCallback mRemoteCallback;

        private IsApplicationQualifiedForRoleRequest(@NonNull RemoteService service,
                @NonNull String roleName, @NonNull String packageName,
                @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
            super(service);

            mRoleName = roleName;
            mPackageName = packageName;
            mExecutor = executor;
            mCallback = callback;

            mRemoteCallback = new RemoteCallback(result -> mExecutor.execute(() -> {
                long token = Binder.clearCallingIdentity();
                try {
                    boolean qualified = result != null;
                    mCallback.accept(qualified);
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    finish();
                    }
                }));
    }

        @Override
        protected void onTimeout(RemoteService remoteService) {
            mExecutor.execute(() -> mCallback.accept(false));
        }

        @Override
        public void run() {
            try {
                getService().getServiceInterface().isApplicationQualifiedForRole(mRoleName,
                        mPackageName, mRemoteCallback);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Error calling isApplicationQualifiedForRole()", e);
            }
        }
    }

    /**
     * Request for {@link #isRoleVisible(String, Executor, Consumer)}
     */
    private static final class IsRoleVisibleRequest
            extends AbstractRemoteService.PendingRequest<RemoteService, IRoleController> {

        @NonNull
        private final String mRoleName;
        @NonNull
        private final Executor mExecutor;
        @NonNull
        private final Consumer<Boolean> mCallback;

        @NonNull
        private final RemoteCallback mRemoteCallback;

        private IsRoleVisibleRequest(@NonNull RemoteService service, @NonNull String roleName,
                @CallbackExecutor @NonNull Executor executor, @NonNull Consumer<Boolean> callback) {
            super(service);

            mRoleName = roleName;
            mExecutor = executor;
            mCallback = callback;

            mRemoteCallback = new RemoteCallback(result -> mExecutor.execute(() -> {
    private void propagateCallback(AndroidFuture<Bundle> operation, String opName,
            RemoteCallback destination) {
        operation.orTimeout(REQUEST_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS)
                .whenComplete((res, err) -> {
                    long token = Binder.clearCallingIdentity();
                    try {
                    boolean visible = result != null;
                    mCallback.accept(visible);
                        if (err != null) {
                            Log.e(LOG_TAG, "Error calling " + opName + "()", err);
                            destination.sendResult(null);
                        } else {
                            destination.sendResult(res);
                        }
                    } finally {
                        Binder.restoreCallingIdentity(token);
                    finish();
                }
            }));
        }

        @Override
        protected void onTimeout(RemoteService remoteService) {
            mExecutor.execute(() -> mCallback.accept(false));
        }

        @Override
        public void run() {
            try {
                getService().getServiceInterface().isRoleVisible(mRoleName, mRemoteCallback);
            } catch (RemoteException e) {
                Log.e(LOG_TAG, "Error calling isRoleVisible()", e);
            }
                    }
                });
    }
}