Improve the cache rebuild latency of AppsFilter on boot

- Moving the rebuild task from background thread to the service
  thread in order to get more cpu resource for visibility
  computing.
- Using a thread pool to compute component visibility of all
  packages on the device in parallel. It can improve the latency
  if lots of packages on device.

Bug: 257339238
Bug: 262515424
Test: atest CtsAppEnumerationTestCases
Change-Id: Id9dbd4e55fa8fdd0a3f07e3ff7b953e1d9c5f1a9
This commit is contained in:
Rhed Jao
2022-12-21 15:06:27 +08:00
parent 746366d041
commit c285dbe125
5 changed files with 124 additions and 35 deletions

View File

@@ -137,10 +137,9 @@ public abstract class AppsFilterBase implements AppsFilterSnapshot {
protected SnapshotCache<WatchedSparseSetArray<Integer>> mQueryableViaUsesPermissionSnapshot;
/**
* Handler for running reasonably short background tasks such as building the initial
* visibility cache.
* Handler for running tasks such as building the initial visibility cache.
*/
protected Handler mBackgroundHandler;
protected Handler mHandler;
/**
* Pending full recompute of mQueriesViaComponent. Occurs when a package adds a new set of

View File

@@ -35,7 +35,6 @@ import static com.android.server.pm.AppsFilterUtils.canQueryAsInstaller;
import static com.android.server.pm.AppsFilterUtils.canQueryViaComponents;
import static com.android.server.pm.AppsFilterUtils.canQueryViaPackage;
import static com.android.server.pm.AppsFilterUtils.canQueryViaUsesLibrary;
import static com.android.server.pm.AppsFilterUtils.requestsQueryAllPackages;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -63,6 +62,7 @@ import com.android.internal.util.FrameworkStatsLog;
import com.android.server.FgThread;
import com.android.server.compat.CompatChange;
import com.android.server.om.OverlayReferenceMapper;
import com.android.server.pm.AppsFilterUtils.ParallelComputeComponentVisibility;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
@@ -185,13 +185,13 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
String[] forceQueryableList,
boolean systemAppsQueryable,
@Nullable OverlayReferenceMapper.Provider overlayProvider,
Handler backgroundHandler) {
Handler handler) {
mFeatureConfig = featureConfig;
mForceQueryableByDevicePackageNames = forceQueryableList;
mSystemAppsQueryable = systemAppsQueryable;
mOverlayReferenceMapper = new OverlayReferenceMapper(true /*deferRebuild*/,
overlayProvider);
mBackgroundHandler = backgroundHandler;
mHandler = handler;
mShouldFilterCache = new WatchedSparseBooleanMatrix();
mShouldFilterCacheSnapshot = new SnapshotCache.Auto<>(
mShouldFilterCache, mShouldFilterCache, "AppsFilter.mShouldFilterCache");
@@ -428,7 +428,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
}
AppsFilterImpl appsFilter = new AppsFilterImpl(featureConfig,
forcedQueryablePackageNames, forceSystemAppsQueryable, null,
injector.getBackgroundHandler());
injector.getHandler());
featureConfig.setAppsFilter(appsFilter);
return appsFilter;
}
@@ -797,7 +797,7 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
private void updateEntireShouldFilterCacheAsync(PackageManagerInternal pmInternal,
long delayMs, int reason) {
mBackgroundHandler.postDelayed(() -> {
mHandler.postDelayed(() -> {
if (!mCacheValid.compareAndSet(CACHE_INVALID, CACHE_VALID)) {
// Cache is already valid.
return;
@@ -990,34 +990,15 @@ public final class AppsFilterImpl extends AppsFilterLocked implements Watchable,
*/
private void recomputeComponentVisibility(
ArrayMap<String, ? extends PackageStateInternal> existingSettings) {
final WatchedArraySet<String> protectedBroadcasts;
synchronized (mProtectedBroadcastsLock) {
protectedBroadcasts = mProtectedBroadcasts.snapshot();
}
final ParallelComputeComponentVisibility computer = new ParallelComputeComponentVisibility(
existingSettings, mForceQueryable, protectedBroadcasts);
synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.clear();
}
for (int i = existingSettings.size() - 1; i >= 0; i--) {
PackageStateInternal setting = existingSettings.valueAt(i);
if (setting.getPkg() == null || requestsQueryAllPackages(setting.getPkg())) {
continue;
}
for (int j = existingSettings.size() - 1; j >= 0; j--) {
if (i == j) {
continue;
}
final PackageStateInternal otherSetting = existingSettings.valueAt(j);
if (otherSetting.getPkg() == null || mForceQueryable.contains(
otherSetting.getAppId())) {
continue;
}
final boolean canQueryViaComponents;
synchronized (mProtectedBroadcastsLock) {
canQueryViaComponents = canQueryViaComponents(setting.getPkg(),
otherSetting.getPkg(), mProtectedBroadcasts);
}
if (canQueryViaComponents) {
synchronized (mQueriesViaComponentLock) {
mQueriesViaComponent.add(setting.getAppId(), otherSetting.getAppId());
}
}
}
computer.execute(mQueriesViaComponent);
}
mQueriesViaComponentRequireRecompute.set(false);

View File

@@ -77,6 +77,6 @@ public final class AppsFilterSnapshotImpl extends AppsFilterBase {
mCacheEnabled = orig.mCacheEnabled;
mShouldFilterCacheSnapshot = new SnapshotCache.Sealed<>();
mBackgroundHandler = null;
mHandler = null;
}
}

View File

@@ -16,24 +16,36 @@
package com.android.server.pm;
import static android.os.Process.THREAD_PRIORITY_DEFAULT;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.component.ParsedComponent;
import com.android.server.pm.pkg.component.ParsedIntentInfo;
import com.android.server.pm.pkg.component.ParsedMainComponent;
import com.android.server.pm.pkg.component.ParsedProvider;
import com.android.server.utils.WatchedArraySet;
import com.android.server.utils.WatchedSparseSetArray;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
final class AppsFilterUtils {
public static boolean requestsQueryAllPackages(@NonNull AndroidPackage pkg) {
@@ -169,4 +181,93 @@ final class AppsFilterUtils {
intent.getData(), intent.getCategories(), "AppsFilter", true,
protectedBroadcasts != null ? protectedBroadcasts.untrackedStorage() : null) > 0;
}
/**
* A helper class for parallel computing of component visibility of all packages on the device.
*/
public static final class ParallelComputeComponentVisibility {
private static final int MAX_THREADS = 4;
private final ArrayMap<String, ? extends PackageStateInternal> mExistingSettings;
private final WatchedArraySet<Integer> mForceQueryable;
private final WatchedArraySet<String> mProtectedBroadcasts;
ParallelComputeComponentVisibility(
@NonNull ArrayMap<String, ? extends PackageStateInternal> existingSettings,
@NonNull WatchedArraySet<Integer> forceQueryable,
@NonNull WatchedArraySet<String> protectedBroadcasts) {
mExistingSettings = existingSettings;
mForceQueryable = forceQueryable;
mProtectedBroadcasts = protectedBroadcasts;
}
/**
* Computes component visibility of all packages in parallel from a thread pool.
*/
void execute(@NonNull WatchedSparseSetArray<Integer> outQueriesViaComponent) {
final ExecutorService pool = ConcurrentUtils.newFixedThreadPool(
MAX_THREADS, ParallelComputeComponentVisibility.class.getSimpleName(),
THREAD_PRIORITY_DEFAULT);
try {
final List<Pair<PackageState, Future<ArraySet<Integer>>>> futures =
new ArrayList<>();
for (int i = mExistingSettings.size() - 1; i >= 0; i--) {
final PackageStateInternal setting = mExistingSettings.valueAt(i);
final AndroidPackage pkg = setting.getPkg();
if (pkg == null || requestsQueryAllPackages(pkg)) {
continue;
}
if (pkg.getQueriesIntents().isEmpty()
&& pkg.getQueriesProviders().isEmpty()) {
continue;
}
futures.add(new Pair(setting,
pool.submit(() -> getVisibleListOfQueryViaComponents(setting))));
}
for (int i = 0; i < futures.size(); i++) {
final int appId = futures.get(i).first.getAppId();
final Future<ArraySet<Integer>> future = futures.get(i).second;
try {
final ArraySet<Integer> visibleList = future.get();
if (visibleList.size() != 0) {
outQueriesViaComponent.addAll(appId, visibleList);
}
} catch (InterruptedException | ExecutionException e) {
throw new IllegalStateException(e);
}
}
} finally {
pool.shutdownNow();
}
}
/**
* Returns a set of app IDs that contains components resolved by the queries intent
* or provider that declared in the manifest of the querying package.
*
* @param setting The package to query.
* @return A set of app IDs.
*/
@NonNull
private ArraySet<Integer> getVisibleListOfQueryViaComponents(
@NonNull PackageStateInternal setting) {
final ArraySet<Integer> result = new ArraySet();
for (int i = mExistingSettings.size() - 1; i >= 0; i--) {
final PackageStateInternal otherSetting = mExistingSettings.valueAt(i);
if (setting.getAppId() == otherSetting.getAppId()) {
continue;
}
if (otherSetting.getPkg() == null || mForceQueryable.contains(
otherSetting.getAppId())) {
continue;
}
final boolean canQuery = canQueryViaComponents(
setting.getPkg(), otherSetting.getPkg(), mProtectedBroadcasts);
if (canQuery) {
result.add(otherSetting.getAppId());
}
}
return result;
}
}
}

View File

@@ -64,6 +64,14 @@ public class WatchedSparseSetArray<T> extends WatchableImpl implements Snappable
return res;
}
/**
* Add a set of values for key n.
*/
public void addAll(int n, ArraySet<T> values) {
mStorage.addAll(n, values);
onChanged();
}
/**
* Removes all mappings from this SparseSetArray.
*/