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

Commit db48fe13 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Factor out service handling out of RuntimePermissionPresenter"

parents 1dd29096 22b84988
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();