Merge "Call roles granting only when packages changed"

This commit is contained in:
Eugene Susla
2018-11-30 23:13:50 +00:00
committed by Android (Google) Code Review
6 changed files with 173 additions and 17 deletions

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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;
@@ -189,6 +190,13 @@ public class CollectionUtils {
return cur != null ? cur.size() : 0;
}
/**
* 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}
*/

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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;
@@ -109,6 +115,23 @@ public class RoleUserState {
writeAsyncLocked();
}
/**
* 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.
*
@@ -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;