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

Commit 22b84988 authored by Philip P. Moltmann's avatar Philip P. Moltmann
Browse files

Factor out service handling out of RuntimePermissionPresenter

Fixes: 121270006
Test: Looked at settings page that use RuntimePermissionPresenter
Change-Id: I3030511bce76fd0f30fac1ac922f6ff9f09ca80e
parent 29ee0a48
Loading
Loading
Loading
Loading
+168 −153
Original line number Diff line number Diff line
@@ -16,27 +16,28 @@

package android.permission;

import static android.permission.RuntimePermissionPresenterService.SERVICE_INTERFACE;

import static com.android.internal.util.Preconditions.checkCollectionElementsNotNull;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.RemoteCallback;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
import com.android.internal.infra.AbstractRemoteService;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@@ -80,7 +81,7 @@ public final class RuntimePermissionPresenter {
     */
    public interface OnCountPermissionAppsResultCallback {
        /**
         * The result for {@link #countPermissionApps(List, boolean,
         * The result for {@link #countPermissionApps(List, boolean, boolean,
         * OnCountPermissionAppsResultCallback, Handler)}.
         *
         * @param numApps The number of apps that have one of the permissions
@@ -110,8 +111,13 @@ public final class RuntimePermissionPresenter {
        }
    }

    private RuntimePermissionPresenter(Context context) {
        mRemoteService = new RemoteService(context);
    private RuntimePermissionPresenter(@NonNull Context context) {
        Intent intent = new Intent(SERVICE_INTERFACE);
        intent.setPackage(context.getPackageManager().getPermissionControllerPackageName());
        ResolveInfo serviceInfo = context.getPackageManager().resolveService(intent, 0);

        mRemoteService = new RemoteService(context,
                serviceInfo.getComponentInfo().getComponentName());
    }

    /**
@@ -126,8 +132,8 @@ public final class RuntimePermissionPresenter {
        checkNotNull(packageName);
        checkNotNull(callback);

        mRemoteService.processMessage(obtainMessage(RemoteService::getAppPermissions,
                mRemoteService, packageName, callback, handler));
        mRemoteService.scheduleRequest(new PendingGetAppPermissionRequest(mRemoteService,
                packageName, callback, handler == null ? mRemoteService.getHandler() : handler));
    }

    /**
@@ -141,8 +147,8 @@ public final class RuntimePermissionPresenter {
        checkNotNull(packageName);
        checkNotNull(permissionName);

        mRemoteService.processMessage(obtainMessage(RemoteService::revokeAppPermissions,
                mRemoteService, packageName, permissionName));
        mRemoteService.scheduleAsyncRequest(new PendingRevokeAppPermissionRequest(packageName,
                permissionName));
    }

    /**
@@ -160,76 +166,87 @@ public final class RuntimePermissionPresenter {
        checkCollectionElementsNotNull(permissionNames, "permissionNames");
        checkNotNull(callback);

        mRemoteService.processMessage(obtainMessage(RemoteService::countPermissionApps,
                mRemoteService, permissionNames, countOnlyGranted, countSystem, callback, handler));
        mRemoteService.scheduleRequest(new PendingCountPermissionAppsRequest(mRemoteService,
                permissionNames, countOnlyGranted, countSystem, callback,
                handler == null ? mRemoteService.getHandler() : handler));
    }

    private static final class RemoteService
            extends Handler implements ServiceConnection {
    /**
     * A connection to the remote service
     */
    static final class RemoteService extends
            AbstractMultiplePendingRequestsRemoteService<RemoteService,
                    IRuntimePermissionPresenter>  {
        private static final long UNBIND_TIMEOUT_MILLIS = 10000;
        private static final long MESSAGE_TIMEOUT_MILLIS = 30000;

        public static final int MSG_UNBIND = 0;

        private final Object mLock = new Object();

        private final Context mContext;

        @GuardedBy("mLock")
        private final List<Message> mPendingWork = new ArrayList<>();

        @GuardedBy("mLock")
        private IRuntimePermissionPresenter mRemoteInstance;

        @GuardedBy("mLock")
        private boolean mBound;

        RemoteService(Context context) {
            super(context.getMainLooper(), null, false);
            mContext = context;
        /**
         * Create a connection to the remote service
         *
         * @param context A context to use
         * @param componentName The component of the service to connect to
         */
        RemoteService(@NonNull Context context, @NonNull ComponentName componentName) {
            super(context, SERVICE_INTERFACE, componentName, UserHandle.myUserId(),
                    service -> Log.e(TAG, "RuntimePermPresenterService " + service + " died"),
                    false, false, 1);
        }

        public void processMessage(Message message) {
            synchronized (mLock) {
                if (!mBound) {
                    Intent intent = new Intent(
                            RuntimePermissionPresenterService.SERVICE_INTERFACE);
                    intent.setPackage(mContext.getPackageManager()
                            .getPermissionControllerPackageName());
                    mBound = mContext.bindService(intent, this,
                            Context.BIND_AUTO_CREATE);
                }
                mPendingWork.add(message);
                scheduleNextMessageIfNeededLocked();
            }
        /**
         * @return The default handler used by this service.
         */
        Handler getHandler() {
            return mHandler;
        }

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            synchronized (mLock) {
                mRemoteInstance = IRuntimePermissionPresenter.Stub.asInterface(service);
                scheduleNextMessageIfNeededLocked();
        protected @NonNull IRuntimePermissionPresenter getServiceInterface(
                @NonNull IBinder binder) {
            return IRuntimePermissionPresenter.Stub.asInterface(binder);
        }

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

        @Override
        public void onServiceDisconnected(ComponentName name) {
            synchronized (mLock) {
                mRemoteInstance = null;
        protected long getRemoteRequestMillis() {
            return MESSAGE_TIMEOUT_MILLIS;
        }

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

        private void getAppPermissions(@NonNull String packageName,
                @NonNull OnGetAppPermissionResultCallback callback, @Nullable Handler handler) {
            final IRuntimePermissionPresenter remoteInstance;
            synchronized (mLock) {
                remoteInstance = mRemoteInstance;
        @Override
        public void scheduleAsyncRequest(
                @NonNull AsyncRequest<IRuntimePermissionPresenter> request) {
            super.scheduleAsyncRequest(request);
        }
            if (remoteInstance == null) {
                return;
    }
            try {
                remoteInstance.getAppPermissions(packageName,
                        new RemoteCallback(result -> {

    /**
     * Request for {@link #getAppPermissions}
     */
    private static final class PendingGetAppPermissionRequest extends
            AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
        private final @NonNull String mPackageName;
        private final @NonNull OnGetAppPermissionResultCallback mCallback;

        private final @NonNull RemoteCallback mRemoteCallback;

        private PendingGetAppPermissionRequest(@NonNull RemoteService service,
                @NonNull String packageName, @NonNull OnGetAppPermissionResultCallback callback,
                @NonNull Handler handler) {
            super(service);

            mPackageName = packageName;
            mCallback = callback;

            mRemoteCallback = new RemoteCallback(result -> {
                final List<RuntimePermissionPresentationInfo> reportedPermissions;
                List<RuntimePermissionPresentationInfo> permissions = null;
                if (result != null) {
@@ -239,58 +256,76 @@ public final class RuntimePermissionPresenter {
                    permissions = Collections.emptyList();
                }
                reportedPermissions = permissions;
                            if (handler != null) {
                                handler.post(
                                        () -> callback.onGetAppPermissions(reportedPermissions));
                            } else {

                callback.onGetAppPermissions(reportedPermissions);
                            }
                        }, this));
            } catch (RemoteException re) {
                Log.e(TAG, "Error getting app permissions", re);
            }
            scheduleUnbind();

            synchronized (mLock) {
                scheduleNextMessageIfNeededLocked();
            }
                finish();
            }, handler);
        }

        private void revokeAppPermissions(@NonNull String packageName,
                @NonNull String permissionName) {
            final IRuntimePermissionPresenter remoteInstance;
            synchronized (mLock) {
                remoteInstance = mRemoteInstance;
            }
            if (remoteInstance == null) {
                return;
        @Override
        protected void onTimeout(RemoteService remoteService) {
            mCallback.onGetAppPermissions(Collections.emptyList());
        }

        @Override
        public void run() {
            try {
                remoteInstance.revokeRuntimePermission(packageName, permissionName);
            } catch (RemoteException re) {
                Log.e(TAG, "Error getting app permissions", re);
                getService().getServiceInterface().getAppPermissions(mPackageName, mRemoteCallback);
            } catch (RemoteException e) {
                Log.e(TAG, "Error getting app permission", e);
            }

            synchronized (mLock) {
                scheduleNextMessageIfNeededLocked();
        }
    }

        private void countPermissionApps(@NonNull List<String> permissionNames,
                boolean countOnlyGranted, boolean countSystem,
                @NonNull OnCountPermissionAppsResultCallback callback, @Nullable Handler handler) {
            final IRuntimePermissionPresenter remoteInstance;
    /**
     * Request for {@link #revokeRuntimePermission}
     */
    private static final class PendingRevokeAppPermissionRequest
            implements AbstractRemoteService.AsyncRequest<IRuntimePermissionPresenter> {
        private final @NonNull String mPackageName;
        private final @NonNull String mPermissionName;

            synchronized (mLock) {
                remoteInstance = mRemoteInstance;
            }
            if (remoteInstance == null) {
                return;
        private PendingRevokeAppPermissionRequest(@NonNull String packageName,
                @NonNull String permissionName) {
            mPackageName = packageName;
            mPermissionName = permissionName;
        }

        @Override
        public void run(IRuntimePermissionPresenter remoteInterface) {
            try {
                remoteInstance.countPermissionApps(permissionNames, countOnlyGranted, countSystem,
                        new RemoteCallback(result -> {
                remoteInterface.revokeRuntimePermission(mPackageName, mPermissionName);
            } catch (RemoteException e) {
                Log.e(TAG, "Error revoking app permission", e);
            }
        }
    }

    /**
     * Request for {@link #countPermissionApps}
     */
    private static final class PendingCountPermissionAppsRequest extends
            AbstractRemoteService.PendingRequest<RemoteService, IRuntimePermissionPresenter> {
        private final @NonNull List<String> mPermissionNames;
        private final @NonNull OnCountPermissionAppsResultCallback mCallback;
        private final boolean mCountOnlyGranted;
        private final boolean mCountSystem;

        private final @NonNull RemoteCallback mRemoteCallback;

        private PendingCountPermissionAppsRequest(@NonNull RemoteService service,
                @NonNull List<String> permissionNames, boolean countOnlyGranted,
                boolean countSystem, @NonNull OnCountPermissionAppsResultCallback callback,
                @NonNull Handler handler) {
            super(service);

            mPermissionNames = permissionNames;
            mCountOnlyGranted = countOnlyGranted;
            mCountSystem = countSystem;
            mCallback = callback;

            mRemoteCallback = new RemoteCallback(result -> {
                final int numApps;
                if (result != null) {
                    numApps = result.getInt(KEY_RESULT);
@@ -298,45 +333,25 @@ public final class RuntimePermissionPresenter {
                    numApps = 0;
                }

                            if (handler != null) {
                                handler.post(() -> callback.onCountPermissionApps(numApps));
                            } else {
                callback.onCountPermissionApps(numApps);
                            }
                        }, this));
            } catch (RemoteException re) {
                Log.e(TAG, "Error counting permission apps", re);
            }

            scheduleUnbind();

            synchronized (mLock) {
                scheduleNextMessageIfNeededLocked();
            }
                finish();
            }, handler);
        }

        private void unbind() {
            synchronized (mLock) {
                if (mBound) {
                    mContext.unbindService(this);
                    mBound = false;
                }
                mRemoteInstance = null;
            }
        @Override
        protected void onTimeout(RemoteService remoteService) {
            mCallback.onCountPermissionApps(0);
        }

        @GuardedBy("mLock")
        private void scheduleNextMessageIfNeededLocked() {
            if (mBound && mRemoteInstance != null && !mPendingWork.isEmpty()) {
                Message nextMessage = mPendingWork.remove(0);
                sendMessage(nextMessage);
            }
        @Override
        public void run() {
            try {
                getService().getServiceInterface().countPermissionApps(mPermissionNames,
                        mCountOnlyGranted, mCountSystem, mRemoteCallback);
            } catch (RemoteException e) {
                Log.e(TAG, "Error counting permission apps", e);
            }

        private void scheduleUnbind() {
            removeMessages(MSG_UNBIND);
            sendMessageDelayed(PooledLambda.obtainMessage(RemoteService::unbind, this)
                    .setWhat(MSG_UNBIND), UNBIND_TIMEOUT_MILLIS);
        }
    }
}
+10 −0
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ package com.android.internal.infra;
import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -164,6 +165,15 @@ public abstract class AbstractRemoteService<S extends AbstractRemoteService<S, I
     */
    protected abstract long getRemoteRequestMillis();

    /**
     * Gets the currently registered service interface or {@code null} if the service is not
     * connected.
     */
    @Nullable
    public final I getServiceInterface() {
        return mService;
    }

    private void handleDestroy() {
        if (checkIfDestroyed()) return;
        handleOnDestroy();