Merge "Add listeners to observe role holders changes."

This commit is contained in:
Hai Zhang
2018-12-11 22:13:53 +00:00
committed by Android (Google) Code Review
9 changed files with 377 additions and 37 deletions

View File

@@ -104,6 +104,7 @@ java_defaults {
"core/java/android/app/backup/IRestoreObserver.aidl",
"core/java/android/app/backup/IRestoreSession.aidl",
"core/java/android/app/backup/ISelectBackupTransportCallback.aidl",
"core/java/android/app/role/IOnRoleHoldersChangedListener.aidl",
"core/java/android/app/role/IRoleManager.aidl",
"core/java/android/app/role/IRoleManagerCallback.aidl",
"core/java/android/app/slice/ISliceManager.aidl",

View File

@@ -136,6 +136,7 @@ package android {
field public static final java.lang.String NOTIFICATION_DURING_SETUP = "android.permission.NOTIFICATION_DURING_SETUP";
field public static final java.lang.String NOTIFY_TV_INPUTS = "android.permission.NOTIFY_TV_INPUTS";
field public static final java.lang.String OBSERVE_APP_USAGE = "android.permission.OBSERVE_APP_USAGE";
field public static final java.lang.String OBSERVE_ROLE_HOLDERS = "android.permission.OBSERVE_ROLE_HOLDERS";
field public static final java.lang.String OVERRIDE_WIFI_CONFIG = "android.permission.OVERRIDE_WIFI_CONFIG";
field public static final java.lang.String PACKAGE_USAGE_STATS = "android.permission.PACKAGE_USAGE_STATS";
field public static final java.lang.String PACKAGE_VERIFICATION_AGENT = "android.permission.PACKAGE_VERIFICATION_AGENT";
@@ -888,12 +889,18 @@ package android.app.job {
package android.app.role {
public abstract interface OnRoleHoldersChangedListener {
method public abstract void onRoleHoldersChanged(java.lang.String, android.os.UserHandle);
}
public final class RoleManager {
method public void addOnRoleHoldersChangedListenerAsUser(java.util.concurrent.Executor, android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle);
method public void addRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean addRoleHolderFromController(java.lang.String, java.lang.String);
method public void clearRoleHoldersAsUser(java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public java.util.List<java.lang.String> getRoleHolders(java.lang.String);
method public java.util.List<java.lang.String> getRoleHoldersAsUser(java.lang.String, android.os.UserHandle);
method public void removeOnRoleHoldersChangedListenerAsUser(android.app.role.OnRoleHoldersChangedListener, android.os.UserHandle);
method public void removeRoleHolderAsUser(java.lang.String, java.lang.String, android.os.UserHandle, java.util.concurrent.Executor, android.app.role.RoleManagerCallback);
method public boolean removeRoleHolderFromController(java.lang.String, java.lang.String);
method public void setRoleNamesFromController(java.util.List<java.lang.String>);

View File

@@ -0,0 +1,25 @@
/*
* Copyright (C) 2018 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 android.app.role;
/**
* @hide
*/
oneway interface IOnRoleHoldersChangedListener {
void onRoleHoldersChanged(String roleName, int userId);
}

View File

@@ -16,6 +16,7 @@
package android.app.role;
import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManagerCallback;
/**
@@ -37,6 +38,11 @@ interface IRoleManager {
void clearRoleHoldersAsUser(in String roleName, int userId, in IRoleManagerCallback callback);
void addOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener, int userId);
void removeOnRoleHoldersChangedListenerAsUser(IOnRoleHoldersChangedListener listener,
int userId);
void setRoleNamesFromController(in List<String> roleNames);
boolean addRoleHolderFromController(in String roleName, in String packageName);

View File

@@ -0,0 +1,38 @@
/*
* Copyright (C) 2018 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 android.app.role;
import android.annotation.NonNull;
import android.annotation.SystemApi;
import android.os.UserHandle;
/**
* Listener for role holder changes.
*
* @hide
*/
@SystemApi
public interface OnRoleHoldersChangedListener {
/**
* Called when the holders of roles are changed.
*
* @param roleName the name of the role whose holders are changed
* @param user the user for this role holder change
*/
void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user);
}

View File

@@ -23,6 +23,7 @@ import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
@@ -30,8 +31,12 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.List;
import java.util.concurrent.Executor;
@@ -125,6 +130,13 @@ public final class RoleManager {
@NonNull
private final IRoleManager mService;
@GuardedBy("mListenersLock")
@NonNull
private final SparseArray<ArrayMap<OnRoleHoldersChangedListener,
OnRoleHoldersChangedListenerDelegate>> mListeners = new SparseArray<>();
@NonNull
private final Object mListenersLock = new Object();
/**
* @hide
*/
@@ -146,8 +158,6 @@ public final class RoleManager {
* @param roleName the name of requested role
*
* @return the {@code Intent} to prompt user to grant the role
*
* @throws IllegalArgumentException if {@code role} is {@code null} or empty
*/
@NonNull
public Intent createRequestRoleIntent(@NonNull String roleName) {
@@ -164,8 +174,6 @@ public final class RoleManager {
* @param roleName the name of role to checking for
*
* @return whether the role is available in the system
*
* @throws IllegalArgumentException if the role name is {@code null} or empty
*/
public boolean isRoleAvailable(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -182,8 +190,6 @@ public final class RoleManager {
* @param roleName the name of the role to check for
*
* @return whether the calling application is holding the role
*
* @throws IllegalArgumentException if the role name is {@code null} or empty.
*/
public boolean isRoleHeld(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
@@ -204,8 +210,6 @@ public final class RoleManager {
*
* @return a list of package names of the role holders, or an empty list if none.
*
* @throws IllegalArgumentException if the role name is {@code null} or empty.
*
* @see #getRoleHoldersAsUser(String, UserHandle)
*
* @hide
@@ -230,8 +234,6 @@ public final class RoleManager {
*
* @return a list of package names of the role holders, or an empty list if none.
*
* @throws IllegalArgumentException if the role name is {@code null} or empty.
*
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -266,8 +268,6 @@ public final class RoleManager {
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
*
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -306,8 +306,6 @@ public final class RoleManager {
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
*
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #clearRoleHoldersAsUser(String, UserHandle, Executor, RoleManagerCallback)
@@ -345,8 +343,6 @@ public final class RoleManager {
* @param executor the {@code Executor} to run the callback on.
* @param callback the callback for whether this call is successful
*
* @throws IllegalArgumentException if the role name is {@code null} or empty.
*
* @see #getRoleHoldersAsUser(String, UserHandle)
* @see #addRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
* @see #removeRoleHolderAsUser(String, String, UserHandle, Executor, RoleManagerCallback)
@@ -370,6 +366,96 @@ public final class RoleManager {
}
}
/**
* Add a listener to observe role holder changes
* <p>
* <strong>Note:</strong> Using this API requires holding
* {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
* {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
*
* @param executor the {@code Executor} to call the listener on.
* @param listener the listener to be added
* @param user the user to add the listener for
*
* @see #removeOnRoleHoldersChangedListenerAsUser(OnRoleHoldersChangedListener, UserHandle)
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
@SystemApi
public void addOnRoleHoldersChangedListenerAsUser(@CallbackExecutor @NonNull Executor executor,
@NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
Preconditions.checkNotNull(executor, "executor cannot be null");
Preconditions.checkNotNull(listener, "listener cannot be null");
Preconditions.checkNotNull(user, "user cannot be null");
int userId = user.getIdentifier();
synchronized (mListenersLock) {
ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
mListeners.get(userId);
if (listeners == null) {
listeners = new ArrayMap<>();
mListeners.put(userId, listeners);
} else {
if (listeners.containsKey(listener)) {
return;
}
}
OnRoleHoldersChangedListenerDelegate listenerDelegate =
new OnRoleHoldersChangedListenerDelegate(executor, listener);
try {
mService.addOnRoleHoldersChangedListenerAsUser(listenerDelegate, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
listeners.put(listener, listenerDelegate);
}
}
/**
* Remove a listener observing role holder changes
* <p>
* <strong>Note:</strong> Using this API requires holding
* {@code android.permission.OBSERVE_ROLE_HOLDERS} and if the user id is not the current user
* {@code android.permission.INTERACT_ACROSS_USERS_FULL}.
*
* @param listener the listener to be removed
* @param user the user to remove the listener for
*
* @see #addOnRoleHoldersChangedListenerAsUser(Executor, OnRoleHoldersChangedListener,
* UserHandle)
*
* @hide
*/
@RequiresPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS)
@SystemApi
public void removeOnRoleHoldersChangedListenerAsUser(
@NonNull OnRoleHoldersChangedListener listener, @NonNull UserHandle user) {
Preconditions.checkNotNull(listener, "listener cannot be null");
Preconditions.checkNotNull(user, "user cannot be null");
int userId = user.getIdentifier();
synchronized (mListenersLock) {
ArrayMap<OnRoleHoldersChangedListener, OnRoleHoldersChangedListenerDelegate> listeners =
mListeners.get(userId);
if (listeners == null) {
return;
}
OnRoleHoldersChangedListenerDelegate listenerDelegate = listeners.get(listener);
if (listenerDelegate == null) {
return;
}
try {
mService.removeOnRoleHoldersChangedListenerAsUser(listenerDelegate,
user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
listeners.remove(listener);
if (listeners.isEmpty()) {
mListeners.remove(userId);
}
}
}
/**
* Set the names of all the available roles. Should only be called from
* {@link android.rolecontrollerservice.RoleControllerService}.
@@ -379,8 +465,6 @@ public final class RoleManager {
*
* @param roleNames the names of all the available roles
*
* @throws IllegalArgumentException if the list of role names is {@code null}.
*
* @hide
*/
@RequiresPermission(PERMISSION_MANAGE_ROLES_FROM_CONTROLLER)
@@ -408,8 +492,6 @@ public final class RoleManager {
* @return whether the operation was successful, and will also be {@code true} if a matching
* role holder is already found.
*
* @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
*
* @see #getRoleHolders(String)
* @see #removeRoleHolderFromController(String, String)
*
@@ -442,8 +524,6 @@ public final class RoleManager {
* @return whether the operation was successful, and will also be {@code true} if no matching
* role holder was found to remove.
*
* @throws IllegalArgumentException if the role name or package name is {@code null} or empty.
*
* @see #getRoleHolders(String)
* @see #addRoleHolderFromController(String, String)
*
@@ -495,4 +575,31 @@ public final class RoleManager {
}
}
}
private static class OnRoleHoldersChangedListenerDelegate
extends IOnRoleHoldersChangedListener.Stub {
@NonNull
private final Executor mExecutor;
@NonNull
private final OnRoleHoldersChangedListener mListener;
OnRoleHoldersChangedListenerDelegate(@NonNull Executor executor,
@NonNull OnRoleHoldersChangedListener listener) {
mExecutor = executor;
mListener = listener;
}
@Override
public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
long token = Binder.clearCallingIdentity();
try {
mExecutor.execute(PooledLambda.obtainRunnable(
OnRoleHoldersChangedListener::onRoleHoldersChanged, mListener, roleName,
UserHandle.of(userId)));
} finally {
Binder.restoreCallingIdentity(token);
}
}
}
}

View File

@@ -3350,6 +3350,11 @@
<permission android:name="android.permission.MANAGE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
<!-- @SystemApi Allows an application to observe role holder changes.
@hide -->
<permission android:name="android.permission.OBSERVE_ROLE_HOLDERS"
android:protectionLevel="signature|installer" />
<!-- @SystemApi Allows an application to use SurfaceFlinger's low level features.
<p>Not for use by third-party applications.
@hide

View File

@@ -22,8 +22,10 @@ import android.annotation.MainThread;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.app.AppOpsManager;
import android.app.role.IOnRoleHoldersChangedListener;
import android.app.role.IRoleManager;
import android.app.role.IRoleManagerCallback;
import android.app.role.RoleManager;
@@ -33,6 +35,9 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManagerInternal;
import android.content.pm.Signature;
import android.os.Handler;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ShellCallback;
import android.os.UserHandle;
@@ -53,6 +58,8 @@ import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -73,7 +80,7 @@ import java.util.concurrent.TimeoutException;
*
* @see RoleManager
*/
public class RoleManagerService extends SystemService {
public class RoleManagerService extends SystemService implements RoleUserState.Callback {
private static final String LOG_TAG = RoleManagerService.class.getSimpleName();
@@ -100,6 +107,17 @@ public class RoleManagerService extends SystemService {
private final SparseArray<RemoteRoleControllerService> mControllerServices =
new SparseArray<>();
/**
* Maps user id to its list of listeners.
*/
@GuardedBy("mLock")
@NonNull
private final SparseArray<RemoteCallbackList<IOnRoleHoldersChangedListener>> mListeners =
new SparseArray<>();
@NonNull
private final Handler mListenerHandler = FgThread.getHandler();
public RoleManagerService(@NonNull Context context) {
super(context);
@@ -188,7 +206,7 @@ public class RoleManagerService extends SystemService {
}
@Nullable
private String computeComponentStateHash(@UserIdInt int userId) {
private static String computeComponentStateHash(@UserIdInt int userId) {
PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class);
ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -223,7 +241,7 @@ public class RoleManagerService extends SystemService {
synchronized (mLock) {
RoleUserState userState = mUserStates.get(userId);
if (userState == null) {
userState = new RoleUserState(userId);
userState = new RoleUserState(userId, this);
mUserStates.put(userId, userState);
}
return userState;
@@ -242,17 +260,70 @@ public class RoleManagerService extends SystemService {
}
}
@Nullable
private RemoteCallbackList<IOnRoleHoldersChangedListener> getListeners(@UserIdInt int userId) {
synchronized (mLock) {
return mListeners.get(userId);
}
}
@NonNull
private RemoteCallbackList<IOnRoleHoldersChangedListener> getOrCreateListeners(
@UserIdInt int userId) {
synchronized (mLock) {
RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = mListeners.get(userId);
if (listeners == null) {
listeners = new RemoteCallbackList<>();
mListeners.put(userId, listeners);
}
return listeners;
}
}
private void onRemoveUser(@UserIdInt int userId) {
RemoteCallbackList<IOnRoleHoldersChangedListener> listeners;
RoleUserState userState;
synchronized (mLock) {
listeners = mListeners.removeReturnOld(userId);
mControllerServices.remove(userId);
userState = mUserStates.removeReturnOld(userId);
}
if (listeners != null) {
listeners.kill();
}
if (userState != null) {
userState.destroy();
}
}
@Override
public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
mListenerHandler.sendMessage(PooledLambda.obtainMessage(
RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId));
}
@WorkerThread
private void notifyRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
if (listeners == null) {
return;
}
int broadcastCount = listeners.beginBroadcast();
try {
for (int i = 0; i < broadcastCount; i++) {
IOnRoleHoldersChangedListener listener = listeners.getBroadcastItem(i);
try {
listener.onRoleHoldersChanged(roleName, userId);
} catch (RemoteException e) {
Slog.e(LOG_TAG, "Error calling OnRoleHoldersChangedListener", e);
}
}
} finally {
listeners.finishBroadcast();
}
}
private class Stub extends IRoleManager.Stub {
@Override
@@ -356,6 +427,42 @@ public class RoleManagerService extends SystemService {
getOrCreateControllerService(userId).onClearRoleHolders(roleName, callback);
}
@Override
public void addOnRoleHoldersChangedListenerAsUser(
@NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
Preconditions.checkNotNull(listener, "listener cannot be null");
if (!mUserManagerInternal.exists(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist");
return;
}
userId = handleIncomingUser(userId, "addOnRoleHoldersChangedListenerAsUser");
getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
"addOnRoleHoldersChangedListenerAsUser");
RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getOrCreateListeners(
userId);
listeners.register(listener);
}
@Override
public void removeOnRoleHoldersChangedListenerAsUser(
@NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
Preconditions.checkNotNull(listener, "listener cannot be null");
if (!mUserManagerInternal.exists(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist");
return;
}
userId = handleIncomingUser(userId, "removeOnRoleHoldersChangedListenerAsUser");
getContext().enforceCallingOrSelfPermission(Manifest.permission.OBSERVE_ROLE_HOLDERS,
"removeOnRoleHoldersChangedListenerAsUser");
RemoteCallbackList<IOnRoleHoldersChangedListener> listeners = getListeners(userId);
if (listener == null) {
return;
}
listeners.unregister(listener);
}
@Override
public void setRoleNamesFromController(@NonNull List<String> roleNames) {
Preconditions.checkNotNull(roleNames, "roleNames cannot be null");

View File

@@ -73,6 +73,9 @@ public class RoleUserState {
@UserIdInt
private final int mUserId;
@NonNull
private final Callback mCallback;
@NonNull
private final Object mLock = new Object();
@@ -100,12 +103,14 @@ public class RoleUserState {
private final Handler mWriteHandler = new Handler(BackgroundThread.getHandler().getLooper());
/**
* Create a new instance of user state, and read its state from disk if previously persisted.
* Create a new user state, and read its state from disk if previously persisted.
*
* @param userId the user id for the new user state
* @param userId the user id for this user state
* @param callback the callback for this user state
*/
public RoleUserState(@UserIdInt int userId) {
public RoleUserState(@UserIdInt int userId, @NonNull Callback callback) {
mUserId = userId;
mCallback = callback;
readFile();
}
@@ -116,6 +121,7 @@ public class RoleUserState {
public int getVersion() {
synchronized (mLock) {
throwIfDestroyedLocked();
return mVersion;
}
}
@@ -128,6 +134,7 @@ public class RoleUserState {
public void setVersion(int version) {
synchronized (mLock) {
throwIfDestroyedLocked();
if (mVersion == version) {
return;
}
@@ -156,6 +163,7 @@ public class RoleUserState {
public void setPackagesHash(@Nullable String packagesHash) {
synchronized (mLock) {
throwIfDestroyedLocked();
if (Objects.equals(mPackagesHash, packagesHash)) {
return;
}
@@ -174,6 +182,7 @@ public class RoleUserState {
public boolean isRoleAvailable(@NonNull String roleName) {
synchronized (mLock) {
throwIfDestroyedLocked();
return mRoles.containsKey(roleName);
}
}
@@ -189,6 +198,7 @@ public class RoleUserState {
public ArraySet<String> getRoleHolders(@NonNull String roleName) {
synchronized (mLock) {
throwIfDestroyedLocked();
return new ArraySet<>(mRoles.get(roleName));
}
}
@@ -201,29 +211,34 @@ public class RoleUserState {
public void setRoleNames(@NonNull List<String> roleNames) {
synchronized (mLock) {
throwIfDestroyedLocked();
boolean changed = false;
for (int i = mRoles.size() - 1; i >= 0; i--) {
String roleName = mRoles.keyAt(i);
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: "
+ roleName + ", holders: " + packageNames);
Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up,"
+ " role: " + roleName + ", holders: " + packageNames);
}
mRoles.removeAt(i);
changed = true;
}
}
int roleNamesSize = roleNames.size();
for (int i = 0; i < roleNamesSize; i++) {
String roleName = roleNames.get(i);
if (!mRoles.containsKey(roleName)) {
mRoles.put(roleName, new ArraySet<>());
Slog.i(LOG_TAG, "Added new role: " + roleName);
changed = true;
}
}
if (changed) {
scheduleWriteFileLocked();
}
@@ -241,20 +256,27 @@ public class RoleUserState {
*/
@CheckResult
public boolean addRoleHolder(@NonNull String roleName, @NonNull String packageName) {
boolean changed;
synchronized (mLock) {
throwIfDestroyedLocked();
ArraySet<String> roleHolders = mRoles.get(roleName);
if (roleHolders == null) {
Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
+ ", package: " + packageName);
return false;
}
boolean changed = roleHolders.add(packageName);
changed = roleHolders.add(packageName);
if (changed) {
scheduleWriteFileLocked();
}
return true;
}
if (changed) {
mCallback.onRoleHoldersChanged(roleName, mUserId);
}
return true;
}
/**
@@ -268,20 +290,28 @@ public class RoleUserState {
*/
@CheckResult
public boolean removeRoleHolder(@NonNull String roleName, @NonNull String packageName) {
boolean changed;
synchronized (mLock) {
throwIfDestroyedLocked();
ArraySet<String> roleHolders = mRoles.get(roleName);
if (roleHolders == null) {
Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
+ ", package: " + packageName);
return false;
}
boolean changed = roleHolders.remove(packageName);
changed = roleHolders.remove(packageName);
if (changed) {
scheduleWriteFileLocked();
}
return true;
}
if (changed) {
mCallback.onRoleHoldersChanged(roleName, mUserId);
}
return true;
}
/**
@@ -520,8 +550,8 @@ public class RoleUserState {
}
/**
* 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.
* Destroy this user 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.
*/
public void destroy() {
synchronized (mLock) {
@@ -542,4 +572,18 @@ public class RoleUserState {
private static @NonNull File getFile(@UserIdInt int userId) {
return new File(Environment.getUserSystemDirectory(userId), ROLES_FILE_NAME);
}
/**
* Callback for a user state.
*/
public interface Callback {
/**
* Called when the holders of roles are changed.
*
* @param roleName the name of the role whose holders are changed
* @param userId the user id for this role holder change
*/
void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId);
}
}