Prepare role code for modularization.

- PooledLambda: PoolLambda is replaced with plain lambdas, to avoid
adding ~20 classes to boot classpath (for each module that uses
lambda).

- SystemServiceRegistry: RoleFrameworkInitializer is added to add
ROLE_SERVICE, similar to other modules.

- RoleService: RoleManagerService is renamed to RoleService to better
reflect the manager/service relationship.

- @MainThread: import for Looper is removed because it's only used in
javadoc and triggers package not exist error during build. @linkplain
in the javadoc is also removed because it triggers an error in
Metalava parsing and isn't critical.

- Added small utilities: These utilities has their full version in
platform, but is too large and contains too many internal references
in methods unreferenced by role, whereas actually role only needs 1 or
2 methods from them. So just create a small copy of the used methods
for role modularization, and they will be moved into APEX with role.

Bug: 158736025
Test: manual
Change-Id: I74f20b37d23370e258e7fc7130e28c5312abf46c
This commit is contained in:
Hai Zhang
2021-01-21 17:37:39 -08:00
parent 84705c4ae3
commit 0de31fe1e2
18 changed files with 555 additions and 144 deletions

View File

@@ -21,8 +21,6 @@ import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.os.Looper;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import java.lang.annotation.Target;
@@ -40,8 +38,7 @@ import java.lang.annotation.Target;
* </code> * </code>
* </pre> * </pre>
* *
* @memberDoc This method must be called from the * @memberDoc This method must be called from the main thread of your app.
* {@linkplain Looper#getMainLooper() main thread} of your app.
* @hide * @hide
*/ */
@Retention(SOURCE) @Retention(SOURCE)

View File

@@ -31,7 +31,7 @@ import android.app.contentsuggestions.IContentSuggestionsManager;
import android.app.job.JobSchedulerFrameworkInitializer; import android.app.job.JobSchedulerFrameworkInitializer;
import android.app.people.PeopleManager; import android.app.people.PeopleManager;
import android.app.prediction.AppPredictionManager; import android.app.prediction.AppPredictionManager;
import android.app.role.RoleManager; import android.app.role.RoleFrameworkInitializer;
import android.app.search.SearchUiManager; import android.app.search.SearchUiManager;
import android.app.slice.SliceManager; import android.app.slice.SliceManager;
import android.app.time.TimeManager; import android.app.time.TimeManager;
@@ -1320,14 +1320,6 @@ public final class SystemServiceRegistry {
ctx.getMainThreadHandler()); ctx.getMainThreadHandler());
}}); }});
registerService(Context.ROLE_SERVICE, RoleManager.class,
new CachedServiceFetcher<RoleManager>() {
@Override
public RoleManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
return new RoleManager(ctx.getOuterContext());
}});
registerService(Context.DYNAMIC_SYSTEM_SERVICE, DynamicSystemManager.class, registerService(Context.DYNAMIC_SYSTEM_SERVICE, DynamicSystemManager.class,
new CachedServiceFetcher<DynamicSystemManager>() { new CachedServiceFetcher<DynamicSystemManager>() {
@Override @Override
@@ -1423,6 +1415,7 @@ public final class SystemServiceRegistry {
RollbackManagerFrameworkInitializer.initialize(); RollbackManagerFrameworkInitializer.initialize();
MediaFrameworkPlatformInitializer.registerServiceWrappers(); MediaFrameworkPlatformInitializer.registerServiceWrappers();
MediaFrameworkInitializer.registerServiceWrappers(); MediaFrameworkInitializer.registerServiceWrappers();
RoleFrameworkInitializer.registerServiceWrappers();
} finally { } finally {
// If any of the above code throws, we're in a pretty bad shape and the process // If any of the above code throws, we're in a pretty bad shape and the process
// will likely crash, but we'll reset it just in case there's an exception handler... // will likely crash, but we'll reset it just in case there's an exception handler...

View File

@@ -20,15 +20,15 @@ import android.Manifest;
import android.annotation.CallbackExecutor; import android.annotation.CallbackExecutor;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.RequiresPermission; import android.annotation.RequiresPermission;
import android.app.ActivityThread;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo;
import android.os.Binder; import android.os.Binder;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.os.RemoteCallback; import android.os.RemoteCallback;
import android.util.Log; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
@@ -95,11 +95,10 @@ public class RoleControllerManager {
private RoleControllerManager(@NonNull ComponentName remoteServiceComponentName, private RoleControllerManager(@NonNull ComponentName remoteServiceComponentName,
@NonNull Handler handler, @NonNull Context context) { @NonNull Handler handler, @NonNull Context context) {
synchronized (sRemoteServicesLock) { synchronized (sRemoteServicesLock) {
int userId = context.getUserId(); int userId = context.getUser().getIdentifier();
ServiceConnector<IRoleController> remoteService = sRemoteServices.get(userId); ServiceConnector<IRoleController> remoteService = sRemoteServices.get(userId);
if (remoteService == null) { if (remoteService == null) {
remoteService = new ServiceConnector.Impl<IRoleController>( remoteService = new ServiceConnector.Impl<IRoleController>(context,
ActivityThread.currentApplication(),
new Intent(RoleControllerService.SERVICE_INTERFACE) new Intent(RoleControllerService.SERVICE_INTERFACE)
.setComponent(remoteServiceComponentName), .setComponent(remoteServiceComponentName),
0 /* bindingFlags */, userId, IRoleController.Stub::asInterface) { 0 /* bindingFlags */, userId, IRoleController.Stub::asInterface) {
@@ -119,7 +118,7 @@ public class RoleControllerManager {
* @hide * @hide
*/ */
public RoleControllerManager(@NonNull Context context) { public RoleControllerManager(@NonNull Context context) {
this(getRemoteServiceComponentName(context), context.getMainThreadHandler(), context); this(getRemoteServiceComponentName(context), new Handler(Looper.getMainLooper()), context);
} }
@NonNull @NonNull
@@ -127,8 +126,8 @@ public class RoleControllerManager {
Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE); Intent intent = new Intent(RoleControllerService.SERVICE_INTERFACE);
PackageManager packageManager = context.getPackageManager(); PackageManager packageManager = context.getPackageManager();
intent.setPackage(packageManager.getPermissionControllerPackageName()); intent.setPackage(packageManager.getPermissionControllerPackageName());
ResolveInfo resolveInfo = packageManager.resolveService(intent, 0); ServiceInfo serviceInfo = packageManager.resolveService(intent, 0).serviceInfo;
return resolveInfo.getComponentInfo().getComponentName(); return new ComponentName(serviceInfo.packageName, serviceInfo.name);
} }
/** /**

View File

@@ -33,7 +33,6 @@ import android.os.RemoteCallback;
import android.os.UserHandle; import android.os.UserHandle;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.Objects; import java.util.Objects;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -85,9 +84,7 @@ public abstract class RoleControllerService extends Service {
Objects.requireNonNull(callback, "callback cannot be null"); Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage( mWorkerHandler.post(() -> RoleControllerService.this.grantDefaultRoles(callback));
RoleControllerService::grantDefaultRoles, RoleControllerService.this,
callback));
} }
@Override @Override
@@ -100,9 +97,8 @@ public abstract class RoleControllerService extends Service {
"packageName cannot be null or empty"); "packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null"); Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage( mWorkerHandler.post(() -> RoleControllerService.this.onAddRoleHolder(roleName,
RoleControllerService::onAddRoleHolder, RoleControllerService.this, packageName, flags, callback));
roleName, packageName, flags, callback));
} }
@Override @Override
@@ -115,9 +111,8 @@ public abstract class RoleControllerService extends Service {
"packageName cannot be null or empty"); "packageName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null"); Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage( mWorkerHandler.post(() -> RoleControllerService.this.onRemoveRoleHolder(roleName,
RoleControllerService::onRemoveRoleHolder, RoleControllerService.this, packageName, flags, callback));
roleName, packageName, flags, callback));
} }
@Override @Override
@@ -127,9 +122,8 @@ public abstract class RoleControllerService extends Service {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Objects.requireNonNull(callback, "callback cannot be null"); Objects.requireNonNull(callback, "callback cannot be null");
mWorkerHandler.sendMessage(PooledLambda.obtainMessage( mWorkerHandler.post(() -> RoleControllerService.this.onClearRoleHolders(roleName,
RoleControllerService::onClearRoleHolders, RoleControllerService.this, flags, callback));
roleName, flags, callback));
} }
private void enforceCallerSystemUid(@NonNull String methodName) { private void enforceCallerSystemUid(@NonNull String methodName) {
@@ -274,6 +268,7 @@ public abstract class RoleControllerService extends Service {
* *
* @deprecated Implement {@link #onIsApplicationVisibleForRole(String, String)} instead. * @deprecated Implement {@link #onIsApplicationVisibleForRole(String, String)} instead.
*/ */
@Deprecated
public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName, public abstract boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
@NonNull String packageName); @NonNull String packageName);

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2021 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.app.SystemServiceRegistry;
import android.content.Context;
/**
* Class holding initialization code for role in the permission module.
*
* @hide
*/
//@SystemApi
public class RoleFrameworkInitializer {
private RoleFrameworkInitializer() {}
/**
* Called by {@link SystemServiceRegistry}'s static initializer and registers
* {@link RoleManager} to {@link Context}, so that {@link Context#getSystemService} can return
* it.
*
* <p>If this is called from other places, it throws a {@link IllegalStateException).
*/
public static void registerServiceWrappers() {
SystemServiceRegistry.registerContextAwareService(Context.ROLE_SERVICE, RoleManager.class,
(context, serviceBinder) -> new RoleManager(context,
IRoleManager.Stub.asInterface(serviceBinder)));
}
}

View File

@@ -32,14 +32,12 @@ import android.os.Binder;
import android.os.Process; import android.os.Process;
import android.os.RemoteCallback; import android.os.RemoteCallback;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.UserHandle; import android.os.UserHandle;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.SparseArray; import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import com.android.internal.util.function.pooled.PooledLambda;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
@@ -180,12 +178,16 @@ public final class RoleManager {
private final Object mRoleControllerManagerLock = new Object(); private final Object mRoleControllerManagerLock = new Object();
/** /**
* Create a new instance of this class.
*
* @param context the {@link Context}
* @param service the {@link IRoleManager} service
*
* @hide * @hide
*/ */
public RoleManager(@NonNull Context context) throws ServiceManager.ServiceNotFoundException { public RoleManager(@NonNull Context context, @NonNull IRoleManager service) {
mContext = context; mContext = context;
mService = IRoleManager.Stub.asInterface(ServiceManager.getServiceOrThrow( mService = service;
Context.ROLE_SERVICE));
} }
/** /**
@@ -747,9 +749,8 @@ public final class RoleManager {
public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) { public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
final long token = Binder.clearCallingIdentity(); final long token = Binder.clearCallingIdentity();
try { try {
mExecutor.execute(PooledLambda.obtainRunnable( mExecutor.execute(() ->
OnRoleHoldersChangedListener::onRoleHoldersChanged, mListener, roleName, mListener.onRoleHoldersChanged(roleName, UserHandle.of(userId)));
UserHandle.of(userId)));
} finally { } finally {
Binder.restoreCallingIdentity(token); Binder.restoreCallingIdentity(token);
} }

View File

@@ -9,4 +9,4 @@
] ]
} }
] ]
} }

View File

@@ -18,14 +18,17 @@ package com.android.server.role;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.UserIdInt; import android.annotation.UserIdInt;
import android.util.ArrayMap;
import android.util.ArraySet; import java.util.Map;
import java.util.Set;
/** /**
* Internal calls into {@link RoleManagerService}. * Internal calls into {@link RoleService}.
*
* @hide
*/ */
public abstract class RoleManagerInternal { //@SystemApi(client = SystemApi.Client.SYSTEM_SERVER)
public interface RoleManagerLocal {
/** /**
* Get all roles and their holders. * Get all roles and their holders.
* *
@@ -34,6 +37,5 @@ public abstract class RoleManagerInternal {
* @return The roles and their holders * @return The roles and their holders
*/ */
@NonNull @NonNull
public abstract ArrayMap<String, ArraySet<String>> getRolesAndHolders( Map<String, Set<String>> getRolesAndHolders(@UserIdInt int userId);
@UserIdInt int userId);
} }

View File

@@ -43,25 +43,23 @@ import android.os.RemoteException;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.IndentingPrintWriter; import android.util.IndentingPrintWriter;
import android.util.Slog; import android.util.Log;
import android.util.SparseArray; import android.util.SparseArray;
import android.util.proto.ProtoOutputStream; import android.util.proto.ProtoOutputStream;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.infra.AndroidFuture; import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ThrottledRunnable; import com.android.internal.infra.ThrottledRunnable;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.Preconditions; import com.android.internal.util.Preconditions;
import com.android.internal.util.dump.DualDumpOutputStream; import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.LocalManagerRegistry;
import com.android.server.FgThread;
import com.android.server.LocalServices;
import com.android.server.SystemService; import com.android.server.SystemService;
import com.android.server.pm.UserManagerInternal; import com.android.server.role.compat.UserHandleCompat;
import com.android.server.role.util.ArrayUtils;
import com.android.server.role.util.CollectionUtils;
import com.android.server.role.util.ForegroundThread;
import java.io.FileDescriptor; import java.io.FileDescriptor;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@@ -69,7 +67,9 @@ import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@@ -79,8 +79,8 @@ import java.util.concurrent.TimeoutException;
* *
* @see RoleManager * @see RoleManager
*/ */
public class RoleManagerService extends SystemService implements RoleUserState.Callback { public class RoleService extends SystemService implements RoleUserState.Callback {
private static final String LOG_TAG = RoleManagerService.class.getSimpleName(); private static final String LOG_TAG = RoleService.class.getSimpleName();
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
@@ -89,7 +89,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@NonNull @NonNull
private final AppOpsManager mAppOpsManager; private final AppOpsManager mAppOpsManager;
@NonNull @NonNull
private final UserManagerInternal mUserManagerInternal; private final UserManager mUserManager;
@NonNull @NonNull
private final Object mLock = new Object(); private final Object mLock = new Object();
@@ -120,7 +120,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
new SparseArray<>(); new SparseArray<>();
@NonNull @NonNull
private final Handler mListenerHandler = FgThread.getHandler(); private final Handler mListenerHandler = ForegroundThread.getHandler();
/** /**
* Maps user id to its throttled runnable for granting default roles. * Maps user id to its throttled runnable for granting default roles.
@@ -130,18 +130,17 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
private final SparseArray<ThrottledRunnable> mGrantDefaultRolesThrottledRunnables = private final SparseArray<ThrottledRunnable> mGrantDefaultRolesThrottledRunnables =
new SparseArray<>(); new SparseArray<>();
public RoleManagerService(@NonNull Context context, public RoleService(@NonNull Context context) {
@NonNull RoleServicePlatformHelper platformHelper) {
super(context); super(context);
mPlatformHelper = platformHelper; mPlatformHelper = LocalManagerRegistry.getManager(RoleServicePlatformHelper.class);
RoleControllerManager.initializeRemoteServiceComponentName(context); RoleControllerManager.initializeRemoteServiceComponentName(context);
mAppOpsManager = context.getSystemService(AppOpsManager.class); mAppOpsManager = context.getSystemService(AppOpsManager.class);
mUserManagerInternal = LocalServices.getService(UserManagerInternal.class); mUserManager = context.getSystemService(UserManager.class);
LocalServices.addService(RoleManagerInternal.class, new Internal()); LocalManagerRegistry.addManager(RoleManagerLocal.class, new Local());
registerUserRemovedReceiver(); registerUserRemovedReceiver();
} }
@@ -174,9 +173,9 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
getContext().registerReceiverForAllUsers(new BroadcastReceiver() { getContext().registerReceiverForAllUsers(new BroadcastReceiver() {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, -1)); int userId = UserHandleCompat.getUserId(intent.getIntExtra(Intent.EXTRA_UID, -1));
if (DEBUG) { if (DEBUG) {
Slog.i(LOG_TAG, "Packages changed - re-running initial grants for user " Log.i(LOG_TAG, "Packages changed - re-running initial grants for user "
+ userId); + userId);
} }
if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction()) if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())
@@ -200,7 +199,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
try { try {
future.get(30, TimeUnit.SECONDS); future.get(30, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) { } catch (InterruptedException | ExecutionException | TimeoutException e) {
Slog.e(LOG_TAG, "Failed to grant default roles for user " + userId, e); Log.e(LOG_TAG, "Failed to grant default roles for user " + userId, e);
} }
} }
@@ -209,7 +208,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
synchronized (mLock) { synchronized (mLock) {
runnable = mGrantDefaultRolesThrottledRunnables.get(userId); runnable = mGrantDefaultRolesThrottledRunnables.get(userId);
if (runnable == null) { if (runnable == null) {
runnable = new ThrottledRunnable(FgThread.getHandler(), runnable = new ThrottledRunnable(ForegroundThread.getHandler(),
GRANT_DEFAULT_ROLES_INTERVAL_MILLIS, GRANT_DEFAULT_ROLES_INTERVAL_MILLIS,
() -> maybeGrantDefaultRolesInternal(userId)); () -> maybeGrantDefaultRolesInternal(userId));
mGrantDefaultRolesThrottledRunnables.put(userId, runnable); mGrantDefaultRolesThrottledRunnables.put(userId, runnable);
@@ -226,16 +225,16 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
String newPackagesHash = mPlatformHelper.computePackageStateHash(userId); String newPackagesHash = mPlatformHelper.computePackageStateHash(userId);
if (Objects.equals(oldPackagesHash, newPackagesHash)) { if (Objects.equals(oldPackagesHash, newPackagesHash)) {
if (DEBUG) { if (DEBUG) {
Slog.i(LOG_TAG, "Already granted default roles for packages hash " Log.i(LOG_TAG, "Already granted default roles for packages hash "
+ newPackagesHash); + newPackagesHash);
} }
return AndroidFuture.completedFuture(null); return AndroidFuture.completedFuture(null);
} }
// Some package state has changed, so grant default roles again. // Some package state has changed, so grant default roles again.
Slog.i(LOG_TAG, "Granting default roles..."); Log.i(LOG_TAG, "Granting default roles...");
AndroidFuture<Void> future = new AndroidFuture<>(); AndroidFuture<Void> future = new AndroidFuture<>();
getOrCreateController(userId).grantDefaultRoles(FgThread.getExecutor(), getOrCreateController(userId).grantDefaultRoles(ForegroundThread.getExecutor(),
successful -> { successful -> {
if (successful) { if (successful) {
userState.setPackagesHash(newPackagesHash); userState.setPackagesHash(newPackagesHash);
@@ -273,7 +272,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
throw new RuntimeException(e); throw new RuntimeException(e);
} }
controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName( controller = RoleControllerManager.createWithInitializedRemoteServiceComponentName(
FgThread.getHandler(), context); ForegroundThread.getHandler(), context);
mControllers.put(userId, controller); mControllers.put(userId, controller);
} }
return controller; return controller;
@@ -321,8 +320,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@Override @Override
public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) { public void onRoleHoldersChanged(@NonNull String roleName, @UserIdInt int userId) {
mListenerHandler.sendMessage(PooledLambda.obtainMessage( mListenerHandler.post(() -> notifyRoleHoldersChanged(roleName, userId));
RoleManagerService::notifyRoleHoldersChanged, this, roleName, userId));
} }
@WorkerThread @WorkerThread
@@ -333,7 +331,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
} }
RemoteCallbackList<IOnRoleHoldersChangedListener> allUsersListeners = getListeners( RemoteCallbackList<IOnRoleHoldersChangedListener> allUsersListeners = getListeners(
UserHandle.USER_ALL); UserHandleCompat.USER_ALL);
if (allUsersListeners != null) { if (allUsersListeners != null) {
notifyRoleHoldersChangedForListeners(allUsersListeners, roleName, userId); notifyRoleHoldersChangedForListeners(allUsersListeners, roleName, userId);
} }
@@ -350,7 +348,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
try { try {
listener.onRoleHoldersChanged(roleName, userId); listener.onRoleHoldersChanged(roleName, userId);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.e(LOG_TAG, "Error calling OnRoleHoldersChangedListener", e); Log.e(LOG_TAG, "Error calling OnRoleHoldersChangedListener", e);
} }
} }
} finally { } finally {
@@ -364,7 +362,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
public boolean isRoleAvailable(@NonNull String roleName) { public boolean isRoleAvailable(@NonNull String roleName) {
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
int userId = UserHandle.getUserId(getCallingUid()); int userId = UserHandleCompat.getUserId(getCallingUid());
return getOrCreateUserState(userId).isRoleAvailable(roleName); return getOrCreateUserState(userId).isRoleAvailable(roleName);
} }
@@ -376,7 +374,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
int userId = UserHandle.getUserId(callingUid); int userId = UserHandleCompat.getUserId(callingUid);
ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName); ArraySet<String> roleHolders = getOrCreateUserState(userId).getRoleHolders(roleName);
if (roleHolders == null) { if (roleHolders == null) {
return false; return false;
@@ -387,8 +385,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@NonNull @NonNull
@Override @Override
public List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) { public List<String> getRoleHoldersAsUser(@NonNull String roleName, @UserIdInt int userId) {
if (!mUserManagerInternal.exists(userId)) { if (!isUserExistent(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist"); Log.e(LOG_TAG, "user " + userId + " does not exist");
return Collections.emptyList(); return Collections.emptyList();
} }
enforceCrossUserPermission(userId, false, "getRoleHoldersAsUser"); enforceCrossUserPermission(userId, false, "getRoleHoldersAsUser");
@@ -408,8 +406,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, public void addRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
@NonNull RemoteCallback callback) { @NonNull RemoteCallback callback) {
if (!mUserManagerInternal.exists(userId)) { if (!isUserExistent(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist"); Log.e(LOG_TAG, "user " + userId + " does not exist");
return; return;
} }
enforceCrossUserPermission(userId, false, "addRoleHolderAsUser"); enforceCrossUserPermission(userId, false, "addRoleHolderAsUser");
@@ -428,8 +426,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName, public void removeRoleHolderAsUser(@NonNull String roleName, @NonNull String packageName,
@RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
@NonNull RemoteCallback callback) { @NonNull RemoteCallback callback) {
if (!mUserManagerInternal.exists(userId)) { if (!isUserExistent(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist"); Log.e(LOG_TAG, "user " + userId + " does not exist");
return; return;
} }
enforceCrossUserPermission(userId, false, "removeRoleHolderAsUser"); enforceCrossUserPermission(userId, false, "removeRoleHolderAsUser");
@@ -448,8 +446,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
public void clearRoleHoldersAsUser(@NonNull String roleName, public void clearRoleHoldersAsUser(@NonNull String roleName,
@RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId, @RoleManager.ManageHoldersFlags int flags, @UserIdInt int userId,
@NonNull RemoteCallback callback) { @NonNull RemoteCallback callback) {
if (!mUserManagerInternal.exists(userId)) { if (!isUserExistent(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist"); Log.e(LOG_TAG, "user " + userId + " does not exist");
return; return;
} }
enforceCrossUserPermission(userId, false, "clearRoleHoldersAsUser"); enforceCrossUserPermission(userId, false, "clearRoleHoldersAsUser");
@@ -465,8 +463,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@Override @Override
public void addOnRoleHoldersChangedListenerAsUser( public void addOnRoleHoldersChangedListenerAsUser(
@NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) { @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
if (userId != UserHandle.USER_ALL && !mUserManagerInternal.exists(userId)) { if (userId != UserHandleCompat.USER_ALL && !isUserExistent(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist"); Log.e(LOG_TAG, "user " + userId + " does not exist");
return; return;
} }
enforceCrossUserPermission(userId, true, "addOnRoleHoldersChangedListenerAsUser"); enforceCrossUserPermission(userId, true, "addOnRoleHoldersChangedListenerAsUser");
@@ -483,8 +481,8 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@Override @Override
public void removeOnRoleHoldersChangedListenerAsUser( public void removeOnRoleHoldersChangedListenerAsUser(
@NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) { @NonNull IOnRoleHoldersChangedListener listener, @UserIdInt int userId) {
if (userId != UserHandle.USER_ALL && !mUserManagerInternal.exists(userId)) { if (userId != UserHandleCompat.USER_ALL && !isUserExistent(userId)) {
Slog.e(LOG_TAG, "user " + userId + " does not exist"); Log.e(LOG_TAG, "user " + userId + " does not exist");
return; return;
} }
enforceCrossUserPermission(userId, true, "removeOnRoleHoldersChangedListenerAsUser"); enforceCrossUserPermission(userId, true, "removeOnRoleHoldersChangedListenerAsUser");
@@ -508,7 +506,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
Objects.requireNonNull(roleNames, "roleNames cannot be null"); Objects.requireNonNull(roleNames, "roleNames cannot be null");
int userId = UserHandle.getUserId(Binder.getCallingUid()); int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
getOrCreateUserState(userId).setRoleNames(roleNames); getOrCreateUserState(userId).setRoleNames(roleNames);
} }
@@ -522,7 +520,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
int userId = UserHandle.getUserId(Binder.getCallingUid()); int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
return getOrCreateUserState(userId).addRoleHolder(roleName, packageName); return getOrCreateUserState(userId).addRoleHolder(roleName, packageName);
} }
@@ -536,7 +534,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty"); Preconditions.checkStringNotEmpty(roleName, "roleName cannot be null or empty");
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
int userId = UserHandle.getUserId(Binder.getCallingUid()); int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
return getOrCreateUserState(userId).removeRoleHolder(roleName, packageName); return getOrCreateUserState(userId).removeRoleHolder(roleName, packageName);
} }
@@ -548,24 +546,30 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty"); Preconditions.checkStringNotEmpty(packageName, "packageName cannot be null or empty");
int userId = UserHandle.getUserId(Binder.getCallingUid()); int userId = UserHandleCompat.getUserId(Binder.getCallingUid());
return getOrCreateUserState(userId).getHeldRoles(packageName); return getOrCreateUserState(userId).getHeldRoles(packageName);
} }
private boolean isUserExistent(@UserIdInt int userId) {
// FIXME: This checks whether the user is alive, but we should check for whether the
// user is existent.
return mUserManager.getUserHandles(true).contains(UserHandle.of(userId));
}
private void enforceCrossUserPermission(@UserIdInt int userId, boolean allowAll, private void enforceCrossUserPermission(@UserIdInt int userId, boolean allowAll,
@NonNull String message) { @NonNull String message) {
final int callingUid = Binder.getCallingUid(); final int callingUid = Binder.getCallingUid();
final int callingUserId = UserHandle.getUserId(callingUid); final int callingUserId = UserHandleCompat.getUserId(callingUid);
if (userId == callingUserId) { if (userId == callingUserId) {
return; return;
} }
Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM Preconditions.checkArgument(userId >= UserHandleCompat.USER_SYSTEM
|| (allowAll && userId == UserHandle.USER_ALL), "Invalid user " + userId); || (allowAll && userId == UserHandleCompat.USER_ALL), "Invalid user " + userId);
getContext().enforceCallingOrSelfPermission( getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
if (callingUid == Process.SHELL_UID && userId >= UserHandle.USER_SYSTEM) { if (callingUid == Process.SHELL_UID && userId >= UserHandleCompat.USER_SYSTEM) {
if (mUserManagerInternal.hasUserRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, if (mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_DEBUGGING_FEATURES,
userId)) { UserHandle.of(userId))) {
throw new SecurityException("Shell does not have permission to access user " throw new SecurityException("Shell does not have permission to access user "
+ userId); + userId);
} }
@@ -576,7 +580,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
public int handleShellCommand(@NonNull ParcelFileDescriptor in, public int handleShellCommand(@NonNull ParcelFileDescriptor in,
@NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err,
@NonNull String[] args) { @NonNull String[] args) {
return new RoleManagerShellCommand(this).exec(this, in.getFileDescriptor(), return new RoleShellCommand(this).exec(this, in.getFileDescriptor(),
out.getFileDescriptor(), err.getFileDescriptor(), args); out.getFileDescriptor(), err.getFileDescriptor(), args);
} }
@@ -584,7 +588,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
@Override @Override
public String getBrowserRoleHolder(@UserIdInt int userId) { public String getBrowserRoleHolder(@UserIdInt int userId) {
final int callingUid = Binder.getCallingUid(); final int callingUid = Binder.getCallingUid();
if (UserHandle.getUserId(callingUid) != userId) { if (UserHandleCompat.getUserId(callingUid) != userId) {
getContext().enforceCallingOrSelfPermission( getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
} }
@@ -625,12 +629,12 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
final Context context = getContext(); final Context context = getContext();
context.enforceCallingOrSelfPermission( context.enforceCallingOrSelfPermission(
android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null); android.Manifest.permission.SET_PREFERRED_APPLICATIONS, null);
if (UserHandle.getUserId(Binder.getCallingUid()) != userId) { if (UserHandleCompat.getUserId(Binder.getCallingUid()) != userId) {
context.enforceCallingOrSelfPermission( context.enforceCallingOrSelfPermission(
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
} }
if (!mUserManagerInternal.exists(userId)) { if (!isUserExistent(userId)) {
return false; return false;
} }
@@ -653,7 +657,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
try { try {
future.get(5, TimeUnit.SECONDS); future.get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) { } catch (InterruptedException | ExecutionException | TimeoutException e) {
Slog.e(LOG_TAG, "Exception while setting default browser: " + packageName, e); Log.e(LOG_TAG, "Exception while setting default browser: " + packageName, e);
return false; return false;
} }
} finally { } finally {
@@ -687,7 +691,7 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
dumpOutputStream = new DualDumpOutputStream(new ProtoOutputStream( dumpOutputStream = new DualDumpOutputStream(new ProtoOutputStream(
new FileOutputStream(fd))); new FileOutputStream(fd)));
} else { } else {
fout.println("ROLE MANAGER STATE (dumpsys role):"); fout.println("ROLE STATE (dumpsys role):");
dumpOutputStream = new DualDumpOutputStream(new IndentingPrintWriter(fout, " ")); dumpOutputStream = new DualDumpOutputStream(new IndentingPrintWriter(fout, " "));
} }
@@ -718,11 +722,14 @@ public class RoleManagerService extends SystemService implements RoleUserState.C
} }
} }
private class Internal extends RoleManagerInternal { private class Local implements RoleManagerLocal {
@NonNull @NonNull
@Override @Override
public ArrayMap<String, ArraySet<String>> getRolesAndHolders(@UserIdInt int userId) { public Map<String, Set<String>> getRolesAndHolders(@UserIdInt int userId) {
return getOrCreateUserState(userId).getRolesAndHolders(); // Convert ArrayMap<String, ArraySet<String>> to Map<String, Set<String>> for the API.
//noinspection unchecked
return (Map<String, Set<String>>) (Map<String, ?>)
getOrCreateUserState(userId).getRolesAndHolders();
} }
} }
} }

View File

@@ -21,19 +21,19 @@ import android.annotation.Nullable;
import android.app.role.IRoleManager; import android.app.role.IRoleManager;
import android.os.RemoteCallback; import android.os.RemoteCallback;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.UserHandle;
import com.android.modules.utils.BasicShellCommandHandler; import com.android.modules.utils.BasicShellCommandHandler;
import com.android.server.role.compat.UserHandleCompat;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
class RoleManagerShellCommand extends BasicShellCommandHandler { class RoleShellCommand extends BasicShellCommandHandler {
@NonNull @NonNull
private final IRoleManager mRoleManager; private final IRoleManager mRoleManager;
RoleManagerShellCommand(@NonNull IRoleManager roleManager) { RoleShellCommand(@NonNull IRoleManager roleManager) {
mRoleManager = roleManager; mRoleManager = roleManager;
} }
@@ -86,7 +86,7 @@ class RoleManagerShellCommand extends BasicShellCommandHandler {
} }
private int getUserIdMaybe() { private int getUserIdMaybe() {
int userId = UserHandle.USER_SYSTEM; int userId = UserHandleCompat.USER_SYSTEM;
String option = getNextOption(); String option = getNextOption();
if (option != null && option.equals("--user")) { if (option != null && option.equals("--user")) {
userId = Integer.parseInt(getNextArgRequired()); userId = Integer.parseInt(getNextArgRequired());
@@ -139,7 +139,7 @@ class RoleManagerShellCommand extends BasicShellCommandHandler {
@Override @Override
public void onHelp() { public void onHelp() {
PrintWriter pw = getOutPrintWriter(); PrintWriter pw = getOutPrintWriter();
pw.println("Role manager (role) commands:"); pw.println("Role (role) commands:");
pw.println(" help or -h"); pw.println(" help or -h");
pw.println(" Print this help text."); pw.println(" Print this help text.");
pw.println(); pw.println();

View File

@@ -25,15 +25,14 @@ import android.os.Handler;
import android.os.UserHandle; import android.os.UserHandle;
import android.util.ArrayMap; import android.util.ArrayMap;
import android.util.ArraySet; import android.util.ArraySet;
import android.util.Slog; import android.util.Log;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.dump.DualDumpOutputStream; import com.android.internal.util.dump.DualDumpOutputStream;
import com.android.internal.util.function.pooled.PooledLambda;
import com.android.role.persistence.RolesPersistence; import com.android.role.persistence.RolesPersistence;
import com.android.role.persistence.RolesState; import com.android.role.persistence.RolesState;
import com.android.server.role.util.BackgroundThread;
import com.android.server.role.util.CollectionUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@@ -197,7 +196,7 @@ class RoleUserState {
synchronized (mLock) { synchronized (mLock) {
if (!mRoles.containsKey(roleName)) { if (!mRoles.containsKey(roleName)) {
mRoles.put(roleName, new ArraySet<>()); mRoles.put(roleName, new ArraySet<>());
Slog.i(LOG_TAG, "Added new role: " + roleName); Log.i(LOG_TAG, "Added new role: " + roleName);
scheduleWriteFileLocked(); scheduleWriteFileLocked();
return true; return true;
} else { } else {
@@ -221,7 +220,7 @@ class RoleUserState {
if (!roleNames.contains(roleName)) { if (!roleNames.contains(roleName)) {
ArraySet<String> packageNames = mRoles.valueAt(i); ArraySet<String> packageNames = mRoles.valueAt(i);
if (!packageNames.isEmpty()) { if (!packageNames.isEmpty()) {
Slog.e(LOG_TAG, "Holders of a removed role should have been cleaned up," Log.e(LOG_TAG, "Holders of a removed role should have been cleaned up,"
+ " role: " + roleName + ", holders: " + packageNames); + " role: " + roleName + ", holders: " + packageNames);
} }
mRoles.removeAt(i); mRoles.removeAt(i);
@@ -255,7 +254,7 @@ class RoleUserState {
synchronized (mLock) { synchronized (mLock) {
ArraySet<String> roleHolders = mRoles.get(roleName); ArraySet<String> roleHolders = mRoles.get(roleName);
if (roleHolders == null) { if (roleHolders == null) {
Slog.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName Log.e(LOG_TAG, "Cannot add role holder for unknown role, role: " + roleName
+ ", package: " + packageName); + ", package: " + packageName);
return false; return false;
} }
@@ -286,7 +285,7 @@ class RoleUserState {
synchronized (mLock) { synchronized (mLock) {
ArraySet<String> roleHolders = mRoles.get(roleName); ArraySet<String> roleHolders = mRoles.get(roleName);
if (roleHolders == null) { if (roleHolders == null) {
Slog.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName Log.e(LOG_TAG, "Cannot remove role holder for unknown role, role: " + roleName
+ ", package: " + packageName); + ", package: " + packageName);
return false; return false;
} }
@@ -330,8 +329,7 @@ class RoleUserState {
} }
if (!mWriteScheduled) { if (!mWriteScheduled) {
mWriteHandler.sendMessageDelayed(PooledLambda.obtainMessage(RoleUserState::writeFile, mWriteHandler.postDelayed(this::writeFile, WRITE_DELAY_MILLIS);
this), WRITE_DELAY_MILLIS);
mWriteScheduled = true; mWriteScheduled = true;
} }
} }

View File

@@ -0,0 +1,48 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.role.compat;
import android.annotation.UserIdInt;
import android.os.UserHandle;
/**
* Helper for accessing features in {@link UserHandle}.
*/
public final class UserHandleCompat {
/**
* A user ID to indicate all users on the device.
*/
public static final int USER_ALL = UserHandle.ALL.getIdentifier();
/**
* A user ID to indicate the "system" user of the device.
*/
public static final int USER_SYSTEM = UserHandle.SYSTEM.getIdentifier();
private UserHandleCompat() {}
/**
* Get the user ID of a given UID.
*
* @param uid the UID
* @return the user ID
*/
@UserIdInt
public static int getUserId(int uid) {
return UserHandle.getUserHandleForUid(uid).getIdentifier();
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.role.util;
import android.annotation.Nullable;
import java.util.Objects;
/**
* Array utilities.
*/
public final class ArrayUtils {
private ArrayUtils() {}
/**
* @see java.util.List#contains(Object)
*/
public static <T> boolean contains(@Nullable T[] array, T value) {
return indexOf(array, value) != -1;
}
/**
* Get the first element of an array, or {@code null} if none.
*
* @param array the array
* @param <T> the type of the elements of the array
* @return first element of an array, or {@code null} if none
*/
public static <T> T firstOrNull(@Nullable T[] array) {
return !isEmpty(array) ? array[0] : null;
}
/**
* @see java.util.List#indexOf(Object)
*/
public static <T> int indexOf(@Nullable T[] array, T value) {
if (array == null) {
return -1;
}
final int length = array.length;
for (int i = 0; i < length; i++) {
final T element = array[i];
if (Objects.equals(element, value)) {
return i;
}
}
return -1;
}
/**
* @see java.util.List#isEmpty()
*/
public static <T> boolean isEmpty(@Nullable T[] array) {
return array == null || array.length == 0;
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.role.util;
import android.annotation.NonNull;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import com.android.internal.annotations.GuardedBy;
import java.util.concurrent.Executor;
/**
* Shared singleton background thread.
*/
public class BackgroundThread extends HandlerThread {
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static BackgroundThread sInstance;
@GuardedBy("sLock")
private static Handler sHandler;
@GuardedBy("sLock")
private static Executor sExecutor;
private BackgroundThread() {
super(BackgroundThread.class.getName());
}
@GuardedBy("sLock")
private static void ensureInstanceLocked() {
if (sInstance == null) {
sInstance = new BackgroundThread();
sInstance.start();
sHandler = new Handler(sInstance.getLooper());
sExecutor = new HandlerExecutor(sHandler);
}
}
/**
* Get the singleton instance of thi class.
*
* @return the singleton instance of thi class
*/
@NonNull
public static BackgroundThread get() {
synchronized (sLock) {
ensureInstanceLocked();
return sInstance;
}
}
/**
* Get the {@link Handler} for this thread.
*
* @return the {@link Handler} for this thread.
*/
@NonNull
public static Handler getHandler() {
synchronized (sLock) {
ensureInstanceLocked();
return sHandler;
}
}
/**
* Get the {@link Executor} for this thread.
*
* @return the {@link Executor} for this thread.
*/
@NonNull
public static Executor getExecutor() {
synchronized (sLock) {
ensureInstanceLocked();
return sExecutor;
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.role.util;
import android.annotation.Nullable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
/**
* {@link Collection} utilities.
*/
public class CollectionUtils {
private CollectionUtils() {}
/**
* Get the first element of a {@link List}, or {@code null} if none.
*
* @param list the {@link List}, or {@code null}
* @param <E> the element type of the {@link List}
* @return the first element of the {@link List}, or {@code 0} if none
*/
@Nullable
public static <E> E firstOrNull(@Nullable List<E> list) {
return !isEmpty(list) ? list.get(0) : null;
}
/**
* Check whether a {@link Collection} is empty or {@code null}.
*
* @param collection the {@link Collection}, or {@code null}
* @return whether the {@link Collection} is empty or {@code null}
*/
public static boolean isEmpty(@Nullable Collection<?> collection) {
return collection == null || collection.isEmpty();
}
/**
* Get the size of a {@link Collection}, or {@code 0} if {@code null}.
*
* @param collection the {@link Collection}, or {@code null}
* @return the size of the {@link Collection}, or {@code 0} if {@code null}
*/
public static int size(@Nullable Collection<?> collection) {
return collection != null ? collection.size() : 0;
}
/**
* Get the size of a {@link Map}, or {@code 0} if {@code null}.
*
* @param collection the {@link Map}, or {@code null}
* @return the size of the {@link Map}, or {@code 0} if {@code null}
*/
public static int size(@Nullable Map<?, ?> collection) {
return collection != null ? collection.size() : 0;
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.role.util;
import android.annotation.NonNull;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.HandlerThread;
import com.android.internal.annotations.GuardedBy;
import java.util.concurrent.Executor;
/**
* Shared singleton foreground thread.
*/
public class ForegroundThread extends HandlerThread {
private static final Object sLock = new Object();
@GuardedBy("sLock")
private static ForegroundThread sInstance;
@GuardedBy("sLock")
private static Handler sHandler;
@GuardedBy("sLock")
private static Executor sExecutor;
private ForegroundThread() {
super(ForegroundThread.class.getName());
}
@GuardedBy("sLock")
private static void ensureInstanceLocked() {
if (sInstance == null) {
sInstance = new ForegroundThread();
sInstance.start();
sHandler = new Handler(sInstance.getLooper());
sExecutor = new HandlerExecutor(sHandler);
}
}
/**
* Get the singleton instance of thi class.
*
* @return the singleton instance of thi class
*/
@NonNull
public static ForegroundThread get() {
synchronized (sLock) {
ensureInstanceLocked();
return sInstance;
}
}
/**
* Get the {@link Handler} for this thread.
*
* @return the {@link Handler} for this thread.
*/
@NonNull
public static Handler getHandler() {
synchronized (sLock) {
ensureInstanceLocked();
return sHandler;
}
}
/**
* Get the {@link Executor} for this thread.
*
* @return the {@link Executor} for this thread.
*/
@NonNull
public static Executor getExecutor() {
synchronized (sLock) {
ensureInstanceLocked();
return sExecutor;
}
}
}

View File

@@ -156,12 +156,13 @@ import com.android.internal.util.CollectionUtils;
import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.FrameworkStatsLog;
import com.android.server.BatteryService; import com.android.server.BatteryService;
import com.android.server.BinderCallsStatsService; import com.android.server.BinderCallsStatsService;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices; import com.android.server.LocalServices;
import com.android.server.SystemService; import com.android.server.SystemService;
import com.android.server.SystemServiceManager; import com.android.server.SystemServiceManager;
import com.android.server.am.MemoryStatUtil.MemoryStat; import com.android.server.am.MemoryStatUtil.MemoryStat;
import com.android.server.notification.NotificationManagerService; import com.android.server.notification.NotificationManagerService;
import com.android.server.role.RoleManagerInternal; import com.android.server.role.RoleManagerLocal;
import com.android.server.stats.pull.IonMemoryUtil.IonAllocations; import com.android.server.stats.pull.IonMemoryUtil.IonAllocations;
import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot; import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
import com.android.server.stats.pull.netstats.NetworkStatsExt; import com.android.server.stats.pull.netstats.NetworkStatsExt;
@@ -2916,7 +2917,8 @@ public class StatsPullAtomService extends SystemService {
final long callingToken = Binder.clearCallingIdentity(); final long callingToken = Binder.clearCallingIdentity();
try { try {
PackageManager pm = mContext.getPackageManager(); PackageManager pm = mContext.getPackageManager();
RoleManagerInternal rmi = LocalServices.getService(RoleManagerInternal.class); RoleManagerLocal roleManagerLocal = LocalManagerRegistry.getManager(
RoleManagerLocal.class);
List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers(); List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers();
@@ -2924,27 +2926,23 @@ public class StatsPullAtomService extends SystemService {
for (int userNum = 0; userNum < numUsers; userNum++) { for (int userNum = 0; userNum < numUsers; userNum++) {
int userId = users.get(userNum).getUserHandle().getIdentifier(); int userId = users.get(userNum).getUserHandle().getIdentifier();
ArrayMap<String, ArraySet<String>> roles = rmi.getRolesAndHolders(userId); Map<String, Set<String>> roles = roleManagerLocal.getRolesAndHolders(userId);
int numRoles = roles.size(); for (Map.Entry<String, Set<String>> roleEntry : roles.entrySet()) {
for (int roleNum = 0; roleNum < numRoles; roleNum++) { String roleName = roleEntry.getKey();
String roleName = roles.keyAt(roleNum); Set<String> packageNames = roleEntry.getValue();
ArraySet<String> holders = roles.valueAt(roleNum);
int numHolders = holders.size();
for (int holderNum = 0; holderNum < numHolders; holderNum++) {
String holderName = holders.valueAt(holderNum);
for (String packageName : packageNames) {
PackageInfo pkg; PackageInfo pkg;
try { try {
pkg = pm.getPackageInfoAsUser(holderName, 0, userId); pkg = pm.getPackageInfoAsUser(packageName, 0, userId);
} catch (PackageManager.NameNotFoundException e) { } catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Role holder " + holderName + " not found"); Slog.w(TAG, "Role holder " + packageName + " not found");
return StatsManager.PULL_SKIP; return StatsManager.PULL_SKIP;
} }
pulledData.add(FrameworkStatsLog.buildStatsEvent( pulledData.add(FrameworkStatsLog.buildStatsEvent(
atomTag, pkg.applicationInfo.uid, holderName, roleName)); atomTag, pkg.applicationInfo.uid, packageName, roleName));
} }
} }
} }

View File

@@ -168,7 +168,7 @@ import com.android.server.powerstats.PowerStatsService;
import com.android.server.profcollect.ProfcollectForwardingService; import com.android.server.profcollect.ProfcollectForwardingService;
import com.android.server.recoverysystem.RecoverySystemService; import com.android.server.recoverysystem.RecoverySystemService;
import com.android.server.restrictions.RestrictionsManagerService; import com.android.server.restrictions.RestrictionsManagerService;
import com.android.server.role.RoleManagerService; import com.android.server.role.RoleServicePlatformHelper;
import com.android.server.security.FileIntegrityService; import com.android.server.security.FileIntegrityService;
import com.android.server.security.KeyAttestationApplicationIdProviderService; import com.android.server.security.KeyAttestationApplicationIdProviderService;
import com.android.server.security.KeyChainSystemService; import com.android.server.security.KeyChainSystemService;
@@ -353,6 +353,7 @@ public final class SystemServer implements Dumpable {
"com.android.server.ConnectivityServiceInitializer"; "com.android.server.ConnectivityServiceInitializer";
private static final String IP_CONNECTIVITY_METRICS_CLASS = private static final String IP_CONNECTIVITY_METRICS_CLASS =
"com.android.server.connectivity.IpConnectivityMetrics"; "com.android.server.connectivity.IpConnectivityMetrics";
private static final String ROLE_SERVICE_CLASS = "com.android.server.role.RoleService";
private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector"; private static final String TETHERING_CONNECTOR_CLASS = "android.net.ITetheringConnector";
@@ -2032,8 +2033,9 @@ public final class SystemServer implements Dumpable {
// Grants default permissions and defines roles // Grants default permissions and defines roles
t.traceBegin("StartRoleManagerService"); t.traceBegin("StartRoleManagerService");
mSystemServiceManager.startService(new RoleManagerService( LocalManagerRegistry.addManager(RoleServicePlatformHelper.class,
mSystemContext, new RoleServicePlatformHelperImpl(mSystemContext))); new RoleServicePlatformHelperImpl(mSystemContext));
mSystemServiceManager.startService(ROLE_SERVICE_CLASS);
t.traceEnd(); t.traceEnd();
// We need to always start this service, regardless of whether the // We need to always start this service, regardless of whether the