Merge "Introduce package state mutation APIs"
This commit is contained in:
committed by
Android (Google) Code Review
commit
0edef3c3f3
@@ -50,6 +50,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.pm.pkg.AndroidPackageApi;
|
||||
import com.android.server.pm.pkg.PackageState;
|
||||
import com.android.server.pm.pkg.PackageStateInternal;
|
||||
import com.android.server.pm.pkg.mutate.PackageStateMutator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.annotation.Retention;
|
||||
@@ -884,17 +885,6 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
|
||||
/** 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 performing the actions.
|
||||
*
|
||||
* If the caller does not need all packages, prefer the potentially non-locking
|
||||
* {@link #withPackageSettingsSnapshot(Consumer)}.
|
||||
*
|
||||
* @param actionLocked action to be performed
|
||||
*/
|
||||
public abstract void forEachPackage(Consumer<AndroidPackage> actionLocked);
|
||||
|
||||
/**
|
||||
* Perform the given action for each {@link PackageSetting}.
|
||||
* Note that packages lock will be held while performing the actions.
|
||||
@@ -917,6 +907,12 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
|
||||
*/
|
||||
public abstract void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action);
|
||||
|
||||
/**
|
||||
* {@link #forEachPackageState(boolean, Consumer)} but filtered to only states with packages
|
||||
* on device where {@link PackageStateInternal#getPkg()} is not null.
|
||||
*/
|
||||
public abstract void forEachPackage(Consumer<AndroidPackage> action);
|
||||
|
||||
/**
|
||||
* Perform the given action for each installed package for a user.
|
||||
* Note that packages lock will be held while performing the actions.
|
||||
@@ -924,6 +920,12 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
|
||||
public abstract void forEachInstalledPackage(
|
||||
@NonNull Consumer<AndroidPackage> actionLocked, @UserIdInt int userId);
|
||||
|
||||
/**
|
||||
* Perform the given action for each installed package for a user.
|
||||
*/
|
||||
public abstract void forEachInstalledPackage(boolean locked,
|
||||
@NonNull Consumer<AndroidPackage> action, @UserIdInt int userId);
|
||||
|
||||
/** Returns the list of enabled components */
|
||||
public abstract ArraySet<String> getEnabledComponents(String packageName, int userId);
|
||||
|
||||
@@ -1265,4 +1267,62 @@ public abstract class PackageManagerInternal implements PackageSettingsSnapshotP
|
||||
*/
|
||||
public abstract void reconcileAppsData(int userId, @StorageManager.StorageFlags int flags,
|
||||
boolean migrateAppsData);
|
||||
|
||||
/**
|
||||
* Initiates a package state mutation request, returning the current state as known by
|
||||
* PackageManager. This allows the later commit request to compare the initial values and
|
||||
* determine if any state was changed or any packages were updated since the whole request
|
||||
* was initiated.
|
||||
*
|
||||
* As a concrete example, consider the following steps:
|
||||
* <ol>
|
||||
* <li>Read a package state without taking a lock</li>
|
||||
* <li>Check some values in that state, determine that a mutation needs to occur</li>
|
||||
* <li>Call to commit the change with the new value, takes lock</li>
|
||||
* </ol>
|
||||
*
|
||||
* Between steps 1 and 3, because the lock was not taken for the entire flow, it's possible
|
||||
* a package state was changed by another consumer or a package was updated/installed.
|
||||
*
|
||||
* If anything has changed,
|
||||
* {@link #commitPackageStateMutation(PackageStateMutator.InitialState, Consumer)} will return
|
||||
* a {@link PackageStateMutator.Result} indicating so. If the caller has not indicated it can
|
||||
* ignore changes, it can opt to re-run the commit logic from the top with a true write lock
|
||||
* around all of its read-logic-commit loop.
|
||||
*
|
||||
* Note that if the caller does not care about potential race conditions or package/state
|
||||
* changes between steps 1 and 3, it can simply opt to not call this method and pass in null
|
||||
* for the initial state. This is useful to avoid long running data structure locks when the
|
||||
* caller is changing a value as part of a one-off request. Perhaps from an app side API which
|
||||
* mutates only a single package, where it doesn't care what the state of that package is or
|
||||
* any other packages on the devices.
|
||||
*
|
||||
* Important to note is that if no locking is enforced, callers themselves will not be
|
||||
* synchronized with themselves. The caller may be relying on the PackageManager lock to
|
||||
* enforce ordering within their own code path, and that has to be adjusted if migrated off
|
||||
* the lock.
|
||||
*/
|
||||
@NonNull
|
||||
public abstract PackageStateMutator.InitialState recordInitialState();
|
||||
|
||||
/**
|
||||
* Some questions to ask when designing a mutation:
|
||||
* <ol>
|
||||
* <li>What external system state is required and is it synchronized properly?</li>
|
||||
* <li>Are there any package/state changes that could happen to the target (or another)
|
||||
* package that could result in the commit being invalid?</li>
|
||||
* <li>Is the caller synchronized with itself and can handle multiple mutations being
|
||||
* requested from different threads?</li>
|
||||
* <li>What should be done in case of a conflict and the commit can't be finished?</li>
|
||||
* </ol>
|
||||
*
|
||||
* @param state See {@link #recordInitialState()}. If null, no result is returned.
|
||||
* @param consumer Lean wrapper around just the logic that changes state values
|
||||
* @return result if anything changed since initial state, or null if nothing changed and
|
||||
* commit was successful
|
||||
*/
|
||||
@Nullable
|
||||
public abstract PackageStateMutator.Result commitPackageStateMutation(
|
||||
@Nullable PackageStateMutator.InitialState state,
|
||||
@NonNull Consumer<PackageStateMutator> consumer);
|
||||
}
|
||||
|
||||
@@ -547,7 +547,6 @@ final class DeletePackageHelper {
|
||||
true /*notLaunched*/,
|
||||
false /*hidden*/,
|
||||
0 /*distractionFlags*/,
|
||||
false /*suspended*/,
|
||||
null /*suspendParams*/,
|
||||
false /*instantApp*/,
|
||||
false /*virtualPreload*/,
|
||||
|
||||
@@ -239,6 +239,9 @@ import com.android.server.pm.pkg.PackageStateUtils;
|
||||
import com.android.server.pm.pkg.PackageUserState;
|
||||
import com.android.server.pm.pkg.PackageUserStateInternal;
|
||||
import com.android.server.pm.pkg.SuspendParams;
|
||||
import com.android.server.pm.pkg.mutate.PackageStateMutator;
|
||||
import com.android.server.pm.pkg.mutate.PackageStateWrite;
|
||||
import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
|
||||
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
|
||||
import com.android.server.pm.verify.domain.DomainVerificationService;
|
||||
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
|
||||
@@ -594,6 +597,16 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
// the suffix "Locked". Some methods may use the legacy suffix "LP"
|
||||
final PackageManagerTracedLock mLock;
|
||||
|
||||
// Lock alias for doing package state mutation
|
||||
private final PackageManagerTracedLock mPackageStateWriteLock;
|
||||
|
||||
// Lock alias to track syncing a consistent Computer
|
||||
private final PackageManagerTracedLock mLiveComputerSyncLock;
|
||||
|
||||
private final PackageStateMutator mPackageStateMutator = new PackageStateMutator(
|
||||
this::getPackageSettingForMutation,
|
||||
this::getDisabledPackageSettingForMutation);
|
||||
|
||||
// Keys are String (package name), values are Package.
|
||||
@Watched
|
||||
@GuardedBy("mLock")
|
||||
@@ -1608,6 +1621,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
mInstaller = injector.getInstaller();
|
||||
mInstallLock = injector.getInstallLock();
|
||||
mLock = injector.getLock();
|
||||
mPackageStateWriteLock = mLock;
|
||||
mLiveComputerSyncLock = mLock;
|
||||
mPermissionManager = injector.getPermissionManagerServiceInternal();
|
||||
mSettings = injector.getSettings();
|
||||
mUserManager = injector.getUserManagerService();
|
||||
@@ -1713,6 +1728,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
mInjector.bootstrap(this);
|
||||
mLock = injector.getLock();
|
||||
mPackageStateWriteLock = mLock;
|
||||
mLiveComputerSyncLock = mLock;
|
||||
mInstallLock = injector.getInstallLock();
|
||||
LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
|
||||
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
|
||||
@@ -4553,8 +4570,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
if (pus.isSuspended()) {
|
||||
for (int i = 0; i < pus.getSuspendParams().size(); i++) {
|
||||
final SuspendParams params = pus.getSuspendParams().valueAt(i);
|
||||
if (params != null && params.appExtras != null) {
|
||||
allExtras.putAll(params.appExtras);
|
||||
if (params != null && params.getAppExtras() != null) {
|
||||
allExtras.putAll(params.getAppExtras());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4627,9 +4644,9 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
synchronized (mLock) {
|
||||
for (String packageName : packagesToChange) {
|
||||
final PackageSetting ps = mSettings.getPackageLPr(packageName);
|
||||
if (ps != null && ps.getSuspended(userId)) {
|
||||
if (ps != null && ps.getUserStateOrDefault(userId).isSuspended()) {
|
||||
ps.removeSuspension(suspendingPackagePredicate, userId);
|
||||
if (!ps.getSuspended(userId)) {
|
||||
if (!ps.getUserStateOrDefault(userId).isSuspended()) {
|
||||
unsuspendedPackages.add(ps.getPackageName());
|
||||
unsuspendedUids.add(UserHandle.getUid(userId, ps.getAppId()));
|
||||
}
|
||||
@@ -7413,8 +7430,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
if (userState.isSuspended()) {
|
||||
for (int i = 0; i < userState.getSuspendParams().size(); i++) {
|
||||
final SuspendParams params = userState.getSuspendParams().valueAt(i);
|
||||
if (params != null && params.launcherExtras != null) {
|
||||
allExtras.putAll(params.launcherExtras);
|
||||
if (params != null && params.getLauncherExtras() != null) {
|
||||
allExtras.putAll(params.getLauncherExtras());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7503,7 +7520,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
|
||||
final SuspendParams suspendParams = suspendParamsMap.get(suspendingPackage);
|
||||
return (suspendParams != null) ? suspendParams.dialogInfo : null;
|
||||
return (suspendParams != null) ? suspendParams.getDialogInfo() : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -7949,7 +7966,13 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Override
|
||||
public void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
|
||||
@UserIdInt int userId) {
|
||||
PackageManagerService.this.forEachInstalledPackage(actionLocked, userId);
|
||||
forEachInstalledPackage(true, actionLocked, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void forEachInstalledPackage(boolean locked,
|
||||
@NonNull Consumer<AndroidPackage> action, int userId) {
|
||||
PackageManagerService.this.forEachInstalledPackage(locked, action, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -8222,51 +8245,24 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Override
|
||||
public void withPackageSettingsSnapshot(
|
||||
@NonNull Consumer<Function<String, PackageStateInternal>> block) {
|
||||
final Computer snapshot = snapshotComputer();
|
||||
|
||||
// This method needs to either lock or not lock consistently throughout the method,
|
||||
// so if the live computer is returned, force a wrapping sync block.
|
||||
if (snapshot == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
block.accept(snapshot::getPackageStateInternal);
|
||||
}
|
||||
} else {
|
||||
block.accept(snapshot::getPackageStateInternal);
|
||||
}
|
||||
executeWithConsistentComputer(computer ->
|
||||
block.accept(computer::getPackageStateInternal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <Output> Output withPackageSettingsSnapshotReturning(
|
||||
@NonNull FunctionalUtils.ThrowingFunction<Function<String, PackageStateInternal>,
|
||||
Output> block) {
|
||||
final Computer snapshot = snapshotComputer();
|
||||
|
||||
// This method needs to either lock or not lock consistently throughout the method,
|
||||
// so if the live computer is returned, force a wrapping sync block.
|
||||
if (snapshot == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
return block.apply(snapshot::getPackageStateInternal);
|
||||
}
|
||||
} else {
|
||||
return block.apply(snapshot::getPackageStateInternal);
|
||||
}
|
||||
return executeWithConsistentComputerReturning(computer ->
|
||||
block.apply(computer::getPackageStateInternal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <ExceptionType extends Exception> void withPackageSettingsSnapshotThrowing(
|
||||
@NonNull FunctionalUtils.ThrowingCheckedConsumer<Function<String,
|
||||
PackageStateInternal>, ExceptionType> block) throws ExceptionType {
|
||||
final Computer snapshot = snapshotComputer();
|
||||
|
||||
// This method needs to either lock or not lock consistently throughout the method,
|
||||
// so if the live computer is returned, force a wrapping sync block.
|
||||
if (snapshot == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
block.accept(snapshot::getPackageStateInternal);
|
||||
}
|
||||
} else {
|
||||
block.accept(snapshot::getPackageStateInternal);
|
||||
}
|
||||
executeWithConsistentComputerThrowing(computer ->
|
||||
block.accept(computer::getPackageStateInternal));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -8276,17 +8272,9 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
Function<String, PackageStateInternal>, ExceptionOne,
|
||||
ExceptionTwo> block)
|
||||
throws ExceptionOne, ExceptionTwo {
|
||||
final Computer snapshot = snapshotComputer();
|
||||
|
||||
// This method needs to either lock or not lock consistently throughout the method,
|
||||
// so if the live computer is returned, force a wrapping sync block.
|
||||
if (snapshot == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
block.accept(snapshot::getPackageStateInternal);
|
||||
}
|
||||
} else {
|
||||
block.accept(snapshot::getPackageStateInternal);
|
||||
}
|
||||
executeWithConsistentComputerThrowing2(
|
||||
(FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
|
||||
ExceptionTwo>) computer -> block.accept(computer::getPackageStateInternal));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -8296,17 +8284,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
Function<String, PackageStateInternal>, Output,
|
||||
ExceptionType> block)
|
||||
throws ExceptionType {
|
||||
final Computer snapshot = snapshotComputer();
|
||||
|
||||
// This method needs to either lock or not lock consistently throughout the method,
|
||||
// so if the live computer is returned, force a wrapping sync block.
|
||||
if (snapshot == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
return block.apply(snapshot::getPackageStateInternal);
|
||||
}
|
||||
} else {
|
||||
return block.apply(snapshot::getPackageStateInternal);
|
||||
}
|
||||
return executeWithConsistentComputerReturningThrowing(computer ->
|
||||
block.apply(computer::getPackageStateInternal));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -8315,6 +8294,20 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
PackageManagerService.this.mAppDataHelper.reconcileAppsData(userId, flags,
|
||||
migrateAppsData);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageStateMutator.InitialState recordInitialState() {
|
||||
return PackageManagerService.this.recordInitialState();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PackageStateMutator.Result commitPackageStateMutation(
|
||||
@Nullable PackageStateMutator.InitialState state,
|
||||
@NonNull Consumer<PackageStateMutator> consumer) {
|
||||
return PackageManagerService.this.commitPackageStateMutation(state, consumer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -8357,7 +8350,15 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
@Nullable
|
||||
@GuardedBy("mLock")
|
||||
PackageSetting getPackageSettingForMutation(String packageName) {
|
||||
return (PackageSetting) mComputer.getPackageStateInternal(packageName);
|
||||
return mSettings.getPackageLPr(packageName);
|
||||
}
|
||||
|
||||
// TODO: Remove
|
||||
@Deprecated
|
||||
@Nullable
|
||||
@GuardedBy("mLock")
|
||||
PackageSetting getDisabledPackageSettingForMutation(String packageName) {
|
||||
return mSettings.getDisabledSystemPkgLPr(packageName);
|
||||
}
|
||||
|
||||
@VisibleForTesting(visibility = Visibility.PRIVATE)
|
||||
@@ -8365,7 +8366,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
PackageStateInternal getPackageStateInternal(String packageName) {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
PackageSetting pkgSetting =
|
||||
(PackageSetting) computer.getPackageStateInternal(packageName);
|
||||
if (pkgSetting == null) {
|
||||
@@ -8374,15 +8375,16 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
return new PackageSetting(pkgSetting);
|
||||
}
|
||||
} else {
|
||||
return computer.getPackageStateInternal(packageName);
|
||||
}
|
||||
return computer.getPackageStateInternal(packageName);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
PackageStateInternal getPackageStateInternal(String packageName, int callingUid) {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
PackageSetting pkgSetting =
|
||||
(PackageSetting) computer.getPackageStateInternal(packageName, callingUid);
|
||||
if (pkgSetting == null) {
|
||||
@@ -8391,8 +8393,9 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
return new PackageSetting(pkgSetting);
|
||||
}
|
||||
} else {
|
||||
return computer.getPackageStateInternal(packageName, callingUid);
|
||||
}
|
||||
return computer.getPackageStateInternal(packageName, callingUid);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -8400,7 +8403,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
int callingUid, @UserIdInt int userId) {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
PackageSetting pkgSetting =
|
||||
(PackageSetting) filterPackageStateForInstalledAndFiltered(computer,
|
||||
packageName, callingUid, userId);
|
||||
@@ -8409,9 +8412,10 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
return new PackageSetting(pkgSetting);
|
||||
}
|
||||
} else {
|
||||
return filterPackageStateForInstalledAndFiltered(computer, packageName, callingUid,
|
||||
userId);
|
||||
}
|
||||
|
||||
return filterPackageStateForInstalledAndFiltered(computer, packageName, callingUid, userId);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -8439,16 +8443,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
return new ArrayMap<>(computer.getPackageStates());
|
||||
}
|
||||
return computer.getPackageStates();
|
||||
}
|
||||
|
||||
void forEachPackage(Consumer<AndroidPackage> actionLocked) {
|
||||
synchronized (mLock) {
|
||||
int numPackages = mPackages.size();
|
||||
for (int i = 0; i < numPackages; i++) {
|
||||
actionLocked.accept(mPackages.valueAt(i));
|
||||
}
|
||||
} else {
|
||||
return computer.getPackageStates();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8461,17 +8457,31 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
private void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
|
||||
void forEachPackageState(boolean locked, Consumer<PackageStateInternal> action) {
|
||||
if (locked) {
|
||||
forEachPackageSetting(action::accept);
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
forEachPackageState(mComputer.getPackageStates(), action);
|
||||
}
|
||||
} else {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLock) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
forEachPackageState(computer.getPackageStates(), action);
|
||||
};
|
||||
}
|
||||
} else {
|
||||
forEachPackageState(computer.getPackageStates(), action);
|
||||
}
|
||||
forEachPackageState(computer.getPackageStates(), action);
|
||||
}
|
||||
}
|
||||
|
||||
void forEachPackage(Consumer<AndroidPackage> action) {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
forEachPackage(computer.getPackageStates(), action);
|
||||
}
|
||||
} else {
|
||||
forEachPackage(computer.getPackageStates(), action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8485,21 +8495,106 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
}
|
||||
|
||||
void forEachInstalledPackage(@NonNull Consumer<AndroidPackage> actionLocked,
|
||||
@UserIdInt int userId) {
|
||||
synchronized (mLock) {
|
||||
int numPackages = mPackages.size();
|
||||
for (int i = 0; i < numPackages; i++) {
|
||||
AndroidPackage pkg = mPackages.valueAt(i);
|
||||
PackageSetting setting = mSettings.getPackageLPr(pkg.getPackageName());
|
||||
if (setting == null || !setting.getInstalled(userId)) {
|
||||
continue;
|
||||
}
|
||||
actionLocked.accept(pkg);
|
||||
private void forEachPackage(
|
||||
@NonNull ArrayMap<String, ? extends PackageStateInternal> packageStates,
|
||||
@NonNull Consumer<AndroidPackage> consumer) {
|
||||
int size = packageStates.size();
|
||||
for (int index = 0; index < size; index++) {
|
||||
PackageStateInternal packageState = packageStates.valueAt(index);
|
||||
if (packageState.getPkg() != null) {
|
||||
consumer.accept(packageState.getPkg());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void forEachInstalledPackage(boolean locked, @NonNull Consumer<AndroidPackage> action,
|
||||
@UserIdInt int userId) {
|
||||
Consumer<PackageStateInternal> actionWrapped = packageState -> {
|
||||
if (packageState.getPkg() != null
|
||||
&& packageState.getUserStateOrDefault(userId).isInstalled()) {
|
||||
action.accept(packageState.getPkg());
|
||||
}
|
||||
};
|
||||
if (locked) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
forEachPackageState(mComputer.getPackageStates(), actionWrapped);
|
||||
}
|
||||
} else {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
forEachPackageState(computer.getPackageStates(), actionWrapped);
|
||||
}
|
||||
} else {
|
||||
forEachPackageState(computer.getPackageStates(), actionWrapped);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void executeWithConsistentComputer(
|
||||
@NonNull FunctionalUtils.ThrowingConsumer<Computer> consumer) {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
consumer.accept(computer);
|
||||
}
|
||||
} else {
|
||||
consumer.accept(computer);
|
||||
}
|
||||
}
|
||||
|
||||
private <T> T executeWithConsistentComputerReturning(
|
||||
@NonNull FunctionalUtils.ThrowingFunction<Computer, T> function) {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
return function.apply(computer);
|
||||
}
|
||||
} else {
|
||||
return function.apply(computer);
|
||||
}
|
||||
}
|
||||
|
||||
private <ExceptionType extends Exception> void executeWithConsistentComputerThrowing(
|
||||
@NonNull FunctionalUtils.ThrowingCheckedConsumer<Computer, ExceptionType> consumer)
|
||||
throws ExceptionType {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
consumer.accept(computer);
|
||||
}
|
||||
} else {
|
||||
consumer.accept(computer);
|
||||
}
|
||||
}
|
||||
|
||||
private <ExceptionOne extends Exception, ExceptionTwo extends Exception> void
|
||||
executeWithConsistentComputerThrowing2(
|
||||
@NonNull FunctionalUtils.ThrowingChecked2Consumer<Computer, ExceptionOne,
|
||||
ExceptionTwo> consumer) throws ExceptionOne, ExceptionTwo {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
consumer.accept(computer);
|
||||
}
|
||||
} else {
|
||||
consumer.accept(computer);
|
||||
}
|
||||
}
|
||||
|
||||
private <T, ExceptionType extends Exception> T executeWithConsistentComputerReturningThrowing(
|
||||
@NonNull FunctionalUtils.ThrowingCheckedFunction<Computer, T, ExceptionType> function)
|
||||
throws ExceptionType {
|
||||
Computer computer = snapshotComputer();
|
||||
if (computer == mLiveComputer) {
|
||||
synchronized (mLiveComputerSyncLock) {
|
||||
return function.apply(computer);
|
||||
}
|
||||
} else {
|
||||
return function.apply(computer);
|
||||
}
|
||||
}
|
||||
|
||||
boolean isHistoricalPackageUsageAvailable() {
|
||||
return mPackageUsage.isHistoricalPackageUsageAvailable();
|
||||
}
|
||||
@@ -8695,7 +8790,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
enforceOwnerRights(packageName, Binder.getCallingUid());
|
||||
final boolean changed;
|
||||
synchronized (mLock) {
|
||||
changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup, mimeTypes);
|
||||
changed = mSettings.getPackageLPr(packageName).setMimeGroup(mimeGroup,
|
||||
new ArraySet<>(mimeTypes));
|
||||
}
|
||||
if (changed) {
|
||||
applyMimeGroupChanges(packageName, mimeGroup);
|
||||
@@ -9198,4 +9294,62 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
return new Pair<>(rescanFlags, reparseFlags);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see PackageManagerInternal#recordInitialState()
|
||||
*/
|
||||
@NonNull
|
||||
public PackageStateMutator.InitialState recordInitialState() {
|
||||
return mPackageStateMutator.initialState(mChangedPackagesSequenceNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState,
|
||||
* Consumer)
|
||||
*/
|
||||
@NonNull
|
||||
public PackageStateMutator.Result commitPackageStateMutation(
|
||||
@Nullable PackageStateMutator.InitialState initialState,
|
||||
@NonNull Consumer<PackageStateMutator> consumer) {
|
||||
synchronized (mPackageStateWriteLock) {
|
||||
final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
|
||||
initialState, mChangedPackagesSequenceNumber);
|
||||
if (result != PackageStateMutator.Result.SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
consumer.accept(mPackageStateMutator);
|
||||
onChanged();
|
||||
}
|
||||
|
||||
return PackageStateMutator.Result.SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState,
|
||||
* Consumer)
|
||||
*/
|
||||
@NonNull
|
||||
public PackageStateMutator.Result commitPackageStateMutation(
|
||||
@Nullable PackageStateMutator.InitialState initialState, @NonNull String packageName,
|
||||
@NonNull Consumer<PackageStateWrite> consumer) {
|
||||
synchronized (mPackageStateWriteLock) {
|
||||
final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
|
||||
initialState, mChangedPackagesSequenceNumber);
|
||||
if (result != PackageStateMutator.Result.SUCCESS) {
|
||||
return result;
|
||||
}
|
||||
|
||||
PackageStateWrite state = mPackageStateMutator.forPackage(packageName);
|
||||
if (state == null) {
|
||||
return PackageStateMutator.Result.SPECIFIC_PACKAGE_NULL;
|
||||
} else {
|
||||
consumer.accept(state);
|
||||
}
|
||||
|
||||
onChanged();
|
||||
}
|
||||
|
||||
return PackageStateMutator.Result.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -22,6 +22,7 @@ import android.content.pm.ApplicationInfo;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.pm.permission.LegacyPermissionState;
|
||||
import com.android.server.pm.pkg.mutate.PackageStateMutator;
|
||||
import com.android.server.utils.Snappable;
|
||||
import com.android.server.utils.Watchable;
|
||||
import com.android.server.utils.WatchableImpl;
|
||||
@@ -88,6 +89,7 @@ public abstract class SettingBase implements Watchable, Snappable {
|
||||
* Notify listeners that this object has changed.
|
||||
*/
|
||||
protected void onChanged() {
|
||||
PackageStateMutator.onPackageStateChanged();
|
||||
dispatchChange(this);
|
||||
}
|
||||
|
||||
@@ -122,7 +124,7 @@ public abstract class SettingBase implements Watchable, Snappable {
|
||||
return mLegacyPermissionsState;
|
||||
}
|
||||
|
||||
SettingBase setFlags(int pkgFlags) {
|
||||
public SettingBase setFlags(int pkgFlags) {
|
||||
this.mPkgFlags = pkgFlags
|
||||
& (ApplicationInfo.FLAG_SYSTEM
|
||||
| ApplicationInfo.FLAG_EXTERNAL_STORAGE
|
||||
@@ -131,7 +133,7 @@ public abstract class SettingBase implements Watchable, Snappable {
|
||||
return this;
|
||||
}
|
||||
|
||||
SettingBase setPrivateFlags(int pkgPrivateFlags) {
|
||||
public SettingBase setPrivateFlags(int pkgPrivateFlags) {
|
||||
this.mPkgPrivateFlags = pkgPrivateFlags
|
||||
& (ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
|
||||
| ApplicationInfo.PRIVATE_FLAG_OEM
|
||||
|
||||
@@ -114,6 +114,7 @@ import com.android.server.pm.permission.LegacyPermissionDataProvider;
|
||||
import com.android.server.pm.permission.LegacyPermissionSettings;
|
||||
import com.android.server.pm.permission.LegacyPermissionState;
|
||||
import com.android.server.pm.permission.LegacyPermissionState.PermissionState;
|
||||
import com.android.server.pm.pkg.PackageStateInternal;
|
||||
import com.android.server.pm.pkg.PackageUserState;
|
||||
import com.android.server.pm.pkg.PackageUserStateInternal;
|
||||
import com.android.server.pm.pkg.SuspendParams;
|
||||
@@ -162,6 +163,7 @@ import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Holds information about dynamic settings.
|
||||
@@ -257,7 +259,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
public static final int SIGNATURE_MALFORMED_RECOVER = 3;
|
||||
}
|
||||
|
||||
private static final boolean DEBUG_STOPPED = false;
|
||||
static final boolean DEBUG_STOPPED = false;
|
||||
private static final boolean DEBUG_MU = false;
|
||||
private static final boolean DEBUG_KERNEL = false;
|
||||
private static final boolean DEBUG_PARSER = false;
|
||||
@@ -625,7 +627,15 @@ public final class Settings implements Watchable, Snappable {
|
||||
mOtherAppIds = new WatchedSparseArray<>();
|
||||
mPermissions = new LegacyPermissionSettings(lock);
|
||||
mRuntimePermissionsPersistence = new RuntimePermissionPersistence(
|
||||
runtimePermissionsPersistence);
|
||||
runtimePermissionsPersistence, new Consumer<Integer>() {
|
||||
@Override
|
||||
public void accept(Integer userId) {
|
||||
synchronized (mLock) {
|
||||
mRuntimePermissionsPersistence.writeStateForUserSync(userId,
|
||||
mPermissionDataProvider, mPackages, mSharedUsers);
|
||||
}
|
||||
}
|
||||
});
|
||||
mPermissionDataProvider = permissionDataProvider;
|
||||
|
||||
mSystemDir = new File(dataDir, "system");
|
||||
@@ -989,7 +999,6 @@ public final class Settings implements Watchable, Snappable {
|
||||
true /*notLaunched*/,
|
||||
false /*hidden*/,
|
||||
0 /*distractionFlags*/,
|
||||
false /*suspended*/,
|
||||
null /*suspendParams*/,
|
||||
instantApp,
|
||||
virtualPreload,
|
||||
@@ -1423,7 +1432,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
|
||||
void writeAllRuntimePermissionsLPr() {
|
||||
for (int userId : UserManagerService.getInstance().getUserIds()) {
|
||||
mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
|
||||
mRuntimePermissionsPersistence.writeStateForUserAsync(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1432,15 +1441,15 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
|
||||
void updateRuntimePermissionsFingerprintLPr(@UserIdInt int userId) {
|
||||
mRuntimePermissionsPersistence.updateRuntimePermissionsFingerprintLPr(userId);
|
||||
mRuntimePermissionsPersistence.updateRuntimePermissionsFingerprint(userId);
|
||||
}
|
||||
|
||||
int getDefaultRuntimePermissionsVersionLPr(int userId) {
|
||||
return mRuntimePermissionsPersistence.getVersionLPr(userId);
|
||||
return mRuntimePermissionsPersistence.getVersion(userId);
|
||||
}
|
||||
|
||||
void setDefaultRuntimePermissionsVersionLPr(int version, int userId) {
|
||||
mRuntimePermissionsPersistence.setVersionLPr(version, userId);
|
||||
mRuntimePermissionsPersistence.setVersion(version, userId);
|
||||
}
|
||||
|
||||
void setPermissionControllerVersion(long version) {
|
||||
@@ -1646,7 +1655,6 @@ public final class Settings implements Watchable, Snappable {
|
||||
false /*notLaunched*/,
|
||||
false /*hidden*/,
|
||||
0 /*distractionFlags*/,
|
||||
false /*suspended*/,
|
||||
null /*suspendParams*/,
|
||||
false /*instantApp*/,
|
||||
false /*virtualPreload*/,
|
||||
@@ -1816,10 +1824,9 @@ public final class Settings implements Watchable, Snappable {
|
||||
setBlockUninstallLPw(userId, name, true);
|
||||
}
|
||||
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
|
||||
hidden, distractionFlags, suspended, suspendParamsMap,
|
||||
instantApp, virtualPreload, enabledCaller, enabledComponents,
|
||||
disabledComponents, installReason, uninstallReason, harmfulAppWarning,
|
||||
splashScreenTheme);
|
||||
hidden, distractionFlags, suspendParamsMap, instantApp, virtualPreload,
|
||||
enabledCaller, enabledComponents, disabledComponents, installReason,
|
||||
uninstallReason, harmfulAppWarning, splashScreenTheme);
|
||||
|
||||
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
|
||||
} else if (tagName.equals("preferred-activities")) {
|
||||
@@ -3100,7 +3107,8 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
|
||||
for (UserInfo user : users) {
|
||||
mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id);
|
||||
mRuntimePermissionsPersistence.readStateForUserSync(user.id, getInternalVersion(),
|
||||
mPackages, mSharedUsers, getUserRuntimePermissionsFile(user.id));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3124,7 +3132,8 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
|
||||
void readPermissionStateForUserSyncLPr(@UserIdInt int userId) {
|
||||
mRuntimePermissionsPersistence.readStateForUserSyncLPr(userId);
|
||||
mRuntimePermissionsPersistence.readStateForUserSync(userId, getInternalVersion(),
|
||||
mPackages, mSharedUsers, getUserRuntimePermissionsFile(userId));
|
||||
}
|
||||
|
||||
void applyDefaultPreferredAppsLPw(int userId) {
|
||||
@@ -4120,7 +4129,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
file.delete();
|
||||
removeCrossProfileIntentFiltersLPw(userId);
|
||||
|
||||
mRuntimePermissionsPersistence.onUserRemovedLPw(userId);
|
||||
mRuntimePermissionsPersistence.onUserRemoved(userId);
|
||||
mDomainVerificationManager.clearUser(userId);
|
||||
|
||||
writePackageListLPr();
|
||||
@@ -4485,24 +4494,25 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
}
|
||||
for (UserInfo user : users) {
|
||||
final PackageUserStateInternal userState = ps.getUserStateOrDefault(user.id);
|
||||
pw.print(checkinTag);
|
||||
pw.print("-");
|
||||
pw.print("usr");
|
||||
pw.print(",");
|
||||
pw.print(user.id);
|
||||
pw.print(",");
|
||||
pw.print(ps.getInstalled(user.id) ? "I" : "i");
|
||||
pw.print(ps.getHidden(user.id) ? "B" : "b");
|
||||
pw.print(ps.getSuspended(user.id) ? "SU" : "su");
|
||||
pw.print(ps.getStopped(user.id) ? "S" : "s");
|
||||
pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
|
||||
pw.print(ps.getInstantApp(user.id) ? "IA" : "ia");
|
||||
pw.print(ps.getVirtualPreload(user.id) ? "VPI" : "vpi");
|
||||
String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
|
||||
pw.print(userState.isInstalled() ? "I" : "i");
|
||||
pw.print(userState.isHidden() ? "B" : "b");
|
||||
pw.print(userState.isSuspended() ? "SU" : "su");
|
||||
pw.print(userState.isStopped() ? "S" : "s");
|
||||
pw.print(userState.isNotLaunched() ? "l" : "L");
|
||||
pw.print(userState.isInstantApp() ? "IA" : "ia");
|
||||
pw.print(userState.isVirtualPreload() ? "VPI" : "vpi");
|
||||
String harmfulAppWarning = userState.getHarmfulAppWarning();
|
||||
pw.print(harmfulAppWarning != null ? "HA" : "ha");
|
||||
pw.print(",");
|
||||
pw.print(ps.getEnabled(user.id));
|
||||
String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
|
||||
pw.print(userState.getEnabledState());
|
||||
String lastDisabledAppCaller = userState.getLastDisableAppCaller();
|
||||
pw.print(",");
|
||||
pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?");
|
||||
pw.print(",");
|
||||
@@ -4819,48 +4829,48 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
|
||||
for (UserInfo user : users) {
|
||||
final PackageUserStateInternal userState = ps.getUserStateOrDefault(user.id);
|
||||
pw.print(prefix); pw.print(" User "); pw.print(user.id); pw.print(": ");
|
||||
pw.print("ceDataInode=");
|
||||
pw.print(ps.getCeDataInode(user.id));
|
||||
pw.print(userState.getCeDataInode());
|
||||
pw.print(" installed=");
|
||||
pw.print(ps.getInstalled(user.id));
|
||||
pw.print(userState.isInstalled());
|
||||
pw.print(" hidden=");
|
||||
pw.print(ps.getHidden(user.id));
|
||||
pw.print(userState.isHidden());
|
||||
pw.print(" suspended=");
|
||||
pw.print(ps.getSuspended(user.id));
|
||||
pw.print(userState.isSuspended());
|
||||
pw.print(" distractionFlags=");
|
||||
pw.print(ps.getDistractionFlags(user.id));
|
||||
pw.print(userState.getDistractionFlags());
|
||||
pw.print(" stopped=");
|
||||
pw.print(ps.getStopped(user.id));
|
||||
pw.print(userState.isStopped());
|
||||
pw.print(" notLaunched=");
|
||||
pw.print(ps.getNotLaunched(user.id));
|
||||
pw.print(userState.isNotLaunched());
|
||||
pw.print(" enabled=");
|
||||
pw.print(ps.getEnabled(user.id));
|
||||
pw.print(userState.getEnabledState());
|
||||
pw.print(" instant=");
|
||||
pw.print(ps.getInstantApp(user.id));
|
||||
pw.print(userState.isInstantApp());
|
||||
pw.print(" virtual=");
|
||||
pw.print(ps.getVirtualPreload(user.id));
|
||||
pw.println(userState.isVirtualPreload());
|
||||
pw.print(" installReason=");
|
||||
pw.println(ps.getInstallReason(user.id));
|
||||
pw.println(userState.getInstallReason());
|
||||
|
||||
if (ps.getSuspended(user.id)) {
|
||||
if (userState.isSuspended()) {
|
||||
pw.print(prefix);
|
||||
pw.println(" Suspend params:");
|
||||
final PackageUserStateInternal pus = ps.readUserState(user.id);
|
||||
for (int i = 0; i < pus.getSuspendParams().size(); i++) {
|
||||
for (int i = 0; i < userState.getSuspendParams().size(); i++) {
|
||||
pw.print(prefix);
|
||||
pw.print(" suspendingPackage=");
|
||||
pw.print(pus.getSuspendParams().keyAt(i));
|
||||
final SuspendParams params = pus.getSuspendParams().valueAt(i);
|
||||
pw.print(userState.getSuspendParams().keyAt(i));
|
||||
final SuspendParams params = userState.getSuspendParams().valueAt(i);
|
||||
if (params != null) {
|
||||
pw.print(" dialogInfo=");
|
||||
pw.print(params.dialogInfo);
|
||||
pw.print(params.getDialogInfo());
|
||||
}
|
||||
pw.println();
|
||||
}
|
||||
}
|
||||
|
||||
final OverlayPaths overlayPaths = ps.getOverlayPaths(user.id);
|
||||
final OverlayPaths overlayPaths = userState.getOverlayPaths();
|
||||
if (overlayPaths != null) {
|
||||
if (!overlayPaths.getOverlayPaths().isEmpty()) {
|
||||
pw.print(prefix);
|
||||
@@ -4883,7 +4893,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
|
||||
final Map<String, OverlayPaths> sharedLibraryOverlayPaths =
|
||||
ps.getOverlayPathsForLibrary(user.id);
|
||||
userState.getSharedLibraryOverlayPaths();
|
||||
if (sharedLibraryOverlayPaths != null) {
|
||||
for (Map.Entry<String, OverlayPaths> libOverlayPaths :
|
||||
sharedLibraryOverlayPaths.entrySet()) {
|
||||
@@ -4916,7 +4926,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
}
|
||||
|
||||
String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
|
||||
String lastDisabledAppCaller = userState.getLastDisableAppCaller();
|
||||
if (lastDisabledAppCaller != null) {
|
||||
pw.print(prefix); pw.print(" lastDisabledCaller: ");
|
||||
pw.println(lastDisabledAppCaller);
|
||||
@@ -4929,21 +4939,21 @@ public final class Settings implements Watchable, Snappable {
|
||||
.getPermissionStates(user.id), dumpAll);
|
||||
}
|
||||
|
||||
String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
|
||||
String harmfulAppWarning = userState.getHarmfulAppWarning();
|
||||
if (harmfulAppWarning != null) {
|
||||
pw.print(prefix); pw.print(" harmfulAppWarning: ");
|
||||
pw.println(harmfulAppWarning);
|
||||
}
|
||||
|
||||
if (permissionNames == null) {
|
||||
ArraySet<String> cmp = ps.getDisabledComponents(user.id);
|
||||
Set<String> cmp = userState.getDisabledComponents();
|
||||
if (cmp != null && cmp.size() > 0) {
|
||||
pw.print(prefix); pw.println(" disabledComponents:");
|
||||
for (String s : cmp) {
|
||||
pw.print(prefix); pw.print(" "); pw.println(s);
|
||||
}
|
||||
}
|
||||
cmp = ps.getEnabledComponents(user.id);
|
||||
cmp = userState.getEnabledComponents();
|
||||
if (cmp != null && cmp.size() > 0) {
|
||||
pw.print(prefix); pw.println(" enabledComponents:");
|
||||
for (String s : cmp) {
|
||||
@@ -5291,9 +5301,10 @@ public final class Settings implements Watchable, Snappable {
|
||||
|
||||
public void writePermissionStateForUserLPr(int userId, boolean sync) {
|
||||
if (sync) {
|
||||
mRuntimePermissionsPersistence.writeStateForUserSyncLPr(userId);
|
||||
mRuntimePermissionsPersistence.writeStateForUserSync(userId, mPermissionDataProvider,
|
||||
mPackages, mSharedUsers);
|
||||
} else {
|
||||
mRuntimePermissionsPersistence.writeStateForUserAsyncLPr(userId);
|
||||
mRuntimePermissionsPersistence.writeStateForUserAsync(userId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5368,7 +5379,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
}
|
||||
|
||||
private final class RuntimePermissionPersistence {
|
||||
private static final class RuntimePermissionPersistence {
|
||||
private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200;
|
||||
private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000;
|
||||
|
||||
@@ -5381,6 +5392,8 @@ public final class Settings implements Watchable, Snappable {
|
||||
|
||||
private final Handler mHandler = new MyHandler();
|
||||
|
||||
private final Object mLock = new Object();
|
||||
|
||||
@GuardedBy("mLock")
|
||||
private final SparseBooleanArray mWriteScheduled = new SparseBooleanArray();
|
||||
|
||||
@@ -5400,45 +5413,58 @@ public final class Settings implements Watchable, Snappable {
|
||||
// The mapping keys are user ids.
|
||||
private final SparseBooleanArray mPermissionUpgradeNeeded = new SparseBooleanArray();
|
||||
|
||||
public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence) {
|
||||
// This is a hack to allow this class to invoke a write using Settings's data structures,
|
||||
// to facilitate moving to a finer scoped lock without a significant refactor.
|
||||
private final Consumer<Integer> mInvokeWriteUserStateAsyncCallback;
|
||||
|
||||
public RuntimePermissionPersistence(RuntimePermissionsPersistence persistence,
|
||||
Consumer<Integer> invokeWriteUserStateAsyncCallback) {
|
||||
mPersistence = persistence;
|
||||
mInvokeWriteUserStateAsyncCallback = invokeWriteUserStateAsyncCallback;
|
||||
}
|
||||
|
||||
@GuardedBy("Settings.this.mLock")
|
||||
int getVersionLPr(int userId) {
|
||||
return mVersions.get(userId, INITIAL_VERSION);
|
||||
}
|
||||
|
||||
@GuardedBy("Settings.this.mLock")
|
||||
void setVersionLPr(int version, int userId) {
|
||||
mVersions.put(userId, version);
|
||||
writeStateForUserAsyncLPr(userId);
|
||||
}
|
||||
|
||||
@GuardedBy("Settings.this.mLock")
|
||||
public boolean isPermissionUpgradeNeeded(int userId) {
|
||||
return mPermissionUpgradeNeeded.get(userId, true);
|
||||
}
|
||||
|
||||
@GuardedBy("Settings.this.mLock")
|
||||
public void updateRuntimePermissionsFingerprintLPr(@UserIdInt int userId) {
|
||||
if (mExtendedFingerprint == null) {
|
||||
throw new RuntimeException("The version of the permission controller hasn't been "
|
||||
+ "set before trying to update the fingerprint.");
|
||||
int getVersion(int userId) {
|
||||
synchronized (mLock) {
|
||||
return mVersions.get(userId, INITIAL_VERSION);
|
||||
}
|
||||
}
|
||||
|
||||
void setVersion(int version, int userId) {
|
||||
synchronized (mLock) {
|
||||
mVersions.put(userId, version);
|
||||
writeStateForUserAsync(userId);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isPermissionUpgradeNeeded(int userId) {
|
||||
synchronized (mLock) {
|
||||
return mPermissionUpgradeNeeded.get(userId, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
|
||||
synchronized (mLock) {
|
||||
if (mExtendedFingerprint == null) {
|
||||
throw new RuntimeException(
|
||||
"The version of the permission controller hasn't been "
|
||||
+ "set before trying to update the fingerprint.");
|
||||
}
|
||||
mFingerprints.put(userId, mExtendedFingerprint);
|
||||
writeStateForUserAsync(userId);
|
||||
}
|
||||
mFingerprints.put(userId, mExtendedFingerprint);
|
||||
writeStateForUserAsyncLPr(userId);
|
||||
}
|
||||
|
||||
public void setPermissionControllerVersion(long version) {
|
||||
int numUser = mFingerprints.size();
|
||||
mExtendedFingerprint = getExtendedFingerprint(version);
|
||||
synchronized (mLock) {
|
||||
int numUser = mFingerprints.size();
|
||||
mExtendedFingerprint = getExtendedFingerprint(version);
|
||||
|
||||
for (int i = 0; i < numUser; i++) {
|
||||
int userId = mFingerprints.keyAt(i);
|
||||
String fingerprint = mFingerprints.valueAt(i);
|
||||
mPermissionUpgradeNeeded.put(userId,
|
||||
!TextUtils.equals(mExtendedFingerprint, fingerprint));
|
||||
for (int i = 0; i < numUser; i++) {
|
||||
int userId = mFingerprints.keyAt(i);
|
||||
String fingerprint = mFingerprints.valueAt(i);
|
||||
mPermissionUpgradeNeeded.put(userId,
|
||||
!TextUtils.equals(mExtendedFingerprint, fingerprint));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5446,84 +5472,92 @@ public final class Settings implements Watchable, Snappable {
|
||||
return PackagePartitions.FINGERPRINT + "?pc_version=" + version;
|
||||
}
|
||||
|
||||
public void writeStateForUserAsyncLPr(int userId) {
|
||||
final long currentTimeMillis = SystemClock.uptimeMillis();
|
||||
public void writeStateForUserAsync(int userId) {
|
||||
synchronized (mLock) {
|
||||
final long currentTimeMillis = SystemClock.uptimeMillis();
|
||||
|
||||
if (mWriteScheduled.get(userId)) {
|
||||
mHandler.removeMessages(userId);
|
||||
if (mWriteScheduled.get(userId)) {
|
||||
mHandler.removeMessages(userId);
|
||||
|
||||
// If enough time passed, write without holding off anymore.
|
||||
final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
|
||||
.get(userId);
|
||||
final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
|
||||
- lastNotWrittenMutationTimeMillis;
|
||||
if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
|
||||
mHandler.obtainMessage(userId).sendToTarget();
|
||||
return;
|
||||
// If enough time passed, write without holding off anymore.
|
||||
final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
|
||||
.get(userId);
|
||||
final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis
|
||||
- lastNotWrittenMutationTimeMillis;
|
||||
if (timeSinceLastNotWrittenMutationMillis
|
||||
>= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
|
||||
mHandler.obtainMessage(userId).sendToTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
// Hold off a bit more as settings are frequently changing.
|
||||
final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
|
||||
+ MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
|
||||
final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
|
||||
maxDelayMillis);
|
||||
|
||||
Message message = mHandler.obtainMessage(userId);
|
||||
mHandler.sendMessageDelayed(message, writeDelayMillis);
|
||||
} else {
|
||||
mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
|
||||
Message message = mHandler.obtainMessage(userId);
|
||||
mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
|
||||
mWriteScheduled.put(userId, true);
|
||||
}
|
||||
|
||||
// Hold off a bit more as settings are frequently changing.
|
||||
final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis
|
||||
+ MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0);
|
||||
final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
|
||||
maxDelayMillis);
|
||||
|
||||
Message message = mHandler.obtainMessage(userId);
|
||||
mHandler.sendMessageDelayed(message, writeDelayMillis);
|
||||
} else {
|
||||
mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
|
||||
Message message = mHandler.obtainMessage(userId);
|
||||
mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
|
||||
mWriteScheduled.put(userId, true);
|
||||
}
|
||||
}
|
||||
|
||||
public void writeStateForUserSyncLPr(int userId) {
|
||||
mHandler.removeMessages(userId);
|
||||
mWriteScheduled.delete(userId);
|
||||
public void writeStateForUserSync(int userId, @NonNull LegacyPermissionDataProvider
|
||||
legacyPermissionDataProvider,
|
||||
@NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
|
||||
@NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers) {
|
||||
synchronized (mLock) {
|
||||
mHandler.removeMessages(userId);
|
||||
mWriteScheduled.delete(userId);
|
||||
|
||||
mPermissionDataProvider.writeLegacyPermissionStateTEMP();
|
||||
legacyPermissionDataProvider.writeLegacyPermissionStateTEMP();
|
||||
|
||||
int version = mVersions.get(userId, INITIAL_VERSION);
|
||||
int version = mVersions.get(userId, INITIAL_VERSION);
|
||||
|
||||
String fingerprint = mFingerprints.get(userId);
|
||||
String fingerprint = mFingerprints.get(userId);
|
||||
|
||||
Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
|
||||
new ArrayMap<>();
|
||||
int packagesSize = mPackages.size();
|
||||
for (int i = 0; i < packagesSize; i++) {
|
||||
String packageName = mPackages.keyAt(i);
|
||||
PackageSetting packageSetting = mPackages.valueAt(i);
|
||||
if (packageSetting.getSharedUser() == null) {
|
||||
Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
|
||||
new ArrayMap<>();
|
||||
int packagesSize = packageStates.size();
|
||||
for (int i = 0; i < packagesSize; i++) {
|
||||
String packageName = packageStates.keyAt(i);
|
||||
PackageStateInternal packageState = packageStates.valueAt(i);
|
||||
if (packageState.getSharedUser() == null) {
|
||||
List<RuntimePermissionsState.PermissionState> permissions =
|
||||
getPermissionsFromPermissionsState(
|
||||
packageState.getLegacyPermissionState(), userId);
|
||||
if (permissions.isEmpty() && !packageState.isInstallPermissionsFixed()) {
|
||||
// Storing an empty state means the package is known to the system and
|
||||
// its install permissions have been granted and fixed. If this is not
|
||||
// the case, we should not store anything.
|
||||
continue;
|
||||
}
|
||||
packagePermissions.put(packageName, permissions);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
|
||||
new ArrayMap<>();
|
||||
final int sharedUsersSize = sharedUsers.size();
|
||||
for (int i = 0; i < sharedUsersSize; i++) {
|
||||
String sharedUserName = sharedUsers.keyAt(i);
|
||||
SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i);
|
||||
List<RuntimePermissionsState.PermissionState> permissions =
|
||||
getPermissionsFromPermissionsState(
|
||||
packageSetting.getLegacyPermissionState(), userId);
|
||||
if (permissions.isEmpty() && !packageSetting.isInstallPermissionsFixed()) {
|
||||
// Storing an empty state means the package is known to the system and its
|
||||
// install permissions have been granted and fixed. If this is not the case,
|
||||
// we should not store anything.
|
||||
continue;
|
||||
}
|
||||
packagePermissions.put(packageName, permissions);
|
||||
sharedUserSetting.getLegacyPermissionState(), userId);
|
||||
sharedUserPermissions.put(sharedUserName, permissions);
|
||||
}
|
||||
|
||||
RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version,
|
||||
fingerprint, packagePermissions, sharedUserPermissions);
|
||||
|
||||
mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
|
||||
}
|
||||
|
||||
Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
|
||||
new ArrayMap<>();
|
||||
final int sharedUsersSize = mSharedUsers.size();
|
||||
for (int i = 0; i < sharedUsersSize; i++) {
|
||||
String sharedUserName = mSharedUsers.keyAt(i);
|
||||
SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
|
||||
List<RuntimePermissionsState.PermissionState> permissions =
|
||||
getPermissionsFromPermissionsState(
|
||||
sharedUserSetting.getLegacyPermissionState(), userId);
|
||||
sharedUserPermissions.put(sharedUserName, permissions);
|
||||
}
|
||||
|
||||
RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version,
|
||||
fingerprint, packagePermissions, sharedUserPermissions);
|
||||
|
||||
mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@@ -5541,82 +5575,91 @@ public final class Settings implements Watchable, Snappable {
|
||||
return permissions;
|
||||
}
|
||||
|
||||
@GuardedBy("Settings.this.mLock")
|
||||
private void onUserRemovedLPw(int userId) {
|
||||
// Make sure we do not
|
||||
mHandler.removeMessages(userId);
|
||||
private void onUserRemoved(int userId) {
|
||||
synchronized (mLock) {
|
||||
// Make sure we do not
|
||||
mHandler.removeMessages(userId);
|
||||
|
||||
mPermissionUpgradeNeeded.delete(userId);
|
||||
mVersions.delete(userId);
|
||||
mFingerprints.remove(userId);
|
||||
mPermissionUpgradeNeeded.delete(userId);
|
||||
mVersions.delete(userId);
|
||||
mFingerprints.remove(userId);
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteUserRuntimePermissionsFile(int userId) {
|
||||
mPersistence.deleteForUser(UserHandle.of(userId));
|
||||
synchronized (mLock) {
|
||||
mPersistence.deleteForUser(UserHandle.of(userId));
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("Settings.this.mLock")
|
||||
public void readStateForUserSyncLPr(int userId) {
|
||||
RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of(
|
||||
userId));
|
||||
if (runtimePermissions == null) {
|
||||
readLegacyStateForUserSyncLPr(userId);
|
||||
writeStateForUserAsyncLPr(userId);
|
||||
return;
|
||||
}
|
||||
|
||||
// If the runtime permissions file exists but the version is not set this is
|
||||
// an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION.
|
||||
int version = runtimePermissions.getVersion();
|
||||
if (version == RuntimePermissionsState.NO_VERSION) {
|
||||
version = UPGRADE_VERSION;
|
||||
}
|
||||
mVersions.put(userId, version);
|
||||
|
||||
String fingerprint = runtimePermissions.getFingerprint();
|
||||
mFingerprints.put(userId, fingerprint);
|
||||
|
||||
boolean isUpgradeToR = getInternalVersion().sdkVersion < Build.VERSION_CODES.R;
|
||||
|
||||
Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
|
||||
runtimePermissions.getPackagePermissions();
|
||||
int packagesSize = mPackages.size();
|
||||
for (int i = 0; i < packagesSize; i++) {
|
||||
String packageName = mPackages.keyAt(i);
|
||||
PackageSetting packageSetting = mPackages.valueAt(i);
|
||||
|
||||
List<RuntimePermissionsState.PermissionState> permissions =
|
||||
packagePermissions.get(packageName);
|
||||
if (permissions != null) {
|
||||
readPermissionsStateLpr(permissions, packageSetting.getLegacyPermissionState(),
|
||||
userId);
|
||||
packageSetting.setInstallPermissionsFixed(true);
|
||||
} else if (packageSetting.getSharedUser() == null && !isUpgradeToR) {
|
||||
Slog.w(TAG, "Missing permission state for package: " + packageName);
|
||||
packageSetting.getLegacyPermissionState().setMissing(true, userId);
|
||||
public void readStateForUserSync(int userId, @NonNull VersionInfo internalVersion,
|
||||
@NonNull WatchedArrayMap<String, PackageSetting> packageSettings,
|
||||
@NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers,
|
||||
@NonNull File userRuntimePermissionsFile) {
|
||||
synchronized (mLock) {
|
||||
RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of(
|
||||
userId));
|
||||
if (runtimePermissions == null) {
|
||||
readLegacyStateForUserSync(userId, userRuntimePermissionsFile, packageSettings,
|
||||
sharedUsers);
|
||||
writeStateForUserAsync(userId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
|
||||
runtimePermissions.getSharedUserPermissions();
|
||||
int sharedUsersSize = mSharedUsers.size();
|
||||
for (int i = 0; i < sharedUsersSize; i++) {
|
||||
String sharedUserName = mSharedUsers.keyAt(i);
|
||||
SharedUserSetting sharedUserSetting = mSharedUsers.valueAt(i);
|
||||
// If the runtime permissions file exists but the version is not set this is
|
||||
// an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION.
|
||||
int version = runtimePermissions.getVersion();
|
||||
if (version == RuntimePermissionsState.NO_VERSION) {
|
||||
version = UPGRADE_VERSION;
|
||||
}
|
||||
mVersions.put(userId, version);
|
||||
|
||||
List<RuntimePermissionsState.PermissionState> permissions =
|
||||
sharedUserPermissions.get(sharedUserName);
|
||||
if (permissions != null) {
|
||||
readPermissionsStateLpr(permissions,
|
||||
sharedUserSetting.getLegacyPermissionState(), userId);
|
||||
} else if (!isUpgradeToR) {
|
||||
Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
|
||||
sharedUserSetting.getLegacyPermissionState().setMissing(true, userId);
|
||||
String fingerprint = runtimePermissions.getFingerprint();
|
||||
mFingerprints.put(userId, fingerprint);
|
||||
|
||||
boolean isUpgradeToR = internalVersion.sdkVersion < Build.VERSION_CODES.R;
|
||||
|
||||
Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
|
||||
runtimePermissions.getPackagePermissions();
|
||||
int packagesSize = packageSettings.size();
|
||||
for (int i = 0; i < packagesSize; i++) {
|
||||
String packageName = packageSettings.keyAt(i);
|
||||
PackageSetting packageSetting = packageSettings.valueAt(i);
|
||||
|
||||
List<RuntimePermissionsState.PermissionState> permissions =
|
||||
packagePermissions.get(packageName);
|
||||
if (permissions != null) {
|
||||
readPermissionsState(permissions,
|
||||
packageSetting.getLegacyPermissionState(),
|
||||
userId);
|
||||
packageSetting.setInstallPermissionsFixed(true);
|
||||
} else if (packageSetting.getSharedUser() == null && !isUpgradeToR) {
|
||||
Slog.w(TAG, "Missing permission state for package: " + packageName);
|
||||
packageSetting.getLegacyPermissionState().setMissing(true, userId);
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, List<RuntimePermissionsState.PermissionState>> sharedUserPermissions =
|
||||
runtimePermissions.getSharedUserPermissions();
|
||||
int sharedUsersSize = sharedUsers.size();
|
||||
for (int i = 0; i < sharedUsersSize; i++) {
|
||||
String sharedUserName = sharedUsers.keyAt(i);
|
||||
SharedUserSetting sharedUserSetting = sharedUsers.valueAt(i);
|
||||
|
||||
List<RuntimePermissionsState.PermissionState> permissions =
|
||||
sharedUserPermissions.get(sharedUserName);
|
||||
if (permissions != null) {
|
||||
readPermissionsState(permissions,
|
||||
sharedUserSetting.getLegacyPermissionState(), userId);
|
||||
} else if (!isUpgradeToR) {
|
||||
Slog.w(TAG, "Missing permission state for shared user: " + sharedUserName);
|
||||
sharedUserSetting.getLegacyPermissionState().setMissing(true, userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void readPermissionsStateLpr(
|
||||
private void readPermissionsState(
|
||||
@NonNull List<RuntimePermissionsState.PermissionState> permissions,
|
||||
@NonNull LegacyPermissionState permissionsState, @UserIdInt int userId) {
|
||||
int permissionsSize = permissions.size();
|
||||
@@ -5630,77 +5673,86 @@ public final class Settings implements Watchable, Snappable {
|
||||
}
|
||||
}
|
||||
|
||||
@GuardedBy("Settings.this.mLock")
|
||||
private void readLegacyStateForUserSyncLPr(int userId) {
|
||||
File permissionsFile = getUserRuntimePermissionsFile(userId);
|
||||
if (!permissionsFile.exists()) {
|
||||
return;
|
||||
}
|
||||
private void readLegacyStateForUserSync(int userId, @NonNull File permissionsFile,
|
||||
@NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
|
||||
@NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers) {
|
||||
synchronized (mLock) {
|
||||
if (!permissionsFile.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FileInputStream in;
|
||||
try {
|
||||
in = new AtomicFile(permissionsFile).openRead();
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
Slog.i(PackageManagerService.TAG, "No permissions state");
|
||||
return;
|
||||
}
|
||||
FileInputStream in;
|
||||
try {
|
||||
in = new AtomicFile(permissionsFile).openRead();
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
Slog.i(PackageManagerService.TAG, "No permissions state");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final TypedXmlPullParser parser = Xml.resolvePullParser(in);
|
||||
parseLegacyRuntimePermissionsLPr(parser, userId);
|
||||
try {
|
||||
final TypedXmlPullParser parser = Xml.resolvePullParser(in);
|
||||
parseLegacyRuntimePermissions(parser, userId, packageStates, sharedUsers);
|
||||
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
throw new IllegalStateException("Failed parsing permissions file: "
|
||||
+ permissionsFile, e);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(in);
|
||||
} catch (XmlPullParserException | IOException e) {
|
||||
throw new IllegalStateException("Failed parsing permissions file: "
|
||||
+ permissionsFile, e);
|
||||
} finally {
|
||||
IoUtils.closeQuietly(in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Private internals
|
||||
|
||||
@GuardedBy("Settings.this.mLock")
|
||||
private void parseLegacyRuntimePermissionsLPr(TypedXmlPullParser parser, int userId)
|
||||
private void parseLegacyRuntimePermissions(TypedXmlPullParser parser, int userId,
|
||||
@NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
|
||||
@NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers)
|
||||
throws IOException, XmlPullParserException {
|
||||
final int outerDepth = parser.getDepth();
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
synchronized (mLock) {
|
||||
final int outerDepth = parser.getDepth();
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (parser.getName()) {
|
||||
case TAG_RUNTIME_PERMISSIONS: {
|
||||
// If the permisions settings file exists but the version is not set this is
|
||||
// an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION
|
||||
int version = parser.getAttributeInt(null, ATTR_VERSION, UPGRADE_VERSION);
|
||||
mVersions.put(userId, version);
|
||||
String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT);
|
||||
mFingerprints.put(userId, fingerprint);
|
||||
} break;
|
||||
|
||||
case TAG_PACKAGE: {
|
||||
String name = parser.getAttributeValue(null, ATTR_NAME);
|
||||
PackageSetting ps = mPackages.get(name);
|
||||
if (ps == null) {
|
||||
Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
continue;
|
||||
switch (parser.getName()) {
|
||||
case TAG_RUNTIME_PERMISSIONS: {
|
||||
// If the permisions settings file exists but the version is not set this is
|
||||
// an upgrade from P->Q. Hence mark it with the special UPGRADE_VERSION
|
||||
int version = parser.getAttributeInt(null, ATTR_VERSION,
|
||||
UPGRADE_VERSION);
|
||||
mVersions.put(userId, version);
|
||||
String fingerprint = parser.getAttributeValue(null, ATTR_FINGERPRINT);
|
||||
mFingerprints.put(userId, fingerprint);
|
||||
}
|
||||
parseLegacyPermissionsLPr(parser, ps.getLegacyPermissionState(), userId);
|
||||
} break;
|
||||
break;
|
||||
|
||||
case TAG_SHARED_USER: {
|
||||
String name = parser.getAttributeValue(null, ATTR_NAME);
|
||||
SharedUserSetting sus = mSharedUsers.get(name);
|
||||
if (sus == null) {
|
||||
Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
continue;
|
||||
case TAG_PACKAGE: {
|
||||
String name = parser.getAttributeValue(null, ATTR_NAME);
|
||||
PackageStateInternal ps = packageStates.get(name);
|
||||
if (ps == null) {
|
||||
Slog.w(PackageManagerService.TAG, "Unknown package:" + name);
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
continue;
|
||||
}
|
||||
parseLegacyPermissionsLPr(parser, ps.getLegacyPermissionState(),
|
||||
userId);
|
||||
}
|
||||
parseLegacyPermissionsLPr(parser, sus.getLegacyPermissionState(), userId);
|
||||
} break;
|
||||
break;
|
||||
|
||||
case TAG_SHARED_USER: {
|
||||
String name = parser.getAttributeValue(null, ATTR_NAME);
|
||||
SharedUserSetting sus = sharedUsers.get(name);
|
||||
if (sus == null) {
|
||||
Slog.w(PackageManagerService.TAG, "Unknown shared user:" + name);
|
||||
XmlUtils.skipCurrentTag(parser);
|
||||
continue;
|
||||
}
|
||||
parseLegacyPermissionsLPr(parser, sus.getLegacyPermissionState(),
|
||||
userId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5708,25 +5760,27 @@ public final class Settings implements Watchable, Snappable {
|
||||
private void parseLegacyPermissionsLPr(TypedXmlPullParser parser,
|
||||
LegacyPermissionState permissionsState, int userId)
|
||||
throws IOException, XmlPullParserException {
|
||||
final int outerDepth = parser.getDepth();
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (parser.getName()) {
|
||||
case TAG_ITEM: {
|
||||
String name = parser.getAttributeValue(null, ATTR_NAME);
|
||||
final boolean granted =
|
||||
parser.getAttributeBoolean(null, ATTR_GRANTED, true);
|
||||
final int flags =
|
||||
parser.getAttributeIntHex(null, ATTR_FLAGS, 0);
|
||||
permissionsState.putPermissionState(new PermissionState(name, true,
|
||||
granted, flags), userId);
|
||||
synchronized (mLock) {
|
||||
final int outerDepth = parser.getDepth();
|
||||
int type;
|
||||
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (parser.getName()) {
|
||||
case TAG_ITEM: {
|
||||
String name = parser.getAttributeValue(null, ATTR_NAME);
|
||||
final boolean granted =
|
||||
parser.getAttributeBoolean(null, ATTR_GRANTED, true);
|
||||
final int flags =
|
||||
parser.getAttributeIntHex(null, ATTR_FLAGS, 0);
|
||||
permissionsState.putPermissionState(new PermissionState(name, true,
|
||||
granted, flags), userId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5740,9 +5794,7 @@ public final class Settings implements Watchable, Snappable {
|
||||
public void handleMessage(Message message) {
|
||||
final int userId = message.what;
|
||||
Runnable callback = (Runnable) message.obj;
|
||||
synchronized (mLock) {
|
||||
writeStateForUserSyncLPr(userId);
|
||||
}
|
||||
mInvokeWriteUserStateAsyncCallback.accept(userId);
|
||||
if (callback != null) {
|
||||
callback.run();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.content.pm.SigningDetails;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.server.pm.InstallSource;
|
||||
import com.android.server.pm.PackageKeySetData;
|
||||
import com.android.server.pm.SharedUserSetting;
|
||||
import com.android.server.pm.parsing.pkg.AndroidPackage;
|
||||
import com.android.server.pm.permission.LegacyPermissionState;
|
||||
@@ -82,4 +83,7 @@ public interface PackageStateInternal extends PackageState {
|
||||
String getPathString();
|
||||
|
||||
float getLoadingProgress();
|
||||
|
||||
@NonNull
|
||||
PackageKeySetData getKeySetData();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import android.util.Pair;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
import com.android.internal.util.CollectionUtils;
|
||||
import com.android.internal.util.DataClass;
|
||||
|
||||
import java.util.Objects;
|
||||
@@ -49,7 +50,6 @@ public class PackageUserStateImpl implements PackageUserStateInternal {
|
||||
private boolean mNotLaunched;
|
||||
private boolean mHidden; // Is the app restricted by owner / admin
|
||||
private int mDistractionFlags;
|
||||
private boolean mSuspended;
|
||||
private boolean mInstantApp;
|
||||
private boolean mVirtualPreload;
|
||||
private int mEnabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
|
||||
@@ -101,7 +101,6 @@ public class PackageUserStateImpl implements PackageUserStateInternal {
|
||||
mNotLaunched = other.mNotLaunched;
|
||||
mHidden = other.mHidden;
|
||||
mDistractionFlags = other.mDistractionFlags;
|
||||
mSuspended = other.mSuspended;
|
||||
mInstantApp = other.mInstantApp;
|
||||
mVirtualPreload = other.mVirtualPreload;
|
||||
mEnabledState = other.mEnabledState;
|
||||
@@ -256,6 +255,28 @@ public class PackageUserStateImpl implements PackageUserStateInternal {
|
||||
return mComponentLabelIconOverrideMap.get(componentName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuspended() {
|
||||
return !CollectionUtils.isEmpty(mSuspendParams);
|
||||
}
|
||||
|
||||
public PackageUserStateImpl putSuspendParams(@NonNull String suspendingPackage,
|
||||
@NonNull SuspendParams suspendParams) {
|
||||
if (mSuspendParams == null) {
|
||||
mSuspendParams = new ArrayMap<>();
|
||||
}
|
||||
mSuspendParams.put(suspendingPackage, suspendParams);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PackageUserStateImpl removeSuspension(@NonNull String suspendingPackage) {
|
||||
if (mSuspendParams != null) {
|
||||
mSuspendParams.remove(suspendingPackage);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// Code below generated by codegen v1.0.23.
|
||||
@@ -311,11 +332,6 @@ public class PackageUserStateImpl implements PackageUserStateInternal {
|
||||
return mDistractionFlags;
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public boolean isSuspended() {
|
||||
return mSuspended;
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public boolean isInstantApp() {
|
||||
return mInstantApp;
|
||||
@@ -432,12 +448,6 @@ public class PackageUserStateImpl implements PackageUserStateInternal {
|
||||
return this;
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull PackageUserStateImpl setSuspended( boolean value) {
|
||||
mSuspended = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@DataClass.Generated.Member
|
||||
public @NonNull PackageUserStateImpl setInstantApp( boolean value) {
|
||||
mInstantApp = value;
|
||||
@@ -532,7 +542,6 @@ public class PackageUserStateImpl implements PackageUserStateInternal {
|
||||
&& mNotLaunched == that.mNotLaunched
|
||||
&& mHidden == that.mHidden
|
||||
&& mDistractionFlags == that.mDistractionFlags
|
||||
&& mSuspended == that.mSuspended
|
||||
&& mInstantApp == that.mInstantApp
|
||||
&& mVirtualPreload == that.mVirtualPreload
|
||||
&& mEnabledState == that.mEnabledState
|
||||
@@ -563,7 +572,6 @@ public class PackageUserStateImpl implements PackageUserStateInternal {
|
||||
_hash = 31 * _hash + Boolean.hashCode(mNotLaunched);
|
||||
_hash = 31 * _hash + Boolean.hashCode(mHidden);
|
||||
_hash = 31 * _hash + mDistractionFlags;
|
||||
_hash = 31 * _hash + Boolean.hashCode(mSuspended);
|
||||
_hash = 31 * _hash + Boolean.hashCode(mInstantApp);
|
||||
_hash = 31 * _hash + Boolean.hashCode(mVirtualPreload);
|
||||
_hash = 31 * _hash + mEnabledState;
|
||||
@@ -581,10 +589,10 @@ public class PackageUserStateImpl implements PackageUserStateInternal {
|
||||
}
|
||||
|
||||
@DataClass.Generated(
|
||||
time = 1633983318771L,
|
||||
time = 1636145886996L,
|
||||
codegenVersion = "1.0.23",
|
||||
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
|
||||
inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mSuspended\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
|
||||
inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
|
||||
@Deprecated
|
||||
private void __metadata() {}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ public interface PackageUserStateInternal extends PackageUserState {
|
||||
|
||||
PackageUserStateInternal DEFAULT = new PackageUserStateDefault();
|
||||
|
||||
// TODO: Make non-null with emptyMap()
|
||||
@Nullable
|
||||
ArrayMap<String, SuspendParams> getSuspendParams();
|
||||
|
||||
|
||||
@@ -42,11 +42,15 @@ public final class SuspendParams {
|
||||
private static final String TAG_APP_EXTRAS = "app-extras";
|
||||
private static final String TAG_LAUNCHER_EXTRAS = "launcher-extras";
|
||||
|
||||
public SuspendDialogInfo dialogInfo;
|
||||
public PersistableBundle appExtras;
|
||||
public PersistableBundle launcherExtras;
|
||||
private final SuspendDialogInfo dialogInfo;
|
||||
private final PersistableBundle appExtras;
|
||||
private final PersistableBundle launcherExtras;
|
||||
|
||||
private SuspendParams() {
|
||||
private SuspendParams(SuspendDialogInfo dialogInfo, PersistableBundle appExtras,
|
||||
PersistableBundle launcherExtras) {
|
||||
this.dialogInfo = dialogInfo;
|
||||
this.appExtras = appExtras;
|
||||
this.launcherExtras = launcherExtras;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -60,11 +64,7 @@ public final class SuspendParams {
|
||||
if (dialogInfo == null && appExtras == null && launcherExtras == null) {
|
||||
return null;
|
||||
}
|
||||
final SuspendParams instance = new SuspendParams();
|
||||
instance.dialogInfo = dialogInfo;
|
||||
instance.appExtras = appExtras;
|
||||
instance.launcherExtras = launcherExtras;
|
||||
return instance;
|
||||
return new SuspendParams(dialogInfo, appExtras, launcherExtras);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -172,4 +172,16 @@ public final class SuspendParams {
|
||||
}
|
||||
return getInstanceOrNull(readDialogInfo, readAppExtras, readLauncherExtras);
|
||||
}
|
||||
|
||||
public SuspendDialogInfo getDialogInfo() {
|
||||
return dialogInfo;
|
||||
}
|
||||
|
||||
public PersistableBundle getAppExtras() {
|
||||
return appExtras;
|
||||
}
|
||||
|
||||
public PersistableBundle getLauncherExtras() {
|
||||
return launcherExtras;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
* 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.pm.pkg.mutate;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.overlay.OverlayPaths;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.server.pm.PackageSetting;
|
||||
import com.android.server.pm.pkg.PackageUserStateImpl;
|
||||
import com.android.server.pm.pkg.SuspendParams;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class PackageStateMutator {
|
||||
|
||||
private static final AtomicLong sStateChangeSequence = new AtomicLong();
|
||||
|
||||
private final StateWriteWrapper mStateWrite = new StateWriteWrapper();
|
||||
|
||||
private final Function<String, PackageSetting> mActiveStateFunction;
|
||||
private final Function<String, PackageSetting> mDisabledStateFunction;
|
||||
|
||||
public PackageStateMutator(@NonNull Function<String, PackageSetting> activeStateFunction,
|
||||
@NonNull Function<String, PackageSetting> disabledStateFunction) {
|
||||
mActiveStateFunction = activeStateFunction;
|
||||
mDisabledStateFunction = disabledStateFunction;
|
||||
}
|
||||
|
||||
public static void onPackageStateChanged() {
|
||||
sStateChangeSequence.incrementAndGet();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PackageStateWrite forPackage(@NonNull String packageName) {
|
||||
return mStateWrite.setState(mActiveStateFunction.apply(packageName));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PackageStateWrite forPackageNullable(@NonNull String packageName) {
|
||||
final PackageSetting packageState = mActiveStateFunction.apply(packageName);
|
||||
mStateWrite.setState(packageState);
|
||||
if (packageState == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mStateWrite.setState(packageState);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public PackageStateWrite forDisabledSystemPackage(@NonNull String packageName) {
|
||||
return mStateWrite.setState(mDisabledStateFunction.apply(packageName));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PackageStateWrite forDisabledSystemPackageNullable(@NonNull String packageName) {
|
||||
final PackageSetting packageState = mDisabledStateFunction.apply(packageName);
|
||||
if (packageState == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mStateWrite.setState(packageState);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public InitialState initialState(int changedPackagesSequenceNumber) {
|
||||
return new InitialState(changedPackagesSequenceNumber, sStateChangeSequence.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return null if initial state is null or if nothing has changed, otherwise return result
|
||||
* with what changed
|
||||
*/
|
||||
@Nullable
|
||||
public Result generateResult(@Nullable InitialState state, int changedPackagesSequenceNumber) {
|
||||
if (state == null) {
|
||||
return Result.SUCCESS;
|
||||
}
|
||||
|
||||
boolean packagesChanged = changedPackagesSequenceNumber != state.mPackageSequence;
|
||||
boolean stateChanged = sStateChangeSequence.get() != state.mStateSequence;
|
||||
if (packagesChanged && stateChanged) {
|
||||
return Result.PACKAGES_AND_STATE_CHANGED;
|
||||
} else if (packagesChanged) {
|
||||
return Result.PACKAGES_CHANGED;
|
||||
} else if (stateChanged) {
|
||||
return Result.STATE_CHANGED;
|
||||
} else {
|
||||
return Result.SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
public static class InitialState {
|
||||
|
||||
private final int mPackageSequence;
|
||||
private final long mStateSequence;
|
||||
|
||||
public InitialState(int packageSequence, long stateSequence) {
|
||||
mPackageSequence = packageSequence;
|
||||
mStateSequence = stateSequence;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Result {
|
||||
|
||||
public static final Result SUCCESS = new Result(true, false, false, false);
|
||||
public static final Result PACKAGES_CHANGED = new Result(false, true, false, false);
|
||||
public static final Result STATE_CHANGED = new Result(false, false, true, false);
|
||||
public static final Result PACKAGES_AND_STATE_CHANGED = new Result(false, true, true, false);
|
||||
public static final Result SPECIFIC_PACKAGE_NULL = new Result(false, false, true, true);
|
||||
|
||||
private final boolean mCommitted;
|
||||
private final boolean mPackagesChanged;
|
||||
private final boolean mStateChanged;
|
||||
private final boolean mSpecificPackageNull;
|
||||
|
||||
public Result(boolean committed, boolean packagesChanged, boolean stateChanged,
|
||||
boolean specificPackageNull) {
|
||||
mCommitted = committed;
|
||||
mPackagesChanged = packagesChanged;
|
||||
mStateChanged = stateChanged;
|
||||
mSpecificPackageNull = specificPackageNull;
|
||||
}
|
||||
|
||||
public boolean isCommitted() {
|
||||
return mCommitted;
|
||||
}
|
||||
|
||||
public boolean isPackagesChanged() {
|
||||
return mPackagesChanged;
|
||||
}
|
||||
|
||||
public boolean isStateChanged() {
|
||||
return mStateChanged;
|
||||
}
|
||||
|
||||
public boolean isSpecificPackageNull() {
|
||||
return mSpecificPackageNull;
|
||||
}
|
||||
}
|
||||
|
||||
private static class StateWriteWrapper implements PackageStateWrite {
|
||||
|
||||
private final UserStateWriteWrapper mUserStateWrite = new UserStateWriteWrapper();
|
||||
|
||||
@NonNull
|
||||
private PackageSetting mState;
|
||||
|
||||
public StateWriteWrapper setState(PackageSetting state) {
|
||||
this.mState = state;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite userState(int userId) {
|
||||
return mUserStateWrite.setStates(
|
||||
mState == null ? null : mState.getOrCreateUserState(userId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageStateWrite setLastPackageUsageTime(int reason, long timeInMillis) {
|
||||
if (mState != null) {
|
||||
mState.getTransientState().setLastPackageUsageTimeInMills(reason, timeInMillis);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PackageStateWrite setHiddenUntilInstalled(boolean value) {
|
||||
if (mState != null) {
|
||||
mState.getTransientState().setHiddenUntilInstalled(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageStateWrite setRequiredForSystemUser(boolean requiredForSystemUser) {
|
||||
if (mState != null) {
|
||||
if (requiredForSystemUser) {
|
||||
mState.setPrivateFlags(mState.getPrivateFlags()
|
||||
| ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
|
||||
} else {
|
||||
mState.setPrivateFlags(mState.getPrivateFlags()
|
||||
& ~ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageStateWrite setMimeGroup(@NonNull String mimeGroup,
|
||||
@NonNull ArraySet<String> mimeTypes) {
|
||||
if (mState != null) {
|
||||
mState.setMimeGroup(mimeGroup, mimeTypes);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
private static class UserStateWriteWrapper implements PackageUserStateWrite {
|
||||
|
||||
@Nullable
|
||||
private PackageUserStateImpl mUserState;
|
||||
|
||||
public UserStateWriteWrapper setStates(@Nullable PackageUserStateImpl userState) {
|
||||
mUserState = userState;
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite setInstalled(boolean installed) {
|
||||
if (mUserState != null) {
|
||||
mUserState.setInstalled(installed);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite setUninstallReason(int reason) {
|
||||
if (mUserState != null) {
|
||||
mUserState.setUninstallReason(reason);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite setDistractionFlags(
|
||||
@PackageManager.DistractionRestriction int restrictionFlags) {
|
||||
if (mUserState != null) {
|
||||
mUserState.setDistractionFlags(restrictionFlags);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
|
||||
@Nullable SuspendParams suspendParams) {
|
||||
if (mUserState != null) {
|
||||
mUserState.putSuspendParams(suspendingPackage, suspendParams);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage) {
|
||||
if (mUserState != null) {
|
||||
mUserState.removeSuspension(suspendingPackage);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite setHidden(boolean hidden) {
|
||||
if (mUserState != null) {
|
||||
mUserState.setHidden(hidden);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite setStopped(boolean stopped) {
|
||||
if (mUserState != null) {
|
||||
mUserState.setStopped(stopped);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite setNotLaunched(boolean notLaunched) {
|
||||
if (mUserState != null) {
|
||||
mUserState.setNotLaunched(notLaunched);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite setOverlayPaths(@NonNull OverlayPaths overlayPaths) {
|
||||
if (mUserState != null) {
|
||||
mUserState.setOverlayPaths(overlayPaths);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite setOverlayPathsForLibrary(@NonNull String libraryName,
|
||||
@Nullable OverlayPaths overlayPaths) {
|
||||
if (mUserState != null) {
|
||||
mUserState.setSharedLibraryOverlayPaths(libraryName, overlayPaths);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public PackageUserStateWrite setHarmfulAppWarning(@Nullable String warning) {
|
||||
if (mUserState != null) {
|
||||
mUserState.setHarmfulAppWarning(warning);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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.pm.pkg.mutate;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.ArraySet;
|
||||
|
||||
public interface PackageStateWrite {
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite userState(@UserIdInt int userId);
|
||||
|
||||
@NonNull
|
||||
PackageStateWrite setLastPackageUsageTime(@PackageManager.NotifyReason int reason,
|
||||
long timeInMillis);
|
||||
|
||||
@NonNull
|
||||
PackageStateWrite setHiddenUntilInstalled(boolean hiddenUntilInstalled);
|
||||
|
||||
@NonNull
|
||||
PackageStateWrite setRequiredForSystemUser(boolean requiredForSystemUser);
|
||||
|
||||
@NonNull
|
||||
PackageStateWrite setMimeGroup(@NonNull String mimeGroup, @NonNull ArraySet<String> mimeTypes);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.pm.pkg.mutate;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.overlay.OverlayPaths;
|
||||
|
||||
import com.android.server.pm.pkg.SuspendParams;
|
||||
|
||||
public interface PackageUserStateWrite {
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite setInstalled(boolean installed);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite setUninstallReason(@PackageManager.UninstallReason int reason);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite setDistractionFlags(
|
||||
@PackageManager.DistractionRestriction int restrictionFlags);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite putSuspendParams(@NonNull String suspendingPackage,
|
||||
@Nullable SuspendParams suspendParams);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite removeSuspension(@NonNull String suspendingPackage);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite setHidden(boolean hidden);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite setStopped(boolean stopped);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite setNotLaunched(boolean notLaunched);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite setOverlayPaths(@Nullable OverlayPaths overlayPaths);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite setOverlayPathsForLibrary(@NonNull String libraryName,
|
||||
@Nullable OverlayPaths overlayPaths);
|
||||
|
||||
@NonNull
|
||||
PackageUserStateWrite setHarmfulAppWarning(@Nullable String warning);
|
||||
}
|
||||
@@ -264,17 +264,17 @@ public class PackageManagerSettingsTests {
|
||||
final SuspendParams params = packageUserState1.getSuspendParams().valueAt(0);
|
||||
watcher.verifyNoChangeReported("fetch user state");
|
||||
assertThat(params, is(notNullValue()));
|
||||
assertThat(params.appExtras.size(), is(1));
|
||||
assertThat(params.appExtras.getString("app_extra_string"), is("value"));
|
||||
assertThat(params.launcherExtras.size(), is(1));
|
||||
assertThat(params.launcherExtras.getLong("launcher_extra_long"), is(4L));
|
||||
assertThat(params.dialogInfo, is(notNullValue()));
|
||||
assertThat(params.dialogInfo.getDialogMessage(), is("Dialog Message"));
|
||||
assertThat(params.dialogInfo.getTitleResId(), is(ID_NULL));
|
||||
assertThat(params.dialogInfo.getIconResId(), is(TEST_RESOURCE_ID));
|
||||
assertThat(params.dialogInfo.getNeutralButtonTextResId(), is(ID_NULL));
|
||||
assertThat(params.dialogInfo.getNeutralButtonAction(), is(BUTTON_ACTION_MORE_DETAILS));
|
||||
assertThat(params.dialogInfo.getDialogMessageResId(), is(ID_NULL));
|
||||
assertThat(params.getAppExtras().size(), is(1));
|
||||
assertThat(params.getAppExtras().getString("app_extra_string"), is("value"));
|
||||
assertThat(params.getLauncherExtras().size(), is(1));
|
||||
assertThat(params.getLauncherExtras().getLong("launcher_extra_long"), is(4L));
|
||||
assertThat(params.getDialogInfo(), is(notNullValue()));
|
||||
assertThat(params.getDialogInfo().getDialogMessage(), is("Dialog Message"));
|
||||
assertThat(params.getDialogInfo().getTitleResId(), is(ID_NULL));
|
||||
assertThat(params.getDialogInfo().getIconResId(), is(TEST_RESOURCE_ID));
|
||||
assertThat(params.getDialogInfo().getNeutralButtonTextResId(), is(ID_NULL));
|
||||
assertThat(params.getDialogInfo().getNeutralButtonAction(), is(BUTTON_ACTION_MORE_DETAILS));
|
||||
assertThat(params.getDialogInfo().getDialogMessageResId(), is(ID_NULL));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -351,18 +351,18 @@ public class PackageManagerSettingsTests {
|
||||
final SuspendParams params11 = readPus1.getSuspendParams().valueAt(0);
|
||||
watcher.verifyNoChangeReported("read package param");
|
||||
assertThat(params11, is(notNullValue()));
|
||||
assertThat(params11.dialogInfo, is(dialogInfo1));
|
||||
assertThat(BaseBundle.kindofEquals(params11.appExtras, appExtras1), is(true));
|
||||
assertThat(BaseBundle.kindofEquals(params11.launcherExtras, launcherExtras1),
|
||||
assertThat(params11.getDialogInfo(), is(dialogInfo1));
|
||||
assertThat(BaseBundle.kindofEquals(params11.getAppExtras(), appExtras1), is(true));
|
||||
assertThat(BaseBundle.kindofEquals(params11.getLauncherExtras(), launcherExtras1),
|
||||
is(true));
|
||||
watcher.verifyNoChangeReported("read package param");
|
||||
|
||||
assertThat(readPus1.getSuspendParams().keyAt(1), is("suspendingPackage2"));
|
||||
final SuspendParams params12 = readPus1.getSuspendParams().valueAt(1);
|
||||
assertThat(params12, is(notNullValue()));
|
||||
assertThat(params12.dialogInfo, is(dialogInfo2));
|
||||
assertThat(BaseBundle.kindofEquals(params12.appExtras, appExtras2), is(true));
|
||||
assertThat(BaseBundle.kindofEquals(params12.launcherExtras, launcherExtras2),
|
||||
assertThat(params12.getDialogInfo(), is(dialogInfo2));
|
||||
assertThat(BaseBundle.kindofEquals(params12.getAppExtras(), appExtras2), is(true));
|
||||
assertThat(BaseBundle.kindofEquals(params12.getLauncherExtras(), launcherExtras2),
|
||||
is(true));
|
||||
watcher.verifyNoChangeReported("read package param");
|
||||
|
||||
@@ -373,9 +373,9 @@ public class PackageManagerSettingsTests {
|
||||
assertThat(readPus2.getSuspendParams().keyAt(0), is("suspendingPackage3"));
|
||||
final SuspendParams params21 = readPus2.getSuspendParams().valueAt(0);
|
||||
assertThat(params21, is(notNullValue()));
|
||||
assertThat(params21.dialogInfo, is(nullValue()));
|
||||
assertThat(BaseBundle.kindofEquals(params21.appExtras, appExtras1), is(true));
|
||||
assertThat(params21.launcherExtras, is(nullValue()));
|
||||
assertThat(params21.getDialogInfo(), is(nullValue()));
|
||||
assertThat(BaseBundle.kindofEquals(params21.getAppExtras(), appExtras1), is(true));
|
||||
assertThat(params21.getLauncherExtras(), is(nullValue()));
|
||||
watcher.verifyNoChangeReported("read package param");
|
||||
|
||||
final PackageUserStateInternal readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3)
|
||||
@@ -388,7 +388,7 @@ public class PackageManagerSettingsTests {
|
||||
@Test
|
||||
public void testPackageRestrictionsSuspendedDefault() {
|
||||
final PackageSetting defaultSetting = createPackageSetting(PACKAGE_NAME_1);
|
||||
assertThat(defaultSetting.getSuspended(0), is(false));
|
||||
assertThat(defaultSetting.getUserStateOrDefault(0).isSuspended(), is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -82,7 +82,8 @@ public class PackageUserStateTest {
|
||||
assertThat(testUserState.equals(oldUserState), is(false));
|
||||
|
||||
oldUserState = new PackageUserStateImpl();
|
||||
oldUserState.setSuspended(true);
|
||||
oldUserState.putSuspendParams("suspendingPackage",
|
||||
SuspendParams.getInstanceOrNull(null, new PersistableBundle(), null));
|
||||
assertThat(testUserState.equals(oldUserState), is(false));
|
||||
|
||||
oldUserState = new PackageUserStateImpl();
|
||||
@@ -231,7 +232,6 @@ public class PackageUserStateTest {
|
||||
|
||||
|
||||
final PackageUserStateImpl testUserState1 = new PackageUserStateImpl();
|
||||
testUserState1.setSuspended(true);
|
||||
testUserState1.setSuspendParams(paramsMap1);
|
||||
|
||||
PackageUserStateImpl testUserState2 =
|
||||
|
||||
Reference in New Issue
Block a user