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

Commit 7cf271ec authored by Alex Buynytskyy's avatar Alex Buynytskyy Committed by Automerger Merge Worker
Browse files

Merge "Reduce lock contention on setting write." into tm-dev am: e06f5b1a am: 0ca9d4e3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17838378



Change-Id: I11a18d8a16ac8095c17a7bcb8df68fc5b068b5df
Ignore-AOSP-First: this is an automerge
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents e022e3e2 0ca9d4e3
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1430,7 +1430,7 @@ public class PackageManagerService implements PackageSender, TestUtilityService
                (i, pm) -> new Settings(Environment.getDataDirectory(),
                        RuntimePermissionsPersistence.createInstance(),
                        i.getPermissionManagerServiceInternal(),
                        domainVerificationService, lock),
                        domainVerificationService, backgroundHandler, lock),
                (i, pm) -> AppsFilterImpl.create(i,
                        i.getLocalService(PackageManagerInternal.class)),
                (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
+120 −61
Original line number Diff line number Diff line
@@ -360,6 +360,8 @@ public final class Settings implements Watchable, Snappable {
    private static final String ATTR_VALUE = "value";
    private static final String ATTR_FIRST_INSTALL_TIME = "first-install-time";

    private final Handler mHandler;

    private final PackageManagerTracedLock mLock;

    @Watched(manual = true)
@@ -583,6 +585,8 @@ public final class Settings implements Watchable, Snappable {
                                         "Settings.mInstallerPackages");
        mKeySetManagerService = new KeySetManagerService(mPackages);

        // Test-only handler working on background thread.
        mHandler = new Handler(BackgroundThread.getHandler().getLooper());
        mLock = new PackageManagerTracedLock();
        mPackages.putAll(pkgSettings);
        mAppIds = new AppIdSettingMap();
@@ -607,6 +611,7 @@ public final class Settings implements Watchable, Snappable {
    Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
            LegacyPermissionDataProvider permissionDataProvider,
            @NonNull DomainVerificationManagerInternal domainVerificationManager,
            @NonNull Handler handler,
            @NonNull PackageManagerTracedLock lock)  {
        mPackages = new WatchedArrayMap<>();
        mPackagesSnapshot  =
@@ -620,6 +625,7 @@ public final class Settings implements Watchable, Snappable {
                                         "Settings.mInstallerPackages");
        mKeySetManagerService = new KeySetManagerService(mPackages);

        mHandler = handler;
        mLock = lock;
        mAppIds = new AppIdSettingMap();
        mPermissions = new LegacyPermissionSettings(lock);
@@ -627,10 +633,8 @@ public final class Settings implements Watchable, Snappable {
                runtimePermissionsPersistence, new Consumer<Integer>() {
            @Override
            public void accept(Integer userId) {
                synchronized (mLock) {
                    mRuntimePermissionsPersistence.writeStateForUserSync(userId,
                            mPermissionDataProvider, mPackages, mSharedUsers);
                }
                mRuntimePermissionsPersistence.writeStateForUser(userId,
                        mPermissionDataProvider, mPackages, mSharedUsers, mHandler, mLock);
            }
        });
        mPermissionDataProvider = permissionDataProvider;
@@ -679,6 +683,7 @@ public final class Settings implements Watchable, Snappable {
        // 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.
        mHandler = null;
        mLock = null;
        mRuntimePermissionsPersistence = r.mRuntimePermissionsPersistence;
        mSettingsFilename = null;
@@ -5286,8 +5291,8 @@ public final class Settings implements Watchable, Snappable {

    public void writePermissionStateForUserLPr(int userId, boolean sync) {
        if (sync) {
            mRuntimePermissionsPersistence.writeStateForUserSync(userId, mPermissionDataProvider,
                    mPackages, mSharedUsers);
            mRuntimePermissionsPersistence.writeStateForUser(userId, mPermissionDataProvider,
                    mPackages, mSharedUsers, /*handler=*/null, mLock);
        } else {
            mRuntimePermissionsPersistence.writeStateForUserAsync(userId);
        }
@@ -5373,9 +5378,14 @@ public final class Settings implements Watchable, Snappable {

        private String mExtendedFingerprint;

        @GuardedBy("mPersistenceLock")
        private final RuntimePermissionsPersistence mPersistence;
        private final Object mPersistenceLock = new Object();

        private final Handler mHandler = new MyHandler();
        // Low-priority handlers running on SystemBg thread.
        private final Handler mAsyncHandler = new MyHandler();
        private final Handler mPersistenceHandler = new Handler(
                BackgroundThread.getHandler().getLooper());

        private final Object mLock = new Object();

@@ -5398,6 +5408,11 @@ public final class Settings implements Watchable, Snappable {
        // The mapping keys are user ids.
        private final SparseBooleanArray mPermissionUpgradeNeeded = new SparseBooleanArray();

        @GuardedBy("mLock")
        // Staging area for states prepared to be written.
        private final SparseArray<RuntimePermissionsState> mPendingStatesToWrite =
                new SparseArray<>();

        // 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;
@@ -5462,7 +5477,7 @@ public final class Settings implements Watchable, Snappable {
                final long currentTimeMillis = SystemClock.uptimeMillis();

                if (mWriteScheduled.get(userId)) {
                    mHandler.removeMessages(userId);
                    mAsyncHandler.removeMessages(userId);

                    // If enough time passed, write without holding off anymore.
                    final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis
@@ -5471,7 +5486,7 @@ public final class Settings implements Watchable, Snappable {
                            - lastNotWrittenMutationTimeMillis;
                    if (timeSinceLastNotWrittenMutationMillis
                            >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) {
                        mHandler.obtainMessage(userId).sendToTarget();
                        mAsyncHandler.obtainMessage(userId).sendToTarget();
                        return;
                    }

@@ -5481,30 +5496,36 @@ public final class Settings implements Watchable, Snappable {
                    final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS,
                            maxDelayMillis);

                    Message message = mHandler.obtainMessage(userId);
                    mHandler.sendMessageDelayed(message, writeDelayMillis);
                    Message message = mAsyncHandler.obtainMessage(userId);
                    mAsyncHandler.sendMessageDelayed(message, writeDelayMillis);
                } else {
                    mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis);
                    Message message = mHandler.obtainMessage(userId);
                    mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
                    Message message = mAsyncHandler.obtainMessage(userId);
                    mAsyncHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS);
                    mWriteScheduled.put(userId, true);
                }
            }
        }

        public void writeStateForUserSync(int userId, @NonNull LegacyPermissionDataProvider
        public void writeStateForUser(int userId, @NonNull LegacyPermissionDataProvider
                legacyPermissionDataProvider,
                @NonNull WatchedArrayMap<String, ? extends PackageStateInternal> packageStates,
                @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers) {
                @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers,
                @Nullable Handler pmHandler, @NonNull Object pmLock) {
            final int version;
            final String fingerprint;
            synchronized (mLock) {
                mHandler.removeMessages(userId);
                mAsyncHandler.removeMessages(userId);
                mWriteScheduled.delete(userId);

                legacyPermissionDataProvider.writeLegacyPermissionStateTEMP();

                int version = mVersions.get(userId, INITIAL_VERSION);
                version = mVersions.get(userId, INITIAL_VERSION);
                fingerprint = mFingerprints.get(userId);
            }

                String fingerprint = mFingerprints.get(userId);
            Runnable writer = () -> {
                final RuntimePermissionsState runtimePermissions;
                synchronized (pmLock) {
                    legacyPermissionDataProvider.writeLegacyPermissionStateTEMP();

                    Map<String, List<RuntimePermissionsState.PermissionState>> packagePermissions =
                            new ArrayMap<>();
@@ -5516,17 +5537,19 @@ public final class Settings implements Watchable, Snappable {
                            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.
                            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 =
                    Map<String, List<RuntimePermissionsState.PermissionState>>
                            sharedUserPermissions =
                            new ArrayMap<>();
                    final int sharedUsersSize = sharedUsers.size();
                    for (int i = 0; i < sharedUsersSize; i++) {
@@ -5538,12 +5561,47 @@ public final class Settings implements Watchable, Snappable {
                        sharedUserPermissions.put(sharedUserName, permissions);
                    }

                RuntimePermissionsState runtimePermissions = new RuntimePermissionsState(version,
                    runtimePermissions = new RuntimePermissionsState(version,
                            fingerprint, packagePermissions, sharedUserPermissions);
                }
                synchronized (mLock) {
                    mPendingStatesToWrite.put(userId, runtimePermissions);
                }
                if (pmHandler != null) {
                    // Async version.
                    mPersistenceHandler.post(() -> writePendingStates());
                } else {
                    // Sync version.
                    writePendingStates();
                }
            };

            if (pmHandler != null) {
                // Async version, use pmHandler.
                pmHandler.post(writer);
            } else {
                // Sync version, use caller's thread.
                writer.run();
            }
        }

        private void writePendingStates() {
            while (true) {
                final RuntimePermissionsState runtimePermissions;
                final int userId;
                synchronized (mLock) {
                    if (mPendingStatesToWrite.size() == 0) {
                        break;
                    }
                    userId = mPendingStatesToWrite.keyAt(0);
                    runtimePermissions = mPendingStatesToWrite.valueAt(0);
                    mPendingStatesToWrite.removeAt(0);
                }
                synchronized (mPersistenceLock) {
                    mPersistence.writeForUser(runtimePermissions, UserHandle.of(userId));
                }
            }
        }

        @NonNull
        private List<RuntimePermissionsState.PermissionState> getPermissionsFromPermissionsState(
@@ -5563,7 +5621,7 @@ public final class Settings implements Watchable, Snappable {
        private void onUserRemoved(int userId) {
            synchronized (mLock) {
                // Make sure we do not
                mHandler.removeMessages(userId);
                mAsyncHandler.removeMessages(userId);

                mPermissionUpgradeNeeded.delete(userId);
                mVersions.delete(userId);
@@ -5572,7 +5630,7 @@ public final class Settings implements Watchable, Snappable {
        }

        public void deleteUserRuntimePermissionsFile(int userId) {
            synchronized (mLock) {
            synchronized (mPersistenceLock) {
                mPersistence.deleteForUser(UserHandle.of(userId));
            }
        }
@@ -5581,16 +5639,17 @@ public final class Settings implements Watchable, Snappable {
                @NonNull WatchedArrayMap<String, PackageSetting> packageSettings,
                @NonNull WatchedArrayMap<String, SharedUserSetting> sharedUsers,
                @NonNull File userRuntimePermissionsFile) {
            synchronized (mLock) {
                RuntimePermissionsState runtimePermissions = mPersistence.readForUser(UserHandle.of(
                        userId));
            final RuntimePermissionsState runtimePermissions;
            synchronized (mPersistenceLock) {
                runtimePermissions = mPersistence.readForUser(UserHandle.of(userId));
            }
            if (runtimePermissions == null) {
                readLegacyStateForUserSync(userId, userRuntimePermissionsFile, packageSettings,
                        sharedUsers);
                writeStateForUserAsync(userId);
                return;
            }

            synchronized (mLock) {
                // 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();
+1 −1
Original line number Diff line number Diff line
@@ -1516,7 +1516,7 @@ public class PackageManagerSettingsTests {
    private Settings makeSettings() {
        return new Settings(InstrumentationRegistry.getContext().getFilesDir(),
                mRuntimePermissionsPersistence, mPermissionDataProvider,
                mDomainVerificationManager, new PackageManagerTracedLock());
                mDomainVerificationManager, null, new PackageManagerTracedLock());
    }

    private void verifyKeySetMetaData(Settings settings)