Merge "Add listeners to observe role holders changes."
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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>);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
38
core/java/android/app/role/OnRoleHoldersChangedListener.java
Normal file
38
core/java/android/app/role/OnRoleHoldersChangedListener.java
Normal 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user