summaryrefslogtreecommitdiff
path: root/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java')
-rw-r--r--PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java491
1 files changed, 491 insertions, 0 deletions
diff --git a/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java
new file mode 100644
index 000000000..2a6010c4d
--- /dev/null
+++ b/PermissionController/role-controller/java/com/android/role/controller/service/RoleControllerServiceImpl.java
@@ -0,0 +1,491 @@
+/*
+ * 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 com.android.role.controller.service;
+
+import android.app.role.RoleControllerService;
+import android.app.role.RoleManager;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.WorkerThread;
+
+import com.android.role.controller.model.Role;
+import com.android.role.controller.model.Roles;
+import com.android.role.controller.util.CollectionUtils;
+import com.android.role.controller.util.LegacyRoleFallbackEnabledUtils;
+import com.android.role.controller.util.PackageUtils;
+import com.android.role.controller.util.UserUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Implementation of {@link RoleControllerService}.
+ */
+public class RoleControllerServiceImpl extends RoleControllerService {
+
+ private static final String LOG_TAG = RoleControllerServiceImpl.class.getSimpleName();
+
+ private static final boolean DEBUG = false;
+
+
+ private UserHandle mUser;
+ private Context mContext;
+ private RoleManager mUserRoleManager;
+
+ public RoleControllerServiceImpl() {}
+
+ public RoleControllerServiceImpl(@NonNull UserHandle user, @NonNull Context context) {
+ init(user, context);
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+
+ init(Process.myUserHandle(), this);
+ }
+
+ private void init(@NonNull UserHandle user, @NonNull Context context) {
+ mUser = user;
+ mContext = context;
+ Context userContext = UserUtils.getUserContext(context, user);
+ mUserRoleManager = userContext.getSystemService(RoleManager.class);
+ }
+
+ @Override
+ @WorkerThread
+ public boolean onGrantDefaultRoles() {
+ if (DEBUG) {
+ Log.i(LOG_TAG, "Granting default roles, user: " + mUser.myUserId());
+ }
+
+ // Gather the available roles for current user.
+ ArrayMap<String, Role> roleMap = Roles.get(mContext);
+ List<Role> roles = new ArrayList<>();
+ List<String> roleNames = new ArrayList<>();
+ ArraySet<String> addedRoleNames = new ArraySet<>();
+ int roleMapSize = roleMap.size();
+ for (int i = 0; i < roleMapSize; i++) {
+ Role role = roleMap.valueAt(i);
+
+ if (!role.isAvailableAsUser(mUser, mContext)) {
+ continue;
+ }
+ roles.add(role);
+ String roleName = role.getName();
+ roleNames.add(roleName);
+ if (!mUserRoleManager.isRoleAvailable(roleName)) {
+ addedRoleNames.add(roleName);
+ }
+ }
+
+ // TODO: Clean up holders of roles that will be removed.
+
+ // Set the available role names in RoleManager.
+ mUserRoleManager.setRoleNamesFromController(roleNames);
+
+ int addedRoleNamesSize = addedRoleNames.size();
+ for (int i = 0; i < addedRoleNamesSize; i++) {
+ String roleName = addedRoleNames.valueAt(i);
+
+ Role role = roleMap.get(roleName);
+ role.onRoleAddedAsUser(mUser, mContext);
+ }
+
+ // Go through the holders of all roles.
+ int rolesSize = roles.size();
+ for (int rolesIndex = 0; rolesIndex < rolesSize; rolesIndex++) {
+ Role role = roles.get(rolesIndex);
+
+ String roleName = role.getName();
+
+ // For each of the current holders, check if it is still qualified, redo grant if so, or
+ // remove it otherwise.
+ List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
+ int currentPackageNamesSize = currentPackageNames.size();
+ for (int currentPackageNamesIndex = 0;
+ currentPackageNamesIndex < currentPackageNamesSize;
+ currentPackageNamesIndex++) {
+ String packageName = currentPackageNames.get(currentPackageNamesIndex);
+
+ if (role.isPackageQualifiedAsUser(packageName, mUser, mContext)) {
+ // We should not override user set or fixed permissions because we are only
+ // redoing the grant here. Otherwise, user won't be able to revoke permissions
+ // granted by role.
+ addRoleHolderInternal(role, packageName, false, false, true);
+ } else {
+ Log.i(LOG_TAG, "Removing package that no longer qualifies for the role,"
+ + " package: " + packageName + ", role: " + roleName);
+ removeRoleHolderInternal(role, packageName, false);
+ }
+ }
+
+ // If there is no holder for a role now, or the role is static, we need to add default
+ // or fallback holders, if any.
+ currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
+ currentPackageNamesSize = currentPackageNames.size();
+ boolean isStaticRole = role.isStatic();
+ if (currentPackageNamesSize == 0 || isStaticRole) {
+ List<String> packageNamesToAdd = null;
+ if (addedRoleNames.contains(roleName) || isStaticRole) {
+ packageNamesToAdd = role.getDefaultHoldersAsUser(mUser, mContext);
+ }
+ if (packageNamesToAdd == null || packageNamesToAdd.isEmpty()) {
+ packageNamesToAdd = CollectionUtils.singletonOrEmpty(
+ role.getFallbackHolderAsUser(mUser, mContext));
+ }
+
+ int packageNamesToAddSize = packageNamesToAdd.size();
+ for (int packageNamesToAddIndex = 0; packageNamesToAddIndex < packageNamesToAddSize;
+ packageNamesToAddIndex++) {
+ String packageName = packageNamesToAdd.get(packageNamesToAddIndex);
+
+ if (currentPackageNames.contains(packageName)) {
+ // This may happen when we are ensuring all default holders are added for
+ // static roles.
+ continue;
+ }
+ if (!role.isPackageQualifiedAsUser(packageName, mUser, mContext)) {
+ Log.e(LOG_TAG, "Default/fallback role holder package doesn't qualify for"
+ + " the role, package: " + packageName + ", role: " + roleName);
+ continue;
+ }
+ Log.i(LOG_TAG, "Adding package as default/fallback role holder, package: "
+ + packageName + ", role: " + roleName);
+ // TODO: If we don't override user here, user might end up missing incoming
+ // phone calls or SMS, so we just keep the old behavior. But overriding user
+ // choice about permission without explicit user action is bad, so maybe we
+ // should at least show a notification?
+ addRoleHolderInternal(role, packageName, role.shouldOverrideUserWhenGranting());
+ }
+ }
+
+ // Ensure that an exclusive role has at most one holder.
+ currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
+ currentPackageNamesSize = currentPackageNames.size();
+ if (role.isExclusive() && currentPackageNamesSize > 1) {
+ Log.w(LOG_TAG, "Multiple packages holding an exclusive role, role: "
+ + roleName);
+ // No good way to determine who should be the only one, just keep the first one.
+ for (int currentPackageNamesIndex = 1;
+ currentPackageNamesIndex < currentPackageNamesSize;
+ currentPackageNamesIndex++) {
+ String packageName = currentPackageNames.get(currentPackageNamesIndex);
+
+ Log.i(LOG_TAG, "Removing extraneous package for an exclusive role, package: "
+ + packageName + ", role: " + roleName);
+ removeRoleHolderInternal(role, packageName, false);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ @WorkerThread
+ public boolean onAddRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ int flags) {
+ if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) {
+ return false;
+ }
+
+ Role role = Roles.get(mContext).get(roleName);
+ if (role == null) {
+ Log.e(LOG_TAG, "Unknown role: " + roleName);
+ return false;
+ }
+ if (!role.isAvailableAsUser(mUser, mContext)) {
+ Log.e(LOG_TAG, "Role is unavailable: " + roleName);
+ return false;
+ }
+
+ if (!role.isPackageQualifiedAsUser(packageName, mUser, mContext)) {
+ Log.e(LOG_TAG, "Package does not qualify for the role, package: " + packageName
+ + ", role: " + roleName);
+ return false;
+ }
+
+ boolean added = false;
+ if (role.isExclusive()) {
+ List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
+ int currentPackageNamesSize = currentPackageNames.size();
+ for (int i = 0; i < currentPackageNamesSize; i++) {
+ String currentPackageName = currentPackageNames.get(i);
+
+ if (Objects.equals(currentPackageName, packageName)) {
+ Log.i(LOG_TAG, "Package is already a role holder, package: " + packageName
+ + ", role: " + roleName);
+ added = true;
+ continue;
+ }
+
+ boolean removed = removeRoleHolderInternal(role, currentPackageName, false);
+ if (!removed) {
+ // TODO: Clean up?
+ return false;
+ }
+ }
+ }
+
+ boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
+ added = addRoleHolderInternal(role, packageName, dontKillApp,
+ role.shouldOverrideUserWhenGranting(), added);
+ if (!added) {
+ return false;
+ }
+
+ role.onHolderAddedAsUser(packageName, mUser, mContext);
+ role.onHolderChangedAsUser(mUser, mContext);
+
+ return true;
+ }
+
+ @Override
+ @WorkerThread
+ public boolean onRemoveRoleHolder(@NonNull String roleName, @NonNull String packageName,
+ int flags) {
+ if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) {
+ return false;
+ }
+
+ Role role = Roles.get(mContext).get(roleName);
+ if (role == null) {
+ Log.e(LOG_TAG, "Unknown role: " + roleName);
+ return false;
+ }
+ if (!role.isAvailableAsUser(mUser, mContext)) {
+ Log.e(LOG_TAG, "Role is unavailable: " + roleName);
+ return false;
+ }
+
+ boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
+ boolean removed = removeRoleHolderInternal(role, packageName, dontKillApp);
+ if (!removed) {
+ return false;
+ }
+
+ // TODO: Should we consider this successful regardless?
+ boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role);
+ if (!fallbackSuccessful) {
+ return false;
+ }
+
+ role.onHolderChangedAsUser(mUser, mContext);
+
+ return true;
+ }
+
+ @Override
+ @WorkerThread
+ public boolean onClearRoleHolders(@NonNull String roleName, int flags) {
+ if (!checkFlags(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP)) {
+ return false;
+ }
+
+ Role role = Roles.get(mContext).get(roleName);
+ if (role == null) {
+ Log.e(LOG_TAG, "Unknown role: " + roleName);
+ return false;
+ }
+ if (!role.isAvailableAsUser(mUser, mContext)) {
+ Log.e(LOG_TAG, "Role is unavailable: " + roleName);
+ return false;
+ }
+
+ boolean dontKillApp = hasFlag(flags, RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP);
+ boolean cleared = clearRoleHoldersInternal(role, dontKillApp);
+ if (!cleared) {
+ return false;
+ }
+
+ // TODO: Should we consider this successful regardless?
+ boolean fallbackSuccessful = addFallbackRoleHolderMaybe(role);
+ if (!fallbackSuccessful) {
+ return false;
+ }
+
+ role.onHolderChangedAsUser(mUser, mContext);
+
+ return true;
+ }
+
+ @WorkerThread
+ private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
+ boolean overrideUser) {
+ return addRoleHolderInternal(role, packageName, false, overrideUser, false);
+ }
+
+ @WorkerThread
+ private boolean addRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
+ boolean dontKillApp, boolean overrideUser, boolean added) {
+ role.grantAsUser(packageName, dontKillApp, overrideUser, mUser, mContext);
+
+ String roleName = role.getName();
+ if (!added) {
+ added = mUserRoleManager.addRoleHolderFromController(roleName, packageName);
+ }
+ if (!added) {
+ Log.e(LOG_TAG, "Failed to add role holder in RoleManager, package: " + packageName
+ + ", role: " + roleName);
+ }
+ return added;
+ }
+
+ @WorkerThread
+ private boolean removeRoleHolderInternal(@NonNull Role role, @NonNull String packageName,
+ boolean dontKillApp) {
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
+ mUser, mContext);
+ if (applicationInfo == null) {
+ Log.w(LOG_TAG, "Cannot get ApplicationInfo for package: " + packageName);
+ }
+
+ if (applicationInfo != null) {
+ role.revokeAsUser(packageName, dontKillApp, false, mUser, mContext);
+ }
+
+ String roleName = role.getName();
+ boolean removed = mUserRoleManager.removeRoleHolderFromController(roleName, packageName);
+ if (!removed) {
+ Log.e(LOG_TAG, "Failed to remove role holder in RoleManager," + " package: "
+ + packageName + ", role: " + roleName);
+ }
+ return removed;
+ }
+
+ @WorkerThread
+ private boolean clearRoleHoldersInternal(@NonNull Role role, boolean dontKillApp) {
+ String roleName = role.getName();
+ List<String> packageNames = mUserRoleManager.getRoleHolders(roleName);
+ boolean cleared = true;
+
+ int packageNamesSize = packageNames.size();
+ for (int i = 0; i < packageNamesSize; i++) {
+ String packageName = packageNames.get(i);
+ boolean removed = removeRoleHolderInternal(role, packageName, dontKillApp);
+ if (!removed) {
+ cleared = false;
+ }
+ }
+
+ if (!cleared) {
+ Log.e(LOG_TAG, "Failed to clear role holders, role: " + roleName);
+ }
+ return cleared;
+ }
+
+ @WorkerThread
+ private boolean addFallbackRoleHolderMaybe(@NonNull Role role) {
+ String roleName = role.getName();
+ List<String> currentPackageNames = mUserRoleManager.getRoleHolders(roleName);
+ if (!currentPackageNames.isEmpty()) {
+ return true;
+ }
+
+ String fallbackPackageName = role.getFallbackHolderAsUser(mUser, mContext);
+ if (fallbackPackageName == null) {
+ return true;
+ }
+
+ if (!role.isPackageQualifiedAsUser(fallbackPackageName, mUser, mContext)) {
+ Log.e(LOG_TAG, "Fallback role holder package doesn't qualify for the role, package: "
+ + fallbackPackageName + ", role: " + roleName);
+ return false;
+ }
+
+ Log.i(LOG_TAG, "Adding package as fallback role holder, package: " + fallbackPackageName
+ + ", role: " + roleName);
+ // TODO: If we don't override user here, user might end up missing incoming
+ // phone calls or SMS, so we just keep the old behavior. But overriding user
+ // choice about permission without explicit user action is bad, so maybe we
+ // should at least show a notification?
+ return addRoleHolderInternal(role, fallbackPackageName,
+ role.shouldOverrideUserWhenGranting());
+ }
+
+ @Override
+ public boolean onIsApplicationQualifiedForRole(@NonNull String roleName,
+ @NonNull String packageName) {
+ // This API has been deprecated and Settings has been using onIsApplicationVisibleForRole()
+ // instead.
+ return false;
+ }
+
+ @Override
+ public boolean onIsApplicationVisibleForRole(@NonNull String roleName,
+ @NonNull String packageName) {
+ Role role = Roles.get(mContext).get(roleName);
+ if (role == null) {
+ return false;
+ }
+ if (!role.isAvailableAsUser(mUser, mContext)) {
+ return false;
+ }
+ if (!role.isPackageQualifiedAsUser(packageName, mUser, mContext)) {
+ return false;
+ }
+ ApplicationInfo applicationInfo = PackageUtils.getApplicationInfoAsUser(packageName,
+ mUser, mContext);
+ if (applicationInfo == null || !role.isApplicationVisibleAsUser(applicationInfo, mUser,
+ mContext)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onIsRoleVisible(@NonNull String roleName) {
+ Role role = Roles.get(mContext).get(roleName);
+ if (role == null) {
+ return false;
+ }
+ if (!role.isAvailableAsUser(mUser, mContext)) {
+ return false;
+ }
+
+ return role.isVisibleAsUser(mUser, mContext);
+ }
+
+ @Override
+ @NonNull
+ public List<String> onGetLegacyFallbackDisabledRoles() {
+ return LegacyRoleFallbackEnabledUtils.getFallbackDisabledRoles(mUser, mContext);
+ }
+
+
+ private static boolean checkFlags(int flags, int allowedFlags) {
+ if ((flags & allowedFlags) != flags) {
+ Log.e(LOG_TAG, "flags is invalid, flags: 0x" + Integer.toHexString(flags)
+ + ", allowed flags: 0x" + Integer.toHexString(allowedFlags));
+ return false;
+ }
+ return true;
+ }
+
+ private static boolean hasFlag(int flags, int flag) {
+ return (flags & flag) == flag;
+ }
+}