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

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

Merge changes from topic "improve-perm-request-flow" into main

* changes:
  Optimize permission request flow for un-requestable/granted permission
  Add new permission API to optimize permission request flow
parents 581bcc15 7ab77461
Loading
Loading
Loading
Loading
+45 −4
Original line number Diff line number Diff line
@@ -5782,8 +5782,7 @@ public class Activity extends ContextThemeWrapper
        }

        if (!getAttributionSource().getRenouncedPermissions().isEmpty()) {
            final int permissionCount = permissions.length;
            for (int i = 0; i < permissionCount; i++) {
            for (int i = 0; i < permissions.length; i++) {
                if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) {
                    throw new IllegalArgumentException("Cannot request renounced permission: "
                            + permissions[i]);
@@ -5791,13 +5790,55 @@ public class Activity extends ContextThemeWrapper
            }
        }

        PackageManager packageManager = getDeviceId() == deviceId ? getPackageManager()
                : createDeviceContext(deviceId).getPackageManager();
        final Context context = getDeviceId() == deviceId ? this : createDeviceContext(deviceId);
        if (Flags.permissionRequestShortCircuitEnabled()) {
            int[] permissionsState = getPermissionRequestStates(context, permissions);
            boolean hasRequestablePermission = false;
            for (int i = 0; i < permissionsState.length; i++) {
                if (permissionsState[i] == Context.PERMISSION_REQUEST_STATE_REQUESTABLE) {
                    hasRequestablePermission = true;
                    break;
                }
            }
            // If none of the permissions is requestable, finish the request here.
            if (!hasRequestablePermission) {
                mHasCurrentPermissionsRequest = true;
                Log.v(TAG, "No requestable permission in the request.");
                int[] results = new int[permissionsState.length];
                for (int i = 0; i < permissionsState.length; i++) {
                    if (permissionsState[i] == Context.PERMISSION_REQUEST_STATE_GRANTED) {
                        results[i] = PackageManager.PERMISSION_GRANTED;
                    } else {
                        results[i] = PackageManager.PERMISSION_DENIED;
                    }
                }
                // Currently permission request result is passed to the client app asynchronously
                // in onRequestPermissionsResult, lets keep async behavior here as well.
                mHandler.post(() -> {
                    mHasCurrentPermissionsRequest = false;
                    onRequestPermissionsResult(requestCode, permissions, results, deviceId);
                });
                return;
            }
        }

        final PackageManager packageManager = context.getPackageManager();
        final Intent intent = packageManager.buildRequestPermissionsIntent(permissions);
        startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
        mHasCurrentPermissionsRequest = true;
    }

    @NonNull
    private int[] getPermissionRequestStates(@NonNull Context deviceContext,
            @NonNull String[] permissions) {
        final int size = permissions.length;
        int[] results = new int[size];
        for (int i = 0; i < size; i++) {
            results[i] = deviceContext.getPermissionRequestState(permissions[i]);
        }
        return results;
    }

    /**
     * Callback for the result from requesting permissions. This method
     * is invoked for every call on {@link #requestPermissions}
+17 −4
Original line number Diff line number Diff line
@@ -2366,7 +2366,11 @@ class ContextImpl extends Context {
            Log.v(TAG, "Treating renounced permission " + permission + " as denied");
            return PERMISSION_DENIED;
        }
        int deviceId = resolveDeviceIdForPermissionCheck(permission);
        return PermissionManager.checkPermission(permission, pid, uid, deviceId);
    }

    private int resolveDeviceIdForPermissionCheck(String permission) {
        // When checking a device-aware permission on a remote device, if the permission is CAMERA
        // or RECORD_AUDIO we need to check remote device's corresponding capability. If the remote
        // device doesn't have capability fall back to checking permission on the default device.
@@ -2400,8 +2404,7 @@ class ContextImpl extends Context {
                }
            }
        }

        return PermissionManager.checkPermission(permission, pid, uid, deviceId);
        return deviceId;
    }

    /** @hide */
@@ -2503,6 +2506,16 @@ class ContextImpl extends Context {
                message);
    }

    /** @hide */
    @Override
    public int getPermissionRequestState(String permission) {
        Objects.requireNonNull(permission, "Permission name can't be null");
        int deviceId = resolveDeviceIdForPermissionCheck(permission);
        PermissionManager permissionManager = getSystemService(PermissionManager.class);
        return permissionManager.getPermissionRequestState(getOpPackageName(), permission,
                deviceId);
    }

    @Override
    public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
         try {
+59 −0
Original line number Diff line number Diff line
@@ -787,6 +787,40 @@ public abstract class Context {
     */
    public static final int RECEIVER_NOT_EXPORTED = 0x4;

    /**
     * The permission is granted.
     *
     * @hide
     */
    public static final int PERMISSION_REQUEST_STATE_GRANTED = 0;

    /**
     * The permission isn't granted, but apps can request the permission. When the app request
     * the permission, user will be prompted with permission dialog to grant or deny the request.
     *
     * @hide
     */
    public static final int PERMISSION_REQUEST_STATE_REQUESTABLE = 1;

    /**
     * The permission is denied, and shouldn't be requested by apps. Permission request
     * will be automatically denied by the system, preventing the permission dialog from being
     * displayed to the user.
     *
     * @hide
     */
    public static final int PERMISSION_REQUEST_STATE_UNREQUESTABLE = 2;


    /** @hide */
    @IntDef(prefix = { "PERMISSION_REQUEST_STATE_" }, value = {
            PERMISSION_REQUEST_STATE_GRANTED,
            PERMISSION_REQUEST_STATE_REQUESTABLE,
            PERMISSION_REQUEST_STATE_UNREQUESTABLE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface PermissionRequestState {}

    /**
     * Returns an AssetManager instance for the application's package.
     * <p>
@@ -6988,6 +7022,31 @@ public abstract class Context {
    public abstract void enforceCallingOrSelfPermission(
            @NonNull @PermissionName String permission, @Nullable String message);

    /**
     * Returns the permission request state for a given runtime permission. This method provides a
     * streamlined mechanism for applications to determine whether a permission can be
     * requested (i.e. whether the user will be prompted with a permission dialog).
     *
     * <p>Traditionally, determining if a permission has been permanently denied (unrequestable)
     * required applications to initiate a permission request and subsequently analyze the result
     * of {@link android.app.Activity#shouldShowRequestPermissionRationale} in conjunction with the
     * grant result within the {@link android.app.Activity#onRequestPermissionsResult} callback.
     *
     * @param permission The name of the permission.
     *
     * @return The current request state of the specified permission, represented by one of the
     * following constants: {@link PermissionRequestState#PERMISSION_REQUEST_STATE_GRANTED},
     * {@link PermissionRequestState#PERMISSION_REQUEST_STATE_REQUESTABLE}, or
     * {@link PermissionRequestState#PERMISSION_REQUEST_STATE_UNREQUESTABLE}.
     *
     * @hide
     */
    @CheckResult
    @PermissionRequestState
    public int getPermissionRequestState(@NonNull String permission) {
        throw new RuntimeException("Not implemented. Must override in a subclass.");
    }

    /**
     * Grant permission to access a specific Uri to another package, regardless
     * of whether that package has general permission to access the Uri's
+6 −0
Original line number Diff line number Diff line
@@ -1012,6 +1012,12 @@ public class ContextWrapper extends Context {
        mBase.enforceCallingOrSelfPermission(permission, message);
    }

    /** @hide */
    @Override
    public int getPermissionRequestState(String permission) {
        return mBase.getPermissionRequestState(permission);
    }

    @Override
    public void grantUriPermission(String toPackage, Uri uri, int modeFlags) {
        mBase.grantUriPermission(toPackage, uri, modeFlags);
+2 −0
Original line number Diff line number Diff line
@@ -108,6 +108,8 @@ interface IPermissionManager {
    int checkUidPermission(int uid, String permissionName, int deviceId);

    Map<String, PermissionState> getAllPermissionStates(String packageName, String persistentDeviceId, int userId);

    int getPermissionRequestState(String packageName, String permissionName, int deviceId);
}

/**
Loading