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

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

Merge "Remove permission state classes for the legacy permission subsystem" into main

parents b55c8c75 cc76b207
Loading
Loading
Loading
Loading
+0 −58
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm.permission;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.util.SparseArray;

/**
 * Permission state for this device.
 */
public final class DevicePermissionState {
    private final SparseArray<UserPermissionState> mUserStates = new SparseArray<>();

    @Nullable
    public UserPermissionState getUserState(@UserIdInt int userId) {
        return mUserStates.get(userId);
    }

    @NonNull
    public UserPermissionState getOrCreateUserState(@UserIdInt int userId) {
        UserPermissionState userState = mUserStates.get(userId);
        if (userState == null) {
            userState = new UserPermissionState();
            mUserStates.put(userId, userState);
        }
        return userState;
    }

    public void removeUserState(@UserIdInt int userId) {
        mUserStates.delete(userId);
    }

    public int[] getUserIds() {
        final int userStatesSize = mUserStates.size();
        final int[] userIds = new int[userStatesSize];
        for (int i = 0; i < userStatesSize; i++) {
            final int userId = mUserStates.keyAt(i);
            userIds[i] = userId;
        }
        return userIds;
    }
}
+0 −118
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm.permission;

import android.annotation.NonNull;
import android.annotation.UserIdInt;

import com.android.internal.annotations.GuardedBy;

/**
 * State for a single permission.
 */
public final class PermissionState {

    @NonNull
    private final Permission mPermission;

    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private boolean mGranted;

    @GuardedBy("mLock")
    private int mFlags;

    public PermissionState(@NonNull Permission permission) {
        mPermission = permission;
    }

    public PermissionState(@NonNull PermissionState other) {
        this(other.mPermission);

        mGranted = other.mGranted;
        mFlags = other.mFlags;
    }

    @NonNull
    public Permission getPermission() {
        return mPermission;
    }

    @NonNull
    public String getName() {
        return mPermission.getName();
    }

    @NonNull
    public int[] computeGids(@UserIdInt int userId) {
        return mPermission.computeGids(userId);
    }

    public boolean isGranted() {
        synchronized (mLock) {
            return mGranted;
        }
    }

    public boolean grant() {
        synchronized (mLock) {
            if (mGranted) {
                return false;
            }
            mGranted = true;
            UidPermissionState.invalidateCache();
            return true;
        }
    }

    public boolean revoke() {
        synchronized (mLock) {
            if (!mGranted) {
                return false;
            }
            mGranted = false;
            UidPermissionState.invalidateCache();
            return true;
        }
    }

    public int getFlags() {
        synchronized (mLock) {
            return mFlags;
        }
    }

    public boolean updateFlags(int flagMask, int flagValues) {
        synchronized (mLock) {
            final int newFlags = flagValues & flagMask;

            // Okay to do before the modification because we hold the lock.
            UidPermissionState.invalidateCache();

            final int oldFlags = mFlags;
            mFlags = (mFlags & ~flagMask) | newFlags;
            return mFlags != oldFlags;
        }
    }

    public boolean isDefault() {
        synchronized (mLock) {
            return !mGranted && mFlags == 0;
        }
    }
}
+0 −354
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm.permission;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.pm.PackageManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IntArray;

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

/**
 * Permission state for a UID.
 */
public final class UidPermissionState {
    private boolean mMissing;

    @Nullable
    private ArrayMap<String, PermissionState> mPermissions;

    public UidPermissionState() {}

    public UidPermissionState(@NonNull UidPermissionState other) {
        mMissing = other.mMissing;

        if (other.mPermissions != null) {
            mPermissions = new ArrayMap<>();
            final int permissionsSize = other.mPermissions.size();
            for (int i = 0; i < permissionsSize; i++) {
                final String name = other.mPermissions.keyAt(i);
                final PermissionState permissionState = other.mPermissions.valueAt(i);
                mPermissions.put(name, new PermissionState(permissionState));
            }
        }
    }

    /**
     * Reset the internal state of this object.
     */
    public void reset() {
        mMissing = false;
        mPermissions = null;
        invalidateCache();
    }

    /**
     * Check whether the permissions state is missing for a user. This can happen if permission
     * state is rolled back and we'll need to generate a reasonable default state to keep the app
     * usable.
     */
    public boolean isMissing() {
        return mMissing;
    }

    /**
     * Set whether the permissions state is missing for a user. This can happen if permission state
     * is rolled back and we'll need to generate a reasonable default state to keep the app usable.
     */
    public void setMissing(boolean missing) {
        mMissing = missing;
    }

    /**
     * Get whether there is a permission state for a permission.
     *
     * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
     */
    @Deprecated
    public boolean hasPermissionState(@NonNull String name) {
        return mPermissions != null && mPermissions.containsKey(name);
    }

    /**
     * Get whether there is a permission state for any of the permissions.
     *
     * @deprecated This used to be named hasRequestedPermission() and its usage is confusing
     */
    @Deprecated
    public boolean hasPermissionState(@NonNull ArraySet<String> names) {
        if (mPermissions == null) {
            return false;
        }
        final int namesSize = names.size();
        for (int i = 0; i < namesSize; i++) {
            final String name = names.valueAt(i);
            if (mPermissions.containsKey(name)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Gets the state for a permission or null if none.
     *
     * @param name the permission name
     * @return the permission state
     */
    @Nullable
    public PermissionState getPermissionState(@NonNull String name) {
        if (mPermissions == null) {
            return null;
        }
        return mPermissions.get(name);
    }

    @NonNull
    private PermissionState getOrCreatePermissionState(@NonNull Permission permission) {
        if (mPermissions == null) {
            mPermissions = new ArrayMap<>();
        }
        final String name = permission.getName();
        PermissionState permissionState = mPermissions.get(name);
        if (permissionState == null) {
            permissionState = new PermissionState(permission);
            mPermissions.put(name, permissionState);
        }
        return permissionState;
    }

    /**
     * Get all permission states.
     *
     * @return the permission states
     */
    @NonNull
    public List<PermissionState> getPermissionStates() {
        if (mPermissions == null) {
            return Collections.emptyList();
        }
        return new ArrayList<>(mPermissions.values());
    }

    /**
     * Put a permission state.
     *
     * @param permission the permission
     * @param granted whether the permission is granted
     * @param flags the permission flags
     */
    public void putPermissionState(@NonNull Permission permission, boolean granted, int flags) {
        final String name = permission.getName();
        if (mPermissions == null) {
            mPermissions = new ArrayMap<>();
        } else {
            mPermissions.remove(name);
        }
        final PermissionState permissionState = new PermissionState(permission);
        if (granted) {
            permissionState.grant();
        }
        permissionState.updateFlags(flags, flags);
        mPermissions.put(name, permissionState);
    }

    /**
     * Remove a permission state.
     *
     * @param name the permission name
     * @return whether the permission state changed
     */
    public boolean removePermissionState(@NonNull String name) {
        if (mPermissions == null) {
            return false;
        }
        final boolean changed = mPermissions.remove(name) != null;
        if (changed && mPermissions.isEmpty()) {
            mPermissions = null;
        }
        return changed;
    }

    /**
     * Get whether a permission is granted.
     *
     * @param name the permission name
     * @return whether the permission is granted
     */
    public boolean isPermissionGranted(@NonNull String name) {
        final PermissionState permissionState = getPermissionState(name);
        return permissionState != null && permissionState.isGranted();
    }

    /**
     * Get all the granted permissions.
     *
     * @return the granted permissions
     */
    @NonNull
    public Set<String> getGrantedPermissions() {
        if (mPermissions == null) {
            return Collections.emptySet();
        }

        final Set<String> permissions = new ArraySet<>(mPermissions.size());
        final int permissionsSize = mPermissions.size();
        for (int i = 0; i < permissionsSize; i++) {
            final PermissionState permissionState = mPermissions.valueAt(i);

            if (permissionState.isGranted()) {
                permissions.add(permissionState.getName());
            }
        }
        return permissions;
    }

    /**
     * Grant a permission.
     *
     * @param permission the permission to grant
     * @return whether the permission grant state changed
     */
    public boolean grantPermission(@NonNull Permission permission) {
        final PermissionState permissionState = getOrCreatePermissionState(permission);
        return permissionState.grant();
    }

    /**
     * Revoke a permission.
     *
     * @param permission the permission to revoke
     * @return whether the permission grant state changed
     */
    public boolean revokePermission(@NonNull Permission permission) {
        final String name = permission.getName();
        final PermissionState permissionState = getPermissionState(name);
        if (permissionState == null) {
            return false;
        }
        final boolean changed = permissionState.revoke();
        if (changed && permissionState.isDefault()) {
            removePermissionState(name);
        }
        return changed;
    }

    /**
     * Get the flags for a permission.
     *
     * @param name the permission name
     * @return the permission flags
     */
    public int getPermissionFlags(@NonNull String name) {
        final PermissionState permissionState = getPermissionState(name);
        if (permissionState == null) {
            return 0;
        }
        return permissionState.getFlags();
    }

    /**
     * Update the flags for a permission.
     *
     * @param permission the permission name
     * @param flagMask the mask for the flags
     * @param flagValues the new values for the masked flags
     * @return whether the permission flags changed
     */
    public boolean updatePermissionFlags(@NonNull Permission permission, int flagMask,
            int flagValues) {
        if (flagMask == 0) {
            return false;
        }
        final PermissionState permissionState = getOrCreatePermissionState(permission);
        final boolean changed = permissionState.updateFlags(flagMask, flagValues);
        if (changed && permissionState.isDefault()) {
            removePermissionState(permission.getName());
        }
        return changed;
    }

    public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
        if (flagMask == 0) {
            return false;
        }
        if (mPermissions == null) {
            return false;
        }
        boolean anyChanged = false;
        for (int i = mPermissions.size() - 1; i >= 0; i--) {
            final PermissionState permissionState = mPermissions.valueAt(i);
            final boolean changed = permissionState.updateFlags(flagMask, flagValues);
            if (changed && permissionState.isDefault()) {
                mPermissions.removeAt(i);
            }
            anyChanged |= changed;
        }
        return anyChanged;
    }

    public boolean isPermissionsReviewRequired() {
        if (mPermissions == null) {
            return false;
        }
        final int permissionsSize = mPermissions.size();
        for (int i = 0; i < permissionsSize; i++) {
            final PermissionState permission = mPermissions.valueAt(i);
            if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Compute the Linux GIDs from the permissions granted to a user.
     *
     * @param userId the user ID
     * @return the GIDs for the user
     */
    @NonNull
    public int[] computeGids(@NonNull int[] globalGids, @UserIdInt int userId) {
        IntArray gids = IntArray.wrap(globalGids);
        if (mPermissions == null) {
            return gids.toArray();
        }
        final int permissionsSize = mPermissions.size();
        for (int i = 0; i < permissionsSize; i++) {
            PermissionState permissionState = mPermissions.valueAt(i);
            if (!permissionState.isGranted()) {
                continue;
            }
            final int[] permissionGids = permissionState.computeGids(userId);
            if (permissionGids.length != 0) {
                gids.addAll(permissionGids);
            }
        }
        return gids.toArray();
    }

    static void invalidateCache() {
        PackageManager.invalidatePackageInfoCache();
    }
}
+0 −91
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm.permission;

import android.annotation.AppIdInt;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.SparseArray;

/**
 * Permission state for a user.
 */
public final class UserPermissionState {
    /**
     * Whether the install permissions have been granted to a package, so that no install
     * permissions should be added to it unless the package is upgraded.
     */
    @NonNull
    private final ArraySet<String> mInstallPermissionsFixed = new ArraySet<>();

    /**
     * Maps from app ID to {@link UidPermissionState}.
     */
    @NonNull
    private final SparseArray<UidPermissionState> mUidStates = new SparseArray<>();

    public boolean areInstallPermissionsFixed(@NonNull String packageName) {
        return mInstallPermissionsFixed.contains(packageName);
    }

    public void setInstallPermissionsFixed(@NonNull String packageName, boolean fixed) {
        if (fixed) {
            mInstallPermissionsFixed.add(packageName);
        } else {
            mInstallPermissionsFixed.remove(packageName);
        }
    }

    @Nullable
    public UidPermissionState getUidState(@AppIdInt int appId) {
        checkAppId(appId);
        return mUidStates.get(appId);
    }

    @NonNull
    public UidPermissionState getOrCreateUidState(@AppIdInt int appId) {
        checkAppId(appId);
        UidPermissionState uidState = mUidStates.get(appId);
        if (uidState == null) {
            uidState = new UidPermissionState();
            mUidStates.put(appId, uidState);
        }
        return uidState;
    }

    @NonNull
    UidPermissionState createUidStateWithExisting(
            @AppIdInt int appId, @NonNull UidPermissionState other) {
        checkAppId(appId);
        UidPermissionState uidState = new UidPermissionState(other);
        mUidStates.put(appId, uidState);
        return uidState;
    }

    public void removeUidState(@AppIdInt int appId) {
        checkAppId(appId);
        mUidStates.delete(appId);
    }

    private void checkAppId(@AppIdInt int appId) {
        if (UserHandle.getUserId(appId) != 0) {
            throw new IllegalArgumentException("Invalid app ID " + appId);
        }
    }
}