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

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

Merge "Make RoleUserState use its own lock."

parents c3effa7b cdc85c59
Loading
Loading
Loading
Loading
+45 −61
Original line number Diff line number Diff line
@@ -156,21 +156,16 @@ public class RoleManagerService extends SystemService {
    @MainThread
    private void performInitialGrantsIfNecessary(@UserIdInt int userId) {
        RoleUserState userState;
        synchronized (mLock) {
            userState = getUserStateLocked(userId);
        }
        userState = getOrCreateUserState(userId);
        String packagesHash = computeComponentStateHash(userId);
        String oldPackagesHash;
        synchronized (mLock) {
            oldPackagesHash = userState.getPackagesHashLocked();
        }
        String oldPackagesHash = userState.getPackagesHash();
        boolean needGrant = !Objects.equals(packagesHash, oldPackagesHash);
        if (needGrant) {
            // Some vital packages state has changed since last role grant
            // Run grants again
            Slog.i(LOG_TAG, "Granting default permissions...");
            CompletableFuture<Void> result = new CompletableFuture<>();
            getControllerService(userId).onGrantDefaultRoles(
            getOrCreateControllerService(userId).onGrantDefaultRoles(
                    new IRoleManagerCallback.Stub() {
                        @Override
                        public void onSuccess() {
@@ -183,9 +178,7 @@ public class RoleManagerService extends SystemService {
                    });
            try {
                result.get(5, TimeUnit.SECONDS);
                synchronized (mLock) {
                    userState.setPackagesHashLocked(packagesHash);
                }
                userState.setPackagesHash(packagesHash);
            } catch (InterruptedException | ExecutionException | TimeoutException e) {
                Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e);
            }
@@ -225,20 +218,21 @@ public class RoleManagerService extends SystemService {
        return PackageUtils.computeSha256Digest(out.toByteArray());
    }

    @GuardedBy("mLock")
    @NonNull
    private RoleUserState getUserStateLocked(@UserIdInt int userId) {
    private RoleUserState getOrCreateUserState(@UserIdInt int userId) {
        synchronized (mLock) {
            RoleUserState userState = mUserStates.get(userId);
            if (userState == null) {
            userState = RoleUserState.newInstanceLocked(userId);
                userState = new RoleUserState(userId);
                mUserStates.put(userId, userState);
            }
            return userState;
        }
    }

    @GuardedBy("mLock")
    @NonNull
    private RemoteRoleControllerService getControllerService(@UserIdInt int userId) {
    private RemoteRoleControllerService getOrCreateControllerService(@UserIdInt int userId) {
        synchronized (mLock) {
            RemoteRoleControllerService controllerService = mControllerServices.get(userId);
            if (controllerService == null) {
                controllerService = new RemoteRoleControllerService(userId, getContext());
@@ -246,14 +240,16 @@ public class RoleManagerService extends SystemService {
            }
            return controllerService;
        }
    }

    private void onRemoveUser(@UserIdInt int userId) {
        RoleUserState userState;
        synchronized (mLock) {
            mControllerServices.remove(userId);
            RoleUserState userState = mUserStates.removeReturnOld(userId);
            if (userState != null) {
                userState.destroyLocked();
            userState = mUserStates.removeReturnOld(userId);
        }
        if (userState != null) {
            userState.destroy();
        }
    }

@@ -264,10 +260,8 @@ public class RoleManagerService extends SystemService {
            Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");

            int userId = UserHandle.getUserId(getCallingUid());
            synchronized (mLock) {
                RoleUserState userState = getUserStateLocked(userId);
                return userState.isRoleAvailableLocked(roleName);
            }
            RoleUserState userState = getOrCreateUserState(userId);
            return userState.isRoleAvailable(roleName);
        }

        @Override
@@ -307,10 +301,8 @@ public class RoleManagerService extends SystemService {
        @Nullable
        private ArraySet<String> getRoleHoldersInternal(@NonNull String roleName,
                @UserIdInt int userId) {
            synchronized (mLock) {
                RoleUserState userState = getUserStateLocked(userId);
                return userState.getRoleHoldersLocked(roleName);
            }
            RoleUserState userState = getOrCreateUserState(userId);
            return userState.getRoleHolders(roleName);
        }

        @Override
@@ -327,7 +319,7 @@ public class RoleManagerService extends SystemService {
            getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
                    "addRoleHolderAsUser");

            getControllerService(userId).onAddRoleHolder(roleName, packageName, callback);
            getOrCreateControllerService(userId).onAddRoleHolder(roleName, packageName, callback);
        }

        @Override
@@ -344,7 +336,7 @@ public class RoleManagerService extends SystemService {
            getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
                    "removeRoleHolderAsUser");

            getControllerService(userId).onRemoveRoleHolder(roleName, packageName,
            getOrCreateControllerService(userId).onRemoveRoleHolder(roleName, packageName,
                    callback);
        }

@@ -361,7 +353,7 @@ public class RoleManagerService extends SystemService {
            getContext().enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ROLE_HOLDERS,
                    "clearRoleHoldersAsUser");

            getControllerService(userId).onClearRoleHolders(roleName, callback);
            getOrCreateControllerService(userId).onClearRoleHolders(roleName, callback);
        }

        @Override
@@ -372,10 +364,8 @@ public class RoleManagerService extends SystemService {
                    "setRoleNamesFromController");

            int userId = UserHandle.getCallingUserId();
            synchronized (mLock) {
                RoleUserState userState = getUserStateLocked(userId);
                userState.setRoleNamesLocked(roleNames);
            }
            RoleUserState userState = getOrCreateUserState(userId);
            userState.setRoleNames(roleNames);
        }

        @Override
@@ -388,10 +378,8 @@ public class RoleManagerService extends SystemService {
                    "addRoleHolderFromController");

            int userId = UserHandle.getCallingUserId();
            synchronized (mLock) {
                RoleUserState userState = getUserStateLocked(userId);
                return userState.addRoleHolderLocked(roleName, packageName);
            }
            RoleUserState userState = getOrCreateUserState(userId);
            return userState.addRoleHolder(roleName, packageName);
        }

        @Override
@@ -404,10 +392,8 @@ public class RoleManagerService extends SystemService {
                    "removeRoleHolderFromController");

            int userId = UserHandle.getCallingUserId();
            synchronized (mLock) {
                RoleUserState userState = getUserStateLocked(userId);
                return userState.removeRoleHolderLocked(roleName, packageName);
            }
            RoleUserState userState = getOrCreateUserState(userId);
            return userState.removeRoleHolder(roleName, packageName);
        }

        @CheckResult
@@ -440,17 +426,15 @@ public class RoleManagerService extends SystemService {
                dumpOutputStream = new DualDumpOutputStream(new IndentingPrintWriter(fout, "  "));
            }

            synchronized (mLock) {
            int[] userIds = mUserManagerInternal.getUserIds();
            int userIdsLength = userIds.length;
            for (int i = 0; i < userIdsLength; i++) {
                int userId = userIds[i];

                    RoleUserState userState = getUserStateLocked(userId);
                    userState.dumpLocked(dumpOutputStream, "user_states",
                RoleUserState userState = getOrCreateUserState(userId);
                userState.dump(dumpOutputStream, "user_states",
                        RoleManagerServiceDumpProto.USER_STATES);
            }
            }

            dumpOutputStream.flush();
        }
+171 −138
Original line number Diff line number Diff line
@@ -74,69 +74,67 @@ public class RoleUserState {
    @UserIdInt
    private final int mUserId;

    @GuardedBy("RoleManagerService.mLock")
    @NonNull
    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private int mVersion = VERSION_UNDEFINED;

    @GuardedBy("RoleManagerService.mLock")
    @GuardedBy("mLock")
    @Nullable
    private String mPackagesHash;

    /**
     * Maps role names to its holders' package names. The values should never be null.
     */
    @GuardedBy("RoleManagerService.mLock")
    @GuardedBy("mLock")
    @NonNull
    private ArrayMap<String, ArraySet<String>> mRoles = new ArrayMap<>();

    @GuardedBy("RoleManagerService.mLock")
    @GuardedBy("mLock")
    private long mWritePendingSinceMillis;

    @GuardedBy("RoleManagerService.mLock")
    @GuardedBy("mLock")
    private boolean mDestroyed;

    @NonNull
    private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());

    private RoleUserState(@UserIdInt int userId) {
        mUserId = userId;

        readLocked();
    }

    /**
     * Create a new instance of user state, and read its state from disk if previously persisted.
     *
     * @param userId the user id for the new user state
     *
     * @return the new user state
     */
    @GuardedBy("RoleManagerService.mLock")
    public static RoleUserState newInstanceLocked(@UserIdInt int userId) {
        return new RoleUserState(userId);
    public RoleUserState(@UserIdInt int userId) {
        mUserId = userId;

        readFile();
    }

    /**
     * Get the version of this user state.
     */
    @GuardedBy("RoleManagerService.mLock")
    public int getVersionLocked() {
    public int getVersion() {
        synchronized (mLock) {
            throwIfDestroyedLocked();
            return mVersion;
        }
    }

    /**
     * Set the version of this user state.
     *
     * @param version the version to set
     */
    @GuardedBy("RoleManagerService.mLock")
    public void setVersionLocked(int version) {
    public void setVersion(int version) {
        synchronized (mLock) {
            throwIfDestroyedLocked();
            if (mVersion == version) {
                return;
            }
            mVersion = version;
        writeAsyncLocked();
            scheduleWriteFileLocked();
        }
    }

    /**
@@ -144,24 +142,27 @@ public class RoleUserState {
     *
     * @return the hash representing the state of packages
     */
    @GuardedBy("RoleManagerService.mLock")
    public String getPackagesHashLocked() {
    @Nullable
    public String getPackagesHash() {
        synchronized (mLock) {
            return mPackagesHash;
        }
    }

    /**
     * Set the hash representing the state of packages during the last time initial grants was run.
     *
     * @param packagesHash the hash representing the state of packages
     */
    @GuardedBy("RoleManagerService.mLock")
    public void setPackagesHashLocked(@Nullable String packagesHash) {
    public void setPackagesHash(@Nullable String packagesHash) {
        synchronized (mLock) {
            throwIfDestroyedLocked();
            if (Objects.equals(mPackagesHash, packagesHash)) {
                return;
            }
            mPackagesHash = packagesHash;
        writeAsyncLocked();
            scheduleWriteFileLocked();
        }
    }

    /**
@@ -171,11 +172,12 @@ public class RoleUserState {
     *
     * @return whether the role is available
     */
    @GuardedBy("RoleManagerService.mLock")
    public boolean isRoleAvailableLocked(@NonNull String roleName) {
    public boolean isRoleAvailable(@NonNull String roleName) {
        synchronized (mLock) {
            throwIfDestroyedLocked();
            return mRoles.containsKey(roleName);
        }
    }

    /**
     * Get the holders of a role.
@@ -184,11 +186,12 @@ public class RoleUserState {
     *
     * @return the set of role holders. {@code null} should not be returned and indicates an issue.
     */
    @GuardedBy("RoleManagerService.mLock")
    @Nullable
    public ArraySet<String> getRoleHoldersLocked(@NonNull String roleName) {
    public ArraySet<String> getRoleHolders(@NonNull String roleName) {
        synchronized (mLock) {
            throwIfDestroyedLocked();
        return mRoles.get(roleName);
            return new ArraySet<>(mRoles.get(roleName));
        }
    }

    /**
@@ -196,8 +199,8 @@ public class RoleUserState {
     *
     * @param roleNames the names of all the available roles
     */
    @GuardedBy("RoleManagerService.mLock")
    public void setRoleNamesLocked(@NonNull List<String> roleNames) {
    public void setRoleNames(@NonNull List<String> roleNames) {
        synchronized (mLock) {
            throwIfDestroyedLocked();
            boolean changed = false;
            for (int i = mRoles.size() - 1; i >= 0; i--) {
@@ -205,7 +208,8 @@ public class RoleUserState {
                if (!roleNames.contains(roleName)) {
                    ArraySet<String> packageNames = mRoles.valueAt(i);
                    if (!packageNames.isEmpty()) {
                    Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up, role: "
                        Slog.e(LOG_TAG,
                                "Holders of a removed role should have been cleaned up, role: "
                                        + roleName + ", holders: " + packageNames);
                    }
                    mRoles.removeAt(i);
@@ -222,7 +226,8 @@ public class RoleUserState {
                }
            }
            if (changed) {
            writeAsyncLocked();
                scheduleWriteFileLocked();
            }
        }
    }

@@ -236,8 +241,8 @@ public class RoleUserState {
     *         indicates an issue.
     */
    @CheckResult
    @GuardedBy("RoleManagerService.mLock")
    public boolean addRoleHolderLocked(@NonNull String roleName, @NonNull String packageName) {
    public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
        synchronized (mLock) {
            throwIfDestroyedLocked();
            ArraySet<String> roleHolders = mRoles.get(roleName);
            if (roleHolders == null) {
@@ -247,10 +252,11 @@ public class RoleUserState {
            }
            boolean changed = roleHolders.add(packageName);
            if (changed) {
            writeAsyncLocked();
                scheduleWriteFileLocked();
            }
            return true;
        }
    }

    /**
     * Remove a holder from a role.
@@ -262,8 +268,8 @@ public class RoleUserState {
     *         indicates an issue.
     */
    @CheckResult
    @GuardedBy("RoleManagerService.mLock")
    public boolean removeRoleHolderLocked(@NonNull String roleName, @NonNull String packageName) {
    public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
        synchronized (mLock) {
            throwIfDestroyedLocked();
            ArraySet<String> roleHolders = mRoles.get(roleName);
            if (roleHolders == null) {
@@ -273,27 +279,19 @@ public class RoleUserState {
            }
            boolean changed = roleHolders.remove(packageName);
            if (changed) {
            writeAsyncLocked();
                scheduleWriteFileLocked();
            }
            return true;
        }
    }

    /**
     * Schedule writing the state to file.
     */
    @GuardedBy("RoleManagerService.mLock")
    private void writeAsyncLocked() {
    @GuardedBy("mLock")
    private void scheduleWriteFileLocked() {
        throwIfDestroyedLocked();

        ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
        for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
            String roleName = mRoles.keyAt(i);
            ArraySet<String> roleHolders = mRoles.valueAt(i);

            roleHolders = new ArraySet<>(roleHolders);
            roles.put(roleName, roleHolders);
        }

        long currentTimeMillis = System.currentTimeMillis();
        long writeDelayMillis;
        if (!mWriteHandler.hasMessagesOrCallbacks()) {
@@ -311,14 +309,26 @@ public class RoleUserState {
            }
        }

        mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeSync, this,
                mVersion, mPackagesHash, roles), writeDelayMillis);
        mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeFile, this),
                writeDelayMillis);
        Slog.i(LOG_TAG, "Scheduled writing roles.xml");
    }

    @WorkerThread
    private void writeSync(int version, @Nullable String packagesHash,
            @NonNull ArrayMap<String, ArraySet<String>> roles) {
    private void writeFile() {
        int version;
        String packagesHash;
        ArrayMap<String, ArraySet<String>> roles;
        synchronized (mLock) {
            if (mDestroyed) {
                return;
            }

            version = mVersion;
            packagesHash = mPackagesHash;
            roles = snapshotRolesLocked();
        }

        AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId);
        FileOutputStream out = null;
        try {
@@ -385,8 +395,8 @@ public class RoleUserState {
    /**
     * Read the state from file.
     */
    @GuardedBy("RoleManagerService.mLock")
    private void readLocked() {
    private void readFile() {
        synchronized (mLock) {
            File file = getFile(mUserId);
            try (FileInputStream in = new AtomicFile(file).openRead()) {
                XmlPullParser parser = Xml.newPullParser();
@@ -399,6 +409,7 @@ public class RoleUserState {
                throw new IllegalStateException("Failed to parse roles.xml: " + file, e);
            }
        }
    }

    private void parseXmlLocked(@NonNull XmlPullParser parser) throws IOException,
            XmlPullParserException {
@@ -470,20 +481,28 @@ public class RoleUserState {
     *
     * @param dumpOutputStream the output stream to dump to
     */
    @GuardedBy("RoleManagerService.mLock")
    public void dumpLocked(@NonNull DualDumpOutputStream dumpOutputStream,
            @NonNull String fieldName, long fieldId) {
    public void dump(@NonNull DualDumpOutputStream dumpOutputStream, @NonNull String fieldName,
            long fieldId) {
        int version;
        String packagesHash;
        ArrayMap<String, ArraySet<String>> roles;
        synchronized (mLock) {
            throwIfDestroyedLocked();

            version = mVersion;
            packagesHash = mPackagesHash;
            roles = snapshotRolesLocked();
        }

        long fieldToken = dumpOutputStream.start(fieldName, fieldId);
        dumpOutputStream.write("user_id", RoleUserStateProto.USER_ID, mUserId);
        dumpOutputStream.write("version", RoleUserStateProto.VERSION, mVersion);
        dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, mPackagesHash);
        dumpOutputStream.write("version", RoleUserStateProto.VERSION, version);
        dumpOutputStream.write("packages_hash", RoleUserStateProto.PACKAGES_HASH, packagesHash);

        int rolesSize = mRoles.size();
        int rolesSize = roles.size();
        for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
            String roleName = mRoles.keyAt(rolesIndex);
            ArraySet<String> roleHolders = mRoles.valueAt(rolesIndex);
            String roleName = roles.keyAt(rolesIndex);
            ArraySet<String> roleHolders = roles.valueAt(rolesIndex);

            long rolesToken = dumpOutputStream.start("roles", RoleUserStateProto.ROLES);
            dumpOutputStream.write("name", RoleProto.NAME, roleName);
@@ -501,19 +520,33 @@ public class RoleUserState {
        dumpOutputStream.end(fieldToken);
    }

    @GuardedBy("mLock")
    private ArrayMap<String, ArraySet<String>> snapshotRolesLocked() {
        ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>();
        for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) {
            String roleName = mRoles.keyAt(i);
            ArraySet<String> roleHolders = mRoles.valueAt(i);

            roleHolders = new ArraySet<>(roleHolders);
            roles.put(roleName, roleHolders);
        }
        return roles;
    }

    /**
     * Destroy this state and delete the corresponding file. Any pending writes to the file will be
     * cancelled and any future interaction with this state will throw an exception.
     */
    @GuardedBy("RoleManagerService.mLock")
    public void destroyLocked() {
    public void destroy() {
        synchronized (mLock) {
            throwIfDestroyedLocked();
            mWriteHandler.removeCallbacksAndMessages(null);
            getFile(mUserId).delete();
            mDestroyed = true;
        }
    }

    @GuardedBy("RoleManagerService.mLock")
    @GuardedBy("mLock")
    private void throwIfDestroyedLocked() {
        if (mDestroyed) {
            throw new IllegalStateException("This RoleUserState has already been destroyed");