Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab5aa140 authored by Lee Shombert's avatar Lee Shombert
Browse files

PackageManager lock reduction: Settings

Bug: 161323622

Modify the Settings class used by PackageManagerServer to be
Watchable and Snappable.

Test: atest
 * FrameworksServicesTests:UserSystemPackageInstallerTest
 * FrameworksServicesTests:PackageManagerSettingsTests
 * FrameworksServicesTests:PackageManagerServiceTest
 * FrameworksServicesTests:AppsFilterTest
 * FrameworksServicesTests:PackageInstallerSessionTest
 * FrameworksServicesTests:ScanTests
 * FrameworksServicesTests:WatcherTest

Change-Id: Ibf6b2b5fb04b39489e52d6a25cce622bce32a805
parent f9270393
Loading
Loading
Loading
Loading
+12 −1
Original line number Diff line number Diff line
@@ -55,6 +55,7 @@ import com.android.server.FgThread;
import com.android.server.compat.CompatChange;
import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.utils.WatchedArrayMap;

import java.io.PrintWriter;
import java.util.Arrays;
@@ -347,7 +348,7 @@ public class AppsFilter {
        }
        final StateProvider stateProvider = command -> {
            synchronized (injector.getLock()) {
                command.currentState(injector.getSettings().getPackagesLocked(),
                command.currentState(injector.getSettings().getPackagesLocked().untrackedMap(),
                        injector.getUserManagerInternal().getUserInfos());
            }
        };
@@ -869,6 +870,16 @@ public class AppsFilter {
        return result;
    }

    /**
     * This api does type conversion on the <existingSettings> parameter.
     */
    @VisibleForTesting(visibility = PRIVATE)
    @Nullable
    SparseArray<int[]> getVisibilityAllowList(PackageSetting setting, int[] users,
            WatchedArrayMap<String, PackageSetting> existingSettings) {
        return getVisibilityAllowList(setting, users, existingSettings.untrackedMap());
    }

    /**
     * Equivalent to calling {@link #addPackage(PackageSetting, boolean)} with {@code isReplace}
     * equal to {@code false}.
+4 −2
Original line number Diff line number Diff line
@@ -394,6 +394,7 @@ import com.android.server.security.VerityUtils;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.wm.ActivityTaskManagerInternal;
import dalvik.system.CloseGuard;
@@ -3302,7 +3303,8 @@ public class PackageManagerService extends IPackageManager.Stub
            // Clean up orphaned packages for which the code path doesn't exist
            // and they are an update to a system app - caused by bug/32321269
            final ArrayMap<String, PackageSetting> packageSettings = mSettings.getPackagesLocked();
            final WatchedArrayMap<String, PackageSetting> packageSettings =
                mSettings.getPackagesLocked();
            final int packageSettingCount = packageSettings.size();
            for (int i = packageSettingCount - 1; i >= 0; i--) {
                PackageSetting ps = packageSettings.valueAt(i);
+24 −0
Original line number Diff line number Diff line
@@ -175,22 +175,27 @@ public abstract class PackageSettingBase extends SettingBase {

    public void setInstallerPackageName(String packageName) {
        installSource = installSource.setInstallerPackage(packageName);
        onChanged();
    }

    public void setInstallSource(InstallSource installSource) {
        this.installSource = Objects.requireNonNull(installSource);
        onChanged();
    }

    void removeInstallerPackage(String packageName) {
        installSource = installSource.removeInstallerPackage(packageName);
        onChanged();
    }

    public void setIsOrphaned(boolean isOrphaned) {
        installSource = installSource.setIsOrphaned(isOrphaned);
        onChanged();
    }

    public void setVolumeUuid(String volumeUuid) {
        this.volumeUuid = volumeUuid;
        onChanged();
    }

    public String getVolumeUuid() {
@@ -199,10 +204,12 @@ public abstract class PackageSettingBase extends SettingBase {

    public void setTimeStamp(long newStamp) {
        timeStamp = newStamp;
        onChanged();
    }

    public void setUpdateAvailable(boolean updateAvailable) {
        this.updateAvailable = updateAvailable;
        onChanged();
    }

    public boolean isUpdateAvailable() {
@@ -272,6 +279,7 @@ public abstract class PackageSettingBase extends SettingBase {
        if (state == null) {
            state = new PackageUserState();
            mUserState.put(userId, state);
            onChanged();
        }
        return state;
    }
@@ -289,6 +297,7 @@ public abstract class PackageSettingBase extends SettingBase {
        PackageUserState st = modifyUserState(userId);
        st.enabled = state;
        st.lastDisableAppCaller = callingPackage;
        onChanged();
    }

    int getEnabled(int userId) {
@@ -434,6 +443,7 @@ public abstract class PackageSettingBase extends SettingBase {
        }
        existingUserState.suspendParams.put(suspendingPackage, newSuspendParams);
        existingUserState.suspended = true;
        onChanged();
    }

    void removeSuspension(String suspendingPackage, int userId) {
@@ -445,6 +455,7 @@ public abstract class PackageSettingBase extends SettingBase {
            }
        }
        existingUserState.suspended = (existingUserState.suspendParams != null);
        onChanged();
    }

    void removeSuspension(Predicate<String> suspendingPackagePredicate, int userId) {
@@ -461,6 +472,7 @@ public abstract class PackageSettingBase extends SettingBase {
            }
        }
        existingUserState.suspended = (existingUserState.suspendParams != null);
        onChanged();
    }

    public boolean getInstantApp(int userId) {
@@ -506,6 +518,7 @@ public abstract class PackageSettingBase extends SettingBase {
        state.instantApp = instantApp;
        state.virtualPreload = virtualPreload;
        state.harmfulAppWarning = harmfulAppWarning;
        onChanged();
    }

    void setUserState(int userId, PackageUserState otherState) {
@@ -547,11 +560,17 @@ public abstract class PackageSettingBase extends SettingBase {

    PackageUserState modifyUserStateComponents(int userId, boolean disabled, boolean enabled) {
        PackageUserState state = modifyUserState(userId);
        boolean changed = false;
        if (disabled && state.disabledComponents == null) {
            state.disabledComponents = new ArraySet<String>(1);
            changed = true;
        }
        if (enabled && state.enabledComponents == null) {
            state.enabledComponents = new ArraySet<String>(1);
            changed = true;
        }
        if (changed) {
            onChanged();
        }
        return state;
    }
@@ -603,6 +622,7 @@ public abstract class PackageSettingBase extends SettingBase {

    void removeUser(int userId) {
        mUserState.delete(userId);
        onChanged();
    }

    public int[] getNotInstalledUserIds() {
@@ -630,6 +650,7 @@ public abstract class PackageSettingBase extends SettingBase {

    void setIntentFilterVerificationInfo(IntentFilterVerificationInfo info) {
        verificationInfo = info;
        onChanged();
    }

    // Returns a packed value as a long:
@@ -648,6 +669,7 @@ public abstract class PackageSettingBase extends SettingBase {
        state.domainVerificationStatus = status;
        if (status == PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS) {
            state.appLinkGeneration = generation;
            onChanged();
        }
    }

@@ -694,6 +716,7 @@ public abstract class PackageSettingBase extends SettingBase {
    void setHarmfulAppWarning(int userId, String harmfulAppWarning) {
        PackageUserState userState = modifyUserState(userId);
        userState.harmfulAppWarning = harmfulAppWarning;
        onChanged();
    }

    String getHarmfulAppWarning(int userId) {
@@ -838,6 +861,7 @@ public abstract class PackageSettingBase extends SettingBase {
        for (int i = 0; i < other.mUserState.size(); i++) {
            mUserState.put(other.mUserState.keyAt(i), other.mUserState.valueAt(i));
        }
        onChanged();
        return this;
    }
}
+58 −1
Original line number Diff line number Diff line
@@ -16,16 +16,70 @@

package com.android.server.pm;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.ApplicationInfo;

import com.android.internal.annotations.VisibleForTesting;
import com.android.server.pm.permission.LegacyPermissionState;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
import com.android.server.utils.Watcher;

@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public abstract class SettingBase {
public abstract class SettingBase implements Watchable {
    // TODO: make this variable protected, or even private with a getter and setter.
    // Simply making it protected or private requires that the name be changed to conformm
    // to the Android naming convention, and that touches quite a few files.
    int pkgFlags;

    // TODO: make this variable protected, or even private with a getter and setter.
    // Simply making it protected or private requires that the name be changed to conformm
    // to the Android naming convention, and that touches quite a few files.
    int pkgPrivateFlags;

    /**
     * Watchable machinery
     */
    private final Watchable mWatchable = new WatchableImpl();

    /**
     * Ensures an observer is in the list, exactly once. The observer cannot be null.  The
     * function quietly returns if the observer is already in the list.
     *
     * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes.
     */
    public void registerObserver(@NonNull Watcher observer) {
        mWatchable.registerObserver(observer);
    }

    /**
     * Ensures an observer is not in the list. The observer must not be null.  The function
     * quietly returns if the objserver is not in the list.
     *
     * @param observer The {@link Watcher} that should not be in the notification list.
     */
    public void unregisterObserver(@NonNull Watcher observer) {
        mWatchable.unregisterObserver(observer);
    }

    /**
     * Invokes {@link Watcher#onChange} on each registered observer.  The method can be called
     * with the {@link Watchable} that generated the event.  In a tree of {@link Watchable}s, this
     * is generally the first (deepest) {@link Watchable} to detect a change.
     *
     * @param what The {@link Watchable} that generated the event.
     */
    public void dispatchChange(@Nullable Watchable what) {
        mWatchable.dispatchChange(what);
    }
    /**
     * Notify listeners that this object has changed.
     */
    protected void onChanged() {
        dispatchChange(this);
    }

    /**
     * The legacy permission state that is read from package settings persistence for migration.
     * This state here can not reflect the current permission state and should not be used for
@@ -53,6 +107,7 @@ public abstract class SettingBase {
        pkgFlags = orig.pkgFlags;
        pkgPrivateFlags = orig.pkgPrivateFlags;
        mLegacyPermissionsState.copyFrom(orig.mLegacyPermissionsState);
        onChanged();
    }

    @Deprecated
@@ -64,6 +119,7 @@ public abstract class SettingBase {
        this.pkgFlags = pkgFlags
                & (ApplicationInfo.FLAG_SYSTEM
                        | ApplicationInfo.FLAG_EXTERNAL_STORAGE);
        onChanged();
    }

    void setPrivateFlags(int pkgPrivateFlags) {
@@ -75,5 +131,6 @@ public abstract class SettingBase {
                | ApplicationInfo.PRIVATE_FLAG_SYSTEM_EXT
                | ApplicationInfo.PRIVATE_FLAG_REQUIRED_FOR_SYSTEM_USER
                | ApplicationInfo.PRIVATE_FLAG_ODM);
        onChanged();
    }
}
+169 −13
Original line number Diff line number Diff line
@@ -114,7 +114,14 @@ 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.utils.Snappable;
import com.android.server.utils.Snapshots;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedSparseArray;
import com.android.server.utils.Watcher;

import libcore.io.IoUtils;

@@ -148,9 +155,57 @@ import java.util.Set;
/**
 * Holds information about dynamic settings.
 */
public final class Settings {
public final class Settings implements Watchable, Snappable {
    private static final String TAG = "PackageSettings";

    /**
     * Cached snapshot
     */
    private volatile Settings mSnapshot = null;

    /**
     * Watchable machinery
     */
    private final WatchableImpl mWatchable = new WatchableImpl();

    /**
     * Ensures an observer is in the list, exactly once. The observer cannot be null.  The
     * function quietly returns if the observer is already in the list.
     *
     * @param observer The {@link Watcher} to be notified when the {@link Watchable} changes.
     */
    public void registerObserver(@NonNull Watcher observer) {
        mWatchable.registerObserver(observer);
    }

    /**
     * Ensures an observer is not in the list. The observer must not be null.  The function
     * quietly returns if the objserver is not in the list.
     *
     * @param observer The {@link Watcher} that should not be in the notification list.
     */
    public void unregisterObserver(@NonNull Watcher observer) {
        mWatchable.unregisterObserver(observer);
    }

    /**
     * Invokes {@link Watcher#onChange} on each registered observer.  The method can be called
     * with the {@link Watchable} that generated the event.  In a tree of {@link Watchable}s, this
     * is generally the first (deepest) {@link Watchable} to detect a change.
     *
     * @param what The {@link Watchable} that generated the event.
     */
    public void dispatchChange(@Nullable Watchable what) {
        mSnapshot = null;
        mWatchable.dispatchChange(what);
    }
    /**
     * Notify listeners that this object has changed.
     */
    protected void onChanged() {
        dispatchChange(this);
    }

    /**
     * Current version of the package database. Set it to the latest version in
     * the {@link DatabaseVersion} class below to ensure the database upgrade
@@ -296,7 +351,7 @@ public final class Settings {

    /** Map from package name to settings */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<>();
    final WatchedArrayMap<String, PackageSetting> mPackages = new WatchedArrayMap<>();

    /**
     * List of packages that were involved in installing other packages, i.e. are listed
@@ -368,21 +423,21 @@ public final class Settings {

    // The user's preferred activities associated with particular intent
    // filters.
    private final SparseArray<PreferredIntentResolver> mPreferredActivities =
            new SparseArray<PreferredIntentResolver>();
    private final WatchedSparseArray<PreferredIntentResolver>
            mPreferredActivities = new WatchedSparseArray<>();

    // The persistent preferred activities of the user's profile/device owner
    // associated with particular intent filters.
    private final SparseArray<PersistentPreferredIntentResolver> mPersistentPreferredActivities =
            new SparseArray<PersistentPreferredIntentResolver>();
    private final WatchedSparseArray<PersistentPreferredIntentResolver>
            mPersistentPreferredActivities = new WatchedSparseArray<>();

    // For every user, it is used to find to which other users the intent can be forwarded.
    private final SparseArray<CrossProfileIntentResolver> mCrossProfileIntentResolvers =
            new SparseArray<CrossProfileIntentResolver>();
    private final WatchedSparseArray<CrossProfileIntentResolver>
            mCrossProfileIntentResolvers = new WatchedSparseArray<>();

    final ArrayMap<String, SharedUserSetting> mSharedUsers = new ArrayMap<>();
    private final ArrayList<SettingBase> mAppIds = new ArrayList<>();
    private final SparseArray<SettingBase> mOtherAppIds = new SparseArray<>();
    private final ArrayList<SettingBase> mAppIds;
    private final SparseArray<SettingBase> mOtherAppIds;

    // For reading/writing settings file.
    private final ArrayList<Signature> mPastSignatures =
@@ -415,17 +470,30 @@ public final class Settings {

    private final File mSystemDir;

    public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages);
    public final KeySetManagerService mKeySetManagerService =
            new KeySetManagerService(mPackages.untrackedMap());

    /** Settings and other information about permissions */
    final LegacyPermissionSettings mPermissions;

    private final LegacyPermissionDataProvider mPermissionDataProvider;

    /**
     * The observer that watches for changes from array members
     */
    private final Watcher mObserver = new Watcher() {
            @Override
            public void onChange(@Nullable Watchable what) {
                Settings.this.dispatchChange(what);
            }
        };

    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    public Settings(Map<String, PackageSetting> pkgSettings) {
        mLock = new Object();
        mPackages.putAll(pkgSettings);
        mAppIds = new ArrayList<>();
        mOtherAppIds = new SparseArray<>();
        mSystemDir = null;
        mPermissions = null;
        mRuntimePermissionsPersistence = null;
@@ -436,11 +504,17 @@ public final class Settings {
        mStoppedPackagesFilename = null;
        mBackupStoppedPackagesFilename = null;
        mKernelMappingFilename = null;
        mPackages.registerObserver(mObserver);
        mPreferredActivities.registerObserver(mObserver);
        mPersistentPreferredActivities.registerObserver(mObserver);
        mCrossProfileIntentResolvers.registerObserver(mObserver);
    }

    Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
            LegacyPermissionDataProvider permissionDataProvider, Object lock) {
        mLock = lock;
        mAppIds = new ArrayList<>();
        mOtherAppIds = new SparseArray<>();
        mPermissions = new LegacyPermissionSettings(lock);
        mRuntimePermissionsPersistence = new RuntimePermissionPersistence(
                runtimePermissionsPersistence);
@@ -463,18 +537,91 @@ public final class Settings {
        // Deprecated: Needed for migration
        mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
        mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
        mPackages.registerObserver(mObserver);
        mPreferredActivities.registerObserver(mObserver);
        mPersistentPreferredActivities.registerObserver(mObserver);
        mCrossProfileIntentResolvers.registerObserver(mObserver);
    }

    /**
     * A copy constructor used in snapshot().  Attributes that are supposed to be
     * immutable in the PackageManagerService application are referenced.  Attributes that
     * are changed by PackageManagerService APIs are deep-copied
     */
    private Settings(Settings r) {
        final int mPackagesSize = r.mPackages.size();
        mPackages.putAll(r.mPackages);

        // The following assignments satisfy Java requirements but are not
        // needed by the read-only methods.  Note especially that the lock
        // is not required because this clone is meant to support lock-free
        // read-only methods.
        mLock = null;
        mRuntimePermissionsPersistence = r.mRuntimePermissionsPersistence;
        mSettingsFilename = null;
        mBackupSettingsFilename = null;
        mPackageListFilename = null;
        mStoppedPackagesFilename = null;
        mBackupStoppedPackagesFilename = null;
        mKernelMappingFilename = null;

        mInstallerPackages.addAll(r.mInstallerPackages);
        mKernelMapping.putAll(r.mKernelMapping);
        mDisabledSysPackages.putAll(r.mDisabledSysPackages);
        Snapshots.copy(mBlockUninstallPackages, r.mBlockUninstallPackages);
        mRestoredIntentFilterVerifications.putAll(r.mRestoredIntentFilterVerifications);
        mVersion.putAll(r.mVersion);
        mVerifierDeviceIdentity = r.mVerifierDeviceIdentity;
        WatchedSparseArray.snapshot(
                mPreferredActivities, r.mPreferredActivities);
        WatchedSparseArray.snapshot(
                mPersistentPreferredActivities, r.mPersistentPreferredActivities);
        WatchedSparseArray.snapshot(
                mCrossProfileIntentResolvers, r.mCrossProfileIntentResolvers);
        mSharedUsers.putAll(r.mSharedUsers);
        mAppIds = new ArrayList<>(r.mAppIds);
        mOtherAppIds = r.mOtherAppIds.clone();
        mPastSignatures.addAll(r.mPastSignatures);
        mKeySetRefs.putAll(r.mKeySetRefs);
        mRenamedPackages.putAll(r.mRenamedPackages);
        Snapshots.copy(mDefaultBrowserApp, r.mDefaultBrowserApp);
        Snapshots.snapshot(mNextAppLinkGeneration, r.mNextAppLinkGeneration);
        // mReadMessages
        mPendingPackages.addAll(r.mPendingPackages);
        mSystemDir = null;
        // mKeySetManagerService;
        mPermissions = r.mPermissions;
        mPermissionDataProvider = r.mPermissionDataProvider;

        // Do not register any Watchables
    }

    private static void invalidatePackageCache() {
    /**
     * Return a snapshot.  If the cached snapshot is null, build a new one.  The logic in
     * the function ensures that this function returns a valid snapshot even if a race
     * condition causes the cached snapshot to be cleared asynchronously to this method.
     */
    public Settings snapshot() {
        Settings s = mSnapshot;
        if (s == null) {
            s = new Settings(this);
            s.mWatchable.seal();
            mSnapshot = s;
        }
        return s;
    }

    private void invalidatePackageCache() {
        PackageManagerService.invalidatePackageInfoCache();
        ChangeIdStateCache.invalidate();
        onChanged();
    }

    PackageSetting getPackageLPr(String pkgName) {
        return mPackages.get(pkgName);
    }

    ArrayMap<String, PackageSetting> getPackagesLocked() {
    WatchedArrayMap<String, PackageSetting> getPackagesLocked() {
        return mPackages;
    }

@@ -5558,6 +5705,7 @@ public final class Settings {
    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
    void clearPackagePreferredActivities(String packageName,
            @NonNull SparseBooleanArray outUserChanged, int userId) {
        boolean changed = false;
        ArrayList<PreferredActivity> removed = null;
        for (int i = 0; i < mPreferredActivities.size(); i++) {
            final int thisUserId = mPreferredActivities.keyAt(i);
@@ -5585,7 +5733,11 @@ public final class Settings {
                    pir.removeFilter(pa);
                }
                outUserChanged.put(thisUserId, true);
                changed = true;
            }
        }
        if (changed) {
            onChanged();
        }
    }

@@ -5617,6 +5769,9 @@ public final class Settings {
                changed = true;
            }
        }
        if (changed) {
            onChanged();
        }
        return changed;
    }

@@ -5649,6 +5804,7 @@ public final class Settings {
                changed.add(mPreferredActivities.keyAt(i));
            }
        }
        onChanged();
        return changed;
    }

Loading