Add listeners to observe role holders changes.
This change adds the ability to add listeners to observe role holder changes. This will be used by the new role management UI and other system components that used to put the default app in settings and observe settings change. Bug: 110557011 Test: manual Change-Id: I2a8eb39220081e3be801adb970b60c55ebc297c7
This commit is contained in:
parent
833960a416
commit
31d06ba6b3
@ -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",
|
||||
|
@ -134,6 +134,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";
|
||||
@ -884,12 +885,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3344,6 +3344,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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user