diff options
| author | 2018-11-30 23:13:50 +0000 | |
|---|---|---|
| committer | 2018-11-30 23:13:50 +0000 | |
| commit | b9df101c9eac5ce534f274c04913e54ea2c3de1d (patch) | |
| tree | ba05ef5cbf3dc24195939e439ae6937a4e0612ae | |
| parent | 5cf6abedcbf035617508175e9f1e08037ac791a6 (diff) | |
| parent | abdefbaeeb74c0311c8f3465658c501260290f73 (diff) | |
Merge "Call roles granting only when packages changed"
6 files changed, 173 insertions, 17 deletions
diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 6f49cc42f6f6..b49c4476e82d 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -28,6 +28,7 @@ import android.content.pm.PackageManager.PackageInfoFlags; import android.content.pm.PackageManager.ResolveInfoFlags; import android.os.Bundle; import android.os.PersistableBundle; +import android.util.ArraySet; import android.util.SparseArray; import com.android.internal.util.function.TriFunction; @@ -37,6 +38,7 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.List; import java.util.function.BiFunction; +import java.util.function.Consumer; /** * Package manager local system service interface. @@ -735,4 +737,22 @@ public abstract class PackageManagerInternal { /** Returns {@code true} if the given user requires extra badging for icons. */ public abstract boolean userNeedsBadging(int userId); + + /** + * Perform the given action for each package. + * Note that packages lock will be held while performin the actions. + * + * @param actionLocked action to be performed + */ + public abstract void forEachPackage(Consumer<PackageParser.Package> actionLocked); + + /** Returns the list of enabled components */ + public abstract ArraySet<String> getEnabledComponents(String packageName, int userId); + + /** Returns the list of disabled components */ + public abstract ArraySet<String> getDisabledComponents(String packageName, int userId); + + /** Returns whether the given package is enabled for the given user */ + public abstract @PackageManager.EnabledState int getApplicationEnabledState( + String packageName, int userId); } diff --git a/core/java/com/android/internal/util/BitUtils.java b/core/java/com/android/internal/util/BitUtils.java index 17d5a2e36974..61581458f98a 100644 --- a/core/java/com/android/internal/util/BitUtils.java +++ b/core/java/com/android/internal/util/BitUtils.java @@ -28,7 +28,7 @@ import java.util.function.IntFunction; /** * A utility class for handling unsigned integers and unsigned arithmetics, as well as syntactic - * sugar methods for ByteBuffer. Useful for networking and packet manipulations. + * sugar methods for {@link ByteBuffer}. Useful for networking and packet manipulations. * {@hide} */ public final class BitUtils { @@ -151,4 +151,11 @@ public final class BitUtils { TextUtils.wrap(builder, "[", "]"); return builder.toString(); } + + /** + * Converts long to byte array + */ + public static byte[] toBytes(long l) { + return ByteBuffer.allocate(8).putLong(l).array(); + } } diff --git a/core/java/com/android/internal/util/CollectionUtils.java b/core/java/com/android/internal/util/CollectionUtils.java index 083c0c9736f9..151901be7b5b 100644 --- a/core/java/com/android/internal/util/CollectionUtils.java +++ b/core/java/com/android/internal/util/CollectionUtils.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; @@ -190,6 +191,13 @@ public class CollectionUtils { } /** + * Returns the size of the given map, or 0 if null + */ + public static int size(@Nullable Map<?, ?> cur) { + return cur != null ? cur.size() : 0; + } + + /** * Returns whether the given collection {@link Collection#isEmpty is empty} or {@code null} */ public static boolean isEmpty(@Nullable Collection<?> cur) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 6a6a5be53409..b76eaaffe078 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -116,8 +116,7 @@ import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import static com.android.server.pm.PackageManagerServiceUtils.verifySignatures; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE; import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS; -import static com.android.server.pm.permission.PermissionsState - .PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; +import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; import android.Manifest; import android.annotation.IntDef; @@ -314,8 +313,7 @@ import com.android.server.pm.dex.DexoptOptions; import com.android.server.pm.dex.PackageDexUsage; import com.android.server.pm.permission.BasePermission; import com.android.server.pm.permission.DefaultPermissionGrantPolicy; -import com.android.server.pm.permission.DefaultPermissionGrantPolicy - .DefaultPermissionGrantedCallback; +import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; import com.android.server.pm.permission.PermissionManagerInternal; import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; import com.android.server.pm.permission.PermissionManagerService; @@ -374,6 +372,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Predicate; /** @@ -23207,6 +23206,45 @@ public class PackageManagerService extends IPackageManager.Stub throws IOException { PackageManagerService.this.freeStorage(volumeUuid, bytes, storageFlags); } + + @Override + public void forEachPackage(Consumer<PackageParser.Package> actionLocked) { + PackageManagerService.this.forEachPackage(actionLocked); + } + + @Override + public ArraySet<String> getEnabledComponents(String packageName, int userId) { + synchronized (mPackages) { + PackageSetting setting = mSettings.getPackageLPr(packageName); + if (setting == null) { + return new ArraySet<>(); + } + return setting.getEnabledComponents(userId); + } + } + + @Override + public ArraySet<String> getDisabledComponents(String packageName, int userId) { + synchronized (mPackages) { + PackageSetting setting = mSettings.getPackageLPr(packageName); + if (setting == null) { + return new ArraySet<>(); + } + return setting.getDisabledComponents(userId); + } + } + + @Override + public @PackageManager.EnabledState int getApplicationEnabledState( + String packageName, int userId) { + synchronized (mPackages) { + PackageSetting setting = mSettings.getPackageLPr(packageName); + if (setting == null) { + return COMPONENT_ENABLED_STATE_DEFAULT; + } + return setting.getEnabled(userId); + } + } } @GuardedBy("mPackages") @@ -23329,6 +23367,15 @@ public class PackageManagerService extends IPackageManager.Stub } } + void forEachPackage(Consumer<PackageParser.Package> actionLocked) { + synchronized (mPackages) { + int numPackages = mPackages.size(); + for (int i = 0; i < numPackages; i++) { + actionLocked.accept(mPackages.valueAt(i)); + } + } + } + private static void enforceSystemOrPhoneCaller(String tag) { int callingUid = Binder.getCallingUid(); if (callingUid != Process.PHONE_UID && callingUid != Process.SYSTEM_UID) { diff --git a/services/core/java/com/android/server/role/RoleManagerService.java b/services/core/java/com/android/server/role/RoleManagerService.java index b390eebf3d7e..b065470fe98c 100644 --- a/services/core/java/com/android/server/role/RoleManagerService.java +++ b/services/core/java/com/android/server/role/RoleManagerService.java @@ -30,20 +30,27 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageManagerInternal; +import android.content.pm.Signature; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.UserHandle; import android.os.UserManagerInternal; import android.text.TextUtils; import android.util.ArraySet; +import android.util.PackageUtils; import android.util.Slog; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.BitUtils; +import com.android.internal.util.CollectionUtils; +import com.android.internal.util.FunctionalUtils; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemService; +import java.io.ByteArrayOutputStream; import java.io.FileDescriptor; import java.util.ArrayList; import java.util.Collections; @@ -117,13 +124,18 @@ public class RoleManagerService extends SystemService { @Override public void onStartUser(@UserIdInt int userId) { + RoleUserState userState; synchronized (mLock) { - //TODO only call into PermissionController if it or system upgreaded (for boot time) - getUserStateLocked(userId); + userState = getUserStateLocked(userId); } - //TODO consider calling grants only when certain conditions are met - // such as OS or PermissionController upgrade - if (RemoteRoleControllerService.DEBUG) { + String packagesHash = computeComponentStateHash(userId); + boolean needGrant; + synchronized (mLock) { + needGrant = !packagesHash.equals(userState.getLastGrantPackagesHashLocked()); + } + if (needGrant) { + // Some vital packages state has changed since last role grant + // Run grants again Slog.i(LOG_TAG, "Granting default permissions..."); CompletableFuture<Void> result = new CompletableFuture<>(); getControllerService(userId).onGrantDefaultRoles( @@ -140,12 +152,47 @@ public class RoleManagerService extends SystemService { }); try { result.get(5, TimeUnit.SECONDS); + synchronized (mLock) { + userState.setLastGrantPackagesHashLocked(packagesHash); + } } catch (InterruptedException | ExecutionException | TimeoutException e) { Slog.e(LOG_TAG, "Failed to grant defaults for user " + userId, e); } + } else if (RemoteRoleControllerService.DEBUG) { + Slog.i(LOG_TAG, "Already ran grants for package state " + packagesHash); } } + private String computeComponentStateHash(int userId) { + PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + pm.forEachPackage(FunctionalUtils.uncheckExceptions(pkg -> { + out.write(pkg.packageName.getBytes()); + out.write(BitUtils.toBytes(pkg.getLongVersionCode())); + out.write(pm.getApplicationEnabledState(pkg.packageName, userId)); + + ArraySet<String> enabledComponents = + pm.getEnabledComponents(pkg.packageName, userId); + int numComponents = CollectionUtils.size(enabledComponents); + for (int i = 0; i < numComponents; i++) { + out.write(enabledComponents.valueAt(i).getBytes()); + } + + ArraySet<String> disabledComponents = + pm.getDisabledComponents(pkg.packageName, userId); + numComponents = CollectionUtils.size(disabledComponents); + for (int i = 0; i < numComponents; i++) { + out.write(disabledComponents.valueAt(i).getBytes()); + } + for (Signature signature : pkg.mSigningDetails.signatures) { + out.write(signature.toByteArray()); + } + })); + + return PackageUtils.computeSha256Digest(out.toByteArray()); + } + @GuardedBy("mLock") @NonNull private RoleUserState getUserStateLocked(@UserIdInt int userId) { diff --git a/services/core/java/com/android/server/role/RoleUserState.java b/services/core/java/com/android/server/role/RoleUserState.java index 9c43f4d02ad0..f218d3a5834b 100644 --- a/services/core/java/com/android/server/role/RoleUserState.java +++ b/services/core/java/com/android/server/role/RoleUserState.java @@ -31,6 +31,7 @@ import android.util.Xml; import com.android.internal.annotations.GuardedBy; import com.android.internal.os.BackgroundThread; +import com.android.internal.util.CollectionUtils; import com.android.internal.util.function.pooled.PooledLambda; import libcore.io.IoUtils; @@ -63,6 +64,7 @@ public class RoleUserState { private static final String TAG_HOLDER = "holder"; private static final String ATTRIBUTE_VERSION = "version"; private static final String ATTRIBUTE_NAME = "name"; + private static final String ATTRIBUTE_PACKAGES_HASH = "packagesHash"; @UserIdInt private final int mUserId; @@ -70,11 +72,15 @@ public class RoleUserState { @GuardedBy("RoleManagerService.mLock") private int mVersion; + @GuardedBy("RoleManagerService.mLock") + private String mLastGrantPackagesHash = null; + /** * Maps role names to its holders' package names. The values should never be null. */ @GuardedBy("RoleManagerService.mLock") - private ArrayMap<String, ArraySet<String>> mRoles; + @Nullable + private ArrayMap<String, ArraySet<String>> mRoles = null; @GuardedBy("RoleManagerService.mLock") private boolean mDestroyed; @@ -110,6 +116,23 @@ public class RoleUserState { } /** + * Get the hash representing the state of packages during the last time initial grants was run + */ + @GuardedBy("RoleManagerService.mLock") + public String getLastGrantPackagesHashLocked() { + return mLastGrantPackagesHash; + } + + /** + * Set the hash representing the state of packages during the last time initial grants was run + */ + @GuardedBy("RoleManagerService.mLock") + public void setLastGrantPackagesHashLocked(String lastGrantPackagesHash) { + mLastGrantPackagesHash = lastGrantPackagesHash; + writeAsyncLocked(); + } + + /** * Get whether the role is available. * * @param roleName the name of the role to get the holders for @@ -227,11 +250,11 @@ public class RoleUserState { * Schedule writing the state to file. */ @GuardedBy("RoleManagerService.mLock") - private void writeAsyncLocked() { + void writeAsyncLocked() { throwIfDestroyedLocked(); int version = mVersion; ArrayMap<String, ArraySet<String>> roles = new ArrayMap<>(); - for (int i = 0, size = mRoles.size(); i < size; ++i) { + for (int i = 0, size = CollectionUtils.size(mRoles); i < size; ++i) { String roleName = mRoles.keyAt(i); ArraySet<String> roleHolders = mRoles.valueAt(i); roleHolders = new ArraySet<>(roleHolders); @@ -240,11 +263,12 @@ public class RoleUserState { mWriteHandler.removeCallbacksAndMessages(null); // TODO: Throttle writes. mWriteHandler.sendMessage(PooledLambda.obtainMessage( - RoleUserState::writeSync, this, version, roles)); + RoleUserState::writeSync, this, version, roles, mLastGrantPackagesHash)); } @WorkerThread - private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles) { + private void writeSync(int version, @NonNull ArrayMap<String, ArraySet<String>> roles, + String packagesHash) { AtomicFile atomicFile = new AtomicFile(getFile(mUserId), "roles-" + mUserId); FileOutputStream out = null; try { @@ -256,7 +280,7 @@ public class RoleUserState { "http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startDocument(null, true); - serializeRoles(serializer, version, roles); + serializeRoles(serializer, version, roles, packagesHash); serializer.endDocument(); atomicFile.finishWrite(out); @@ -272,9 +296,11 @@ public class RoleUserState { @WorkerThread private void serializeRoles(@NonNull XmlSerializer serializer, int version, - @NonNull ArrayMap<String, ArraySet<String>> roles) throws IOException { + @NonNull ArrayMap<String, ArraySet<String>> roles, String packagesHash) + throws IOException { serializer.startTag(null, TAG_ROLES); serializer.attribute(null, ATTRIBUTE_VERSION, Integer.toString(version)); + serializer.attribute(null, ATTRIBUTE_PACKAGES_HASH, packagesHash); for (int i = 0, size = roles.size(); i < size; ++i) { String roleName = roles.keyAt(i); ArraySet<String> roleHolders = roles.valueAt(i); @@ -341,6 +367,7 @@ public class RoleUserState { private void parseRolesLocked(@NonNull XmlPullParser parser) throws IOException, XmlPullParserException { mVersion = Integer.parseInt(parser.getAttributeValue(null, ATTRIBUTE_VERSION)); + mLastGrantPackagesHash = parser.getAttributeValue(null, ATTRIBUTE_PACKAGES_HASH); mRoles = new ArrayMap<>(); int type; |