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

Commit 6330ab89 authored by Lee Shombert's avatar Lee Shombert
Browse files

PackageManager lock reduction: AppsFilter

Bug: 161323622

Modify the AppsFilter 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: Ifa4cd7569fe4ab042de3db1e0e341030d0a7fd22
parent ab5aa140
Loading
Loading
Loading
Loading
+105 −2
Original line number Diff line number Diff line
@@ -55,7 +55,12 @@ 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.Snappable;
import com.android.server.utils.Snapshots;
import com.android.server.utils.Watchable;
import com.android.server.utils.WatchableImpl;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.Watcher;

import java.io.PrintWriter;
import java.util.Arrays;
@@ -70,7 +75,7 @@ import java.util.concurrent.Executor;
 * manifests.
 */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public class AppsFilter {
public class AppsFilter implements Watchable, Snappable {

    private static final String TAG = "AppsFilter";

@@ -146,6 +151,55 @@ public class AppsFilter {
    @GuardedBy("mCacheLock")
    private volatile SparseArray<SparseBooleanArray> mShouldFilterCache;

    /**
     * A cached snapshot.
     */
    private volatile AppsFilter 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);
    }

    /**
     * Report a change to observers.
     */
    private void onChanged() {
        dispatchChange(this);
    }

    @VisibleForTesting(visibility = PRIVATE)
    AppsFilter(StateProvider stateProvider,
            FeatureConfig featureConfig,
@@ -162,6 +216,44 @@ public class AppsFilter {
        mBackgroundExecutor = backgroundExecutor;
    }

    /**
     * The copy constructor is used by PackageManagerService to construct a snapshot.
     * Attributes are not deep-copied since these are supposed to be immutable.
     * TODO: deep-copy the attributes, if necessary.
     */
    private AppsFilter(AppsFilter orig) {
        Snapshots.copy(mImplicitlyQueryable, orig.mImplicitlyQueryable);
        Snapshots.copy(mQueriesViaPackage, orig.mQueriesViaPackage);
        Snapshots.copy(mQueriesViaComponent, orig.mQueriesViaComponent);
        mQueriesViaComponentRequireRecompute = orig.mQueriesViaComponentRequireRecompute;
        mForceQueryable.addAll(orig.mForceQueryable);
        mForceQueryableByDevicePackageNames = orig.mForceQueryableByDevicePackageNames;
        mSystemAppsQueryable = orig.mSystemAppsQueryable;
        mFeatureConfig = orig.mFeatureConfig;
        mOverlayReferenceMapper = orig.mOverlayReferenceMapper;
        mStateProvider = orig.mStateProvider;
        mSystemSigningDetails = orig.mSystemSigningDetails;
        mProtectedBroadcasts = orig.mProtectedBroadcasts;
        mShouldFilterCache = orig.mShouldFilterCache;

        mBackgroundExecutor = null;
    }

    /**
     * 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 AppsFilter snapshot() {
        AppsFilter s = mSnapshot;
        if (s == null) {
            s = new AppsFilter(this);
            s.mWatchable.seal();
            mSnapshot = s;
        }
        return s;
    }

    /**
     * Provides system state to AppsFilter via {@link CurrentStateCallback} after properly guarding
     * the data with the package lock.
@@ -312,6 +404,9 @@ public class AppsFilter {
            } else {
                mDisabledPackages.add(pkg.getPackageName());
            }
            if (mAppsFilter != null) {
                mAppsFilter.onChanged();
            }
        }

        @Override
@@ -483,7 +578,8 @@ public class AppsFilter {
     */
    public void grantImplicitAccess(int recipientUid, int visibleUid) {
        if (recipientUid != visibleUid) {
            if (mImplicitlyQueryable.add(recipientUid, visibleUid) && DEBUG_LOGGING) {
            final boolean changed = mImplicitlyQueryable.add(recipientUid, visibleUid);
            if (changed && DEBUG_LOGGING) {
                Slog.i(TAG, "implicit access granted: " + recipientUid + " -> " + visibleUid);
            }
            synchronized (mCacheLock) {
@@ -498,6 +594,9 @@ public class AppsFilter {
                    visibleUids.put(visibleUid, false);
                }
            }
            if (changed) {
                onChanged();
            }
        }
    }

@@ -506,6 +605,7 @@ public class AppsFilter {
        mFeatureConfig.onSystemReady();

        updateEntireShouldFilterCacheAsync();
        onChanged();
    }

    /**
@@ -531,6 +631,7 @@ public class AppsFilter {
                }
            });
        } finally {
            onChanged();
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
@@ -713,6 +814,7 @@ public class AppsFilter {
        synchronized (mCacheLock) {
            if (mShouldFilterCache != null) {
                updateEntireShouldFilterCache();
                onChanged();
            }
        }
    }
@@ -957,6 +1059,7 @@ public class AppsFilter {
                                siblingSetting, settings, users, settings.size());
                    }
                }
                onChanged();
            }
        });
    }
+99 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.PackageImpl;
import com.android.server.pm.parsing.pkg.ParsedPackage;
import com.android.server.utils.WatchableTester;

import org.junit.Before;
import org.junit.Test;
@@ -884,6 +885,104 @@ public class AppsFilterTest {
                contains(hasProviderAppId, queriesProviderAppId));
    }

    @Test
    public void testOnChangeReport() throws Exception {
        final AppsFilter appsFilter =
                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                        mMockExecutor);
        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange");
        watcher.register();
        simulateAddBasicAndroid(appsFilter);
        watcher.verifyChangeReported("addBasic");
        appsFilter.onSystemReady();
        watcher.verifyChangeReported("systemReady");

        final int systemAppId = Process.FIRST_APPLICATION_UID - 1;
        final int seesNothingAppId = Process.FIRST_APPLICATION_UID;
        final int hasProviderAppId = Process.FIRST_APPLICATION_UID + 1;
        final int queriesProviderAppId = Process.FIRST_APPLICATION_UID + 2;

        PackageSetting system = simulateAddPackage(appsFilter, pkg("some.system.pkg"), systemAppId);
        watcher.verifyChangeReported("addPackage");
        PackageSetting seesNothing = simulateAddPackage(appsFilter, pkg("com.some.package"),
                seesNothingAppId);
        watcher.verifyChangeReported("addPackage");
        PackageSetting hasProvider = simulateAddPackage(appsFilter,
                pkgWithProvider("com.some.other.package", "com.some.authority"), hasProviderAppId);
        watcher.verifyChangeReported("addPackage");
        PackageSetting queriesProvider = simulateAddPackage(appsFilter,
                pkgQueriesProvider("com.yet.some.other.package", "com.some.authority"),
                queriesProviderAppId);
        watcher.verifyChangeReported("addPackage");

        final SparseArray<int[]> systemFilter =
                appsFilter.getVisibilityAllowList(system, USER_ARRAY, mExisting);
        assertThat(toList(systemFilter.get(SYSTEM_USER)),
                contains(seesNothingAppId, hasProviderAppId, queriesProviderAppId));
        watcher.verifyNoChangeReported("get");

        final SparseArray<int[]> seesNothingFilter =
                appsFilter.getVisibilityAllowList(seesNothing, USER_ARRAY, mExisting);
        assertThat(toList(seesNothingFilter.get(SYSTEM_USER)),
                contains(seesNothingAppId));
        assertThat(toList(seesNothingFilter.get(SECONDARY_USER)),
                contains(seesNothingAppId));
        watcher.verifyNoChangeReported("get");

        final SparseArray<int[]> hasProviderFilter =
                appsFilter.getVisibilityAllowList(hasProvider, USER_ARRAY, mExisting);
        assertThat(toList(hasProviderFilter.get(SYSTEM_USER)),
                contains(hasProviderAppId, queriesProviderAppId));
        watcher.verifyNoChangeReported("get");

        SparseArray<int[]> queriesProviderFilter =
                appsFilter.getVisibilityAllowList(queriesProvider, USER_ARRAY, mExisting);
        assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)),
                contains(queriesProviderAppId));
        watcher.verifyNoChangeReported("get");

        // provider read
        appsFilter.grantImplicitAccess(hasProviderAppId, queriesProviderAppId);
        watcher.verifyChangeReported("grantImplicitAccess");

        // ensure implicit access is included in the filter
        queriesProviderFilter =
                appsFilter.getVisibilityAllowList(queriesProvider, USER_ARRAY, mExisting);
        assertThat(toList(queriesProviderFilter.get(SYSTEM_USER)),
                contains(hasProviderAppId, queriesProviderAppId));
        watcher.verifyNoChangeReported("get");

        // remove a package
        appsFilter.removePackage(seesNothing);
        watcher.verifyChangeReported("removePackage");
    }

    @Test
    public void testOnChangeReportedFilter() throws Exception {
        final AppsFilter appsFilter =
                new AppsFilter(mStateProvider, mFeatureConfigMock, new String[]{}, false, null,
                        mMockExecutor);
        simulateAddBasicAndroid(appsFilter);
        appsFilter.onSystemReady();
        final WatchableTester watcher = new WatchableTester(appsFilter, "onChange filter");
        watcher.register();

        PackageSetting target = simulateAddPackage(appsFilter, pkg("com.some.package"),
                DUMMY_TARGET_APPID);
        PackageSetting instrumentation = simulateAddPackage(appsFilter,
                pkgWithInstrumentation("com.some.other.package", "com.some.package"),
                DUMMY_CALLING_APPID);
        watcher.verifyChangeReported("addPackage");

        assertFalse(
                appsFilter.shouldFilterApplication(DUMMY_CALLING_APPID, instrumentation, target,
                        SYSTEM_USER));
        assertFalse(
                appsFilter.shouldFilterApplication(DUMMY_TARGET_APPID, target, instrumentation,
                        SYSTEM_USER));
        watcher.verifyNoChangeReported("shouldFilterApplication");
    }

    private List<Integer> toList(int[] array) {
        ArrayList<Integer> ret = new ArrayList<>(array.length);
        for (int i = 0; i < array.length; i++) {