From 2d80ca09fc1ee076c69d709639b2c3da1a5bc312 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Wed, 12 Feb 2020 14:20:36 -0800 Subject: [PATCH] Squash same ApplicationInfo's in Parcel ApplicationInfo now automatically tries to "squash" the same instances in a Parcel. NOTE: This CL still does *not* optimize the package manager APIs that return a list. e.g. PM.queryContentProviders() still return duplicate AppInfo's. We can optimize them by making ParcelableListSlice call "allowSquashing", but that *could* have negative side effects, so I'm not doing it in this CL. I think we can do that for S. Bug: 148588589 Test: atest CtsContentTestCases # except for two preexsiting failures: - android.content.pm.cts.PackageManagerTest#testGetIcon - android.content.pm.cts.PackageManagerTest#testGetPreferredActivities Test: Use the debugger and make sure bindApplication() is not receiving duplicate AppInfo's in the provider list. Change-Id: I3ba2c047a469169340c0f75c36bdfd394bc5d627 (cherry picked from commit 7d09275d700d3b7596da3a08f3a9594845cec7ab) --- api/test-current.txt | 10 + core/java/android/app/ActivityThread.java | 6 +- core/java/android/app/IApplicationThread.aidl | 3 +- .../android/content/pm/ApplicationInfo.java | 8 +- .../android/content/pm/ComponentInfo.java | 13 +- core/java/android/content/pm/PackageInfo.java | 27 +-- .../android/content/pm/ProviderInfoList.aidl | 19 ++ .../android/content/pm/ProviderInfoList.java | 87 +++++++++ core/java/android/os/Parcel.java | 177 +++++++++++++++++- .../TransactionParcelTests.java | 3 +- .../server/am/ActivityManagerService.java | 6 +- .../android/server/pm/ComponentResolver.java | 27 ++- .../server/pm/parsing/PackageInfoUtils.java | 8 +- .../android/server/pm/PackageParserTest.java | 7 - 14 files changed, 353 insertions(+), 48 deletions(-) create mode 100644 core/java/android/content/pm/ProviderInfoList.aidl create mode 100644 core/java/android/content/pm/ProviderInfoList.java diff --git a/api/test-current.txt b/api/test-current.txt index 2d15c0ea0ffb9..a7f506102547e 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -975,6 +975,14 @@ package android.content.pm { field @Nullable public final String backgroundPermission; } + public final class ProviderInfoList implements android.os.Parcelable { + method public int describeContents(); + method @NonNull public static android.content.pm.ProviderInfoList fromList(@NonNull java.util.List); + method @NonNull public java.util.List getList(); + method public void writeToParcel(@NonNull android.os.Parcel, int); + field @NonNull public static final android.os.Parcelable.Creator CREATOR; + } + public final class ShortcutInfo implements android.os.Parcelable { method public boolean isVisibleToPublisher(); } @@ -2463,7 +2471,9 @@ package android.os { } public final class Parcel { + method public boolean allowSquashing(); method public int readExceptionCode(); + method public void restoreAllowSquashing(boolean); } public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable { diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 92dd91a877a9c..0ed5aec58924a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -68,6 +68,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.res.AssetManager; import android.content.res.CompatibilityInfo; @@ -1011,8 +1012,9 @@ public final class ActivityThread extends ClientTransactionHandler { sendMessage(H.STOP_SERVICE, token); } + @Override public final void bindApplication(String processName, ApplicationInfo appInfo, - List providers, ComponentName instrumentationName, + ProviderInfoList providerList, ComponentName instrumentationName, ProfilerInfo profilerInfo, Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher, IUiAutomationConnection instrumentationUiConnection, int debugMode, @@ -1052,7 +1054,7 @@ public final class ActivityThread extends ClientTransactionHandler { AppBindData data = new AppBindData(); data.processName = processName; data.appInfo = appInfo; - data.providers = providers; + data.providers = providerList.getList(); data.instrumentationName = instrumentationName; data.instrumentationArgs = instrumentationArgs; data.instrumentationWatcher = instrumentationWatcher; diff --git a/core/java/android/app/IApplicationThread.aidl b/core/java/android/app/IApplicationThread.aidl index c33c515f062c7..1f6e4cac199a4 100644 --- a/core/java/android/app/IApplicationThread.aidl +++ b/core/java/android/app/IApplicationThread.aidl @@ -30,6 +30,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -66,7 +67,7 @@ oneway interface IApplicationThread { @UnsupportedAppUsage void scheduleStopService(IBinder token); void bindApplication(in String packageName, in ApplicationInfo info, - in List providers, in ComponentName testName, + in ProviderInfoList providerList, in ComponentName testName, in ProfilerInfo profilerInfo, in Bundle testArguments, IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection, int debugMode, boolean enableBinderTracking, boolean trackAllocation, diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 9d1c677f35c61..4c6fef2e1856e 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -1630,6 +1630,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { @SuppressWarnings("unchecked") public void writeToParcel(Parcel dest, int parcelableFlags) { + if (dest.maybeWriteSquashed(this)) { + return; + } super.writeToParcel(dest, parcelableFlags); dest.writeString(taskAffinity); dest.writeString(permission); @@ -1700,9 +1703,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { + @Override public ApplicationInfo createFromParcel(Parcel source) { - return new ApplicationInfo(source); + return source.readSquashed(ApplicationInfo::new); } + + @Override public ApplicationInfo[] newArray(int size) { return new ApplicationInfo[size]; } diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java index 8b41c04b35494..362098c447cea 100644 --- a/core/java/android/content/pm/ComponentInfo.java +++ b/core/java/android/content/pm/ComponentInfo.java @@ -20,7 +20,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.graphics.drawable.Drawable; import android.os.Parcel; -import android.os.Parcelable; import android.util.Printer; /** @@ -197,12 +196,7 @@ public class ComponentInfo extends PackageItemInfo { public void writeToParcel(Parcel dest, int parcelableFlags) { super.writeToParcel(dest, parcelableFlags); - if ((parcelableFlags & Parcelable.PARCELABLE_ELIDE_DUPLICATES) != 0) { - dest.writeInt(0); - } else { - dest.writeInt(1); - applicationInfo.writeToParcel(dest, parcelableFlags); - } + applicationInfo.writeToParcel(dest, parcelableFlags); dest.writeString(processName); dest.writeString(splitName); dest.writeInt(descriptionRes); @@ -213,10 +207,7 @@ public class ComponentInfo extends PackageItemInfo { protected ComponentInfo(Parcel source) { super(source); - final boolean hasApplicationInfo = (source.readInt() != 0); - if (hasApplicationInfo) { - applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); - } + applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); processName = source.readString(); splitName = source.readString(); descriptionRes = source.readInt(); diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index 36fa5728b34e5..85c698f3fb0c4 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -439,6 +439,8 @@ public class PackageInfo implements Parcelable { @Override public void writeToParcel(Parcel dest, int parcelableFlags) { + // Allow ApplicationInfo to be squashed. + final boolean prevAllowSquashing = dest.allowSquashing(); dest.writeString(packageName); dest.writeStringArray(splitNames); dest.writeInt(versionCode); @@ -457,10 +459,10 @@ public class PackageInfo implements Parcelable { dest.writeLong(firstInstallTime); dest.writeLong(lastUpdateTime); dest.writeIntArray(gids); - dest.writeTypedArray(activities, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); - dest.writeTypedArray(receivers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); - dest.writeTypedArray(services, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); - dest.writeTypedArray(providers, parcelableFlags | Parcelable.PARCELABLE_ELIDE_DUPLICATES); + dest.writeTypedArray(activities, parcelableFlags); + dest.writeTypedArray(receivers, parcelableFlags); + dest.writeTypedArray(services, parcelableFlags); + dest.writeTypedArray(providers, parcelableFlags); dest.writeTypedArray(instrumentation, parcelableFlags); dest.writeTypedArray(permissions, parcelableFlags); dest.writeStringArray(requestedPermissions); @@ -488,6 +490,7 @@ public class PackageInfo implements Parcelable { dest.writeInt(0); } dest.writeBoolean(isApex); + dest.restoreAllowSquashing(prevAllowSquashing); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -550,21 +553,5 @@ public class PackageInfo implements Parcelable { signingInfo = SigningInfo.CREATOR.createFromParcel(source); } isApex = source.readBoolean(); - // The component lists were flattened with the redundant ApplicationInfo - // instances omitted. Distribute the canonical one here as appropriate. - if (applicationInfo != null) { - propagateApplicationInfo(applicationInfo, activities); - propagateApplicationInfo(applicationInfo, receivers); - propagateApplicationInfo(applicationInfo, services); - propagateApplicationInfo(applicationInfo, providers); - } - } - - private void propagateApplicationInfo(ApplicationInfo appInfo, ComponentInfo[] components) { - if (components != null) { - for (ComponentInfo ci : components) { - ci.applicationInfo = appInfo; - } - } } } diff --git a/core/java/android/content/pm/ProviderInfoList.aidl b/core/java/android/content/pm/ProviderInfoList.aidl new file mode 100644 index 0000000000000..bb576d77003d4 --- /dev/null +++ b/core/java/android/content/pm/ProviderInfoList.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2020 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 android.content.pm; + +parcelable ProviderInfoList; diff --git a/core/java/android/content/pm/ProviderInfoList.java b/core/java/android/content/pm/ProviderInfoList.java new file mode 100644 index 0000000000000..566be2e32fe0a --- /dev/null +++ b/core/java/android/content/pm/ProviderInfoList.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2020 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 android.content.pm; + +import android.annotation.NonNull; +import android.annotation.TestApi; +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +/** + * Equivalent to List, but it "squashes" the ApplicationInfo in the elements. + * + * @hide + */ +@TestApi +public final class ProviderInfoList implements Parcelable { + private final List mList; + + private ProviderInfoList(Parcel source) { + final ArrayList list = new ArrayList<>(); + source.readTypedList(list, ProviderInfo.CREATOR); + mList = list; + } + + private ProviderInfoList(List list) { + mList = list; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(@NonNull Parcel dest, int flags) { + // Allow ApplicationInfo to be squashed. + final boolean prevAllowSquashing = dest.allowSquashing(); + dest.writeTypedList(mList, flags); + dest.restoreAllowSquashing(prevAllowSquashing); + } + + public static final @android.annotation.NonNull Parcelable.Creator CREATOR + = new Parcelable.Creator() { + @Override + public ProviderInfoList createFromParcel(@NonNull Parcel source) { + return new ProviderInfoList(source); + } + + @Override + public ProviderInfoList[] newArray(int size) { + return new ProviderInfoList[size]; + } + }; + + /** + * Return the stored list. + */ + @NonNull + public List getList() { + return mList; + } + + /** + * Create a new instance with a {@code list}. The passed list will be shared with the new + * instance, so the caller shouldn't modify it. + */ + @NonNull + public static ProviderInfoList fromList(@NonNull List list) { + return new ProviderInfoList(list); + } +} diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 1a4dac78855f8..f0b7b5fa5a1a2 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -28,6 +28,7 @@ import android.util.ExceptionUtils; import android.util.Log; import android.util.Size; import android.util.SizeF; +import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.SparseIntArray; @@ -1826,6 +1827,179 @@ public final class Parcel { writeString(name); } + /** + * A map used by {@link #maybeWriteSquashed} to keep track of what parcelables have + * been seen, and what positions they were written. The value is the absolute position of + * each parcelable. + */ + private ArrayMap mWrittenSquashableParcelables; + + private void ensureWrittenSquashableParcelables() { + if (mWrittenSquashableParcelables != null) { + return; + } + mWrittenSquashableParcelables = new ArrayMap<>(); + } + + private boolean mAllowSquashing = false; + + /** + * Allow "squashing" writes in {@link #maybeWriteSquashed}. This allows subsequent calls to + * {@link #maybeWriteSquashed(Parcelable)} to "squash" the same instances into one in a Parcel. + * + * Typically, this method is called at the beginning of {@link Parcelable#writeToParcel}. The + * caller must retain the return value from this method and call {@link #restoreAllowSquashing} + * with it. + * + * See {@link #maybeWriteSquashed(Parcelable)} for the details. + * + * @see #restoreAllowSquashing(boolean) + * @see #maybeWriteSquashed(Parcelable) + * @see #readSquashed(SquashReadHelper) + * + * @hide + */ + @TestApi + public boolean allowSquashing() { + boolean previous = mAllowSquashing; + mAllowSquashing = true; + return previous; + } + + /** + * @see #allowSquashing() + * @hide + */ + @TestApi + public void restoreAllowSquashing(boolean previous) { + mAllowSquashing = previous; + if (!mAllowSquashing) { + mWrittenSquashableParcelables = null; + } + } + + private void resetSqaushingState() { + if (mAllowSquashing) { + Slog.wtf(TAG, "allowSquashing wasn't restored."); + } + mWrittenSquashableParcelables = null; + mReadSquashableParcelables = null; + mAllowSquashing = false; + } + + /** + * A map used by {@link #readSquashed} to cache parcelables. It's a map from + * an absolute position in a Parcel to the parcelable stored at the position. + */ + private ArrayMap mReadSquashableParcelables; + + private void ensureReadSquashableParcelables() { + if (mReadSquashableParcelables != null) { + return; + } + mReadSquashableParcelables = new ArrayMap<>(); + } + + /** + * Write a parcelable with "squash" -- that is, when the same instance is written to the + * same Parcelable multiple times, instead of writing the entire instance multiple times, + * only write it once, and in subsequent writes we'll only write the offset to the original + * object. + * + * This approach does not work of the resulting Parcel is copied with {@link #appendFrom} with + * a non-zero offset, so we do not enable this behavior by default. Instead, we only enable + * it between {@link #allowSquashing} and {@link #restoreAllowSquashing}, in order to make sure + * we only do so within each "top level" Parcelable. + * + * Usage: Use this method in {@link Parcelable#writeToParcel}. + * If this method returns TRUE, it's a subsequent call, and the offset is already written, + * so the caller doesn't have to do anything. If this method returns FALSE, it's the first + * time for the instance to be written to this parcel. The caller has to proceed with its + * {@link Parcelable#writeToParcel}. + * + * (See {@code ApplicationInfo} for the example.) + * + * @param p the target Parcelable to write. + * + * @see #allowSquashing() + * @see #restoreAllowSquashing(boolean) + * @see #readSquashed(SquashReadHelper) + * + * @hide + */ + public boolean maybeWriteSquashed(@NonNull Parcelable p) { + if (!mAllowSquashing) { + // Don't squash, and don't put it in the map either. + writeInt(0); + return false; + } + ensureWrittenSquashableParcelables(); + final Integer firstPos = mWrittenSquashableParcelables.get(p); + if (firstPos != null) { + // Already written. + // Write the relative offset from the current position to the first position. + final int pos = dataPosition(); + + // We want the offset from the next byte of this integer, so we need to +4. + writeInt(pos - firstPos + 4); + return true; + } + // First time seen, write a marker. + writeInt(0); + + // Remember the position. + final int pos = dataPosition(); + mWrittenSquashableParcelables.put(p, pos); + + // Return false and let the caller actually write the content. + return false; + } + + /** + * Helper function that's used by {@link #readSquashed(SquashReadHelper)} + * @hide + */ + public interface SquashReadHelper { + /** Read and instantiate {@code T} from a Parcel. */ + @NonNull + T readRawParceled(@NonNull Parcel p); + } + + /** + * Read a {@link Parcelable} that's written with {@link #maybeWriteSquashed}. + * + * @param reader a callback function that instantiates an instance from a parcel. + * Typicallly, a lambda to the instructor that takes a {@link Parcel} is passed. + * + * @see #maybeWriteSquashed(Parcelable) + * + * @hide + */ + @SuppressWarnings("unchecked") + @Nullable + public T readSquashed(SquashReadHelper reader) { + final int offset = readInt(); + final int pos = dataPosition(); + + if (offset == 0) { + // First time read. Unparcel, and remember it. + final T p = reader.readRawParceled(this); + ensureReadSquashableParcelables(); + mReadSquashableParcelables.put(pos, p); + return p; + } + // Subsequent read. + final int firstAbsolutePos = pos - offset; + + final Parcelable p = mReadSquashableParcelables.get(firstAbsolutePos); + if (p == null) { + Slog.wtfStack(TAG, "Map doesn't contain offset " + + firstAbsolutePos + + " : contains=" + new ArrayList<>(mReadSquashableParcelables.keySet())); + } + return (T) p; + } + /** * Write a generic serializable object in to a Parcel. It is strongly * recommended that this method be avoided, since the serialization @@ -3247,6 +3421,7 @@ public final class Parcel { } private void freeBuffer() { + resetSqaushingState(); if (mOwnsNativeParcelObject) { updateNativeSize(nativeFreeBuffer(mNativePtr)); } @@ -3254,6 +3429,7 @@ public final class Parcel { } private void destroy() { + resetSqaushingState(); if (mNativePtr != 0) { if (mOwnsNativeParcelObject) { nativeDestroy(mNativePtr); @@ -3261,7 +3437,6 @@ public final class Parcel { } mNativePtr = 0; } - mReadWriteHelper = null; } @Override diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java index 372b8c2947026..f4fbefe9dde46 100644 --- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java +++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java @@ -37,6 +37,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ParceledListSlice; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ServiceInfo; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; @@ -397,7 +398,7 @@ public class TransactionParcelTests { @Override public void bindApplication(String s, ApplicationInfo applicationInfo, - List list, ComponentName componentName, ProfilerInfo profilerInfo, + ProviderInfoList list, ComponentName componentName, ProfilerInfo profilerInfo, Bundle bundle, IInstrumentationWatcher iInstrumentationWatcher, IUiAutomationConnection iUiAutomationConnection, int i, boolean b, boolean b1, boolean b2, boolean b3, Configuration configuration, diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ca0b03dff259e..513448b00794c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -218,6 +218,7 @@ import android.content.pm.PathPermission; import android.content.pm.PermissionInfo; import android.content.pm.ProcessInfo; import android.content.pm.ProviderInfo; +import android.content.pm.ProviderInfoList; import android.content.pm.ResolveInfo; import android.content.pm.SELinuxUtil; import android.content.pm.ServiceInfo; @@ -5151,12 +5152,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (mPlatformCompat != null) { mPlatformCompat.resetReporting(app.info); } + final ProviderInfoList providerList = ProviderInfoList.fromList(providers); if (app.isolatedEntryPoint != null) { // This is an isolated process which should just call an entry point instead of // being bound to an application. thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs); } else if (instr2 != null) { - thread.bindApplication(processName, appInfo, providers, + thread.bindApplication(processName, appInfo, providerList, instr2.mClass, profilerInfo, instr2.mArguments, instr2.mWatcher, @@ -5169,7 +5171,7 @@ public class ActivityManagerService extends IActivityManager.Stub buildSerial, autofillOptions, contentCaptureOptions, app.mDisabledCompatChanges); } else { - thread.bindApplication(processName, appInfo, providers, null, profilerInfo, + thread.bindApplication(processName, appInfo, providerList, null, profilerInfo, null, null, null, testMode, mBinderTransactionTrackingEnabled, enableTrackAllocation, isRestrictedBackupMode || !normalMode, app.isPersistent(), diff --git a/services/core/java/com/android/server/pm/ComponentResolver.java b/services/core/java/com/android/server/pm/ComponentResolver.java index 85810e3a99548..e86a42c284b6d 100644 --- a/services/core/java/com/android/server/pm/ComponentResolver.java +++ b/services/core/java/com/android/server/pm/ComponentResolver.java @@ -28,6 +28,7 @@ import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ActivityInfo; +import android.content.pm.ApplicationInfo; import android.content.pm.AuxiliaryResolveInfo; import android.content.pm.InstantAppResolveInfo; import android.content.pm.PackageManager; @@ -272,6 +273,9 @@ public class ComponentResolver { return null; } List providerList = null; + + // Map from a package name to the corresponding app info. + ArrayMap appInfos = null; synchronized (mLock) { for (int i = mProviders.mProviders.size() - 1; i >= 0; --i) { final ParsedProvider p = mProviders.mProviders.valueAt(i); @@ -300,8 +304,29 @@ public class ComponentResolver { && (p.getMetaData() == null || !p.getMetaData().containsKey(metaDataKey))) { continue; } + + // Make sure we have AppInfo for this provider. + final PackageUserState state = ps.readUserState(userId); + ApplicationInfo appInfo = + (appInfos == null) ? null : appInfos.get(pkg.getPackageName()); + if (appInfo == null) { + appInfo = PackageInfoUtils.generateApplicationInfo( + pkg, flags, state, userId, ps); + if (appInfo == null) { + // In this case, we should avoid calling generateApplicationInfo() for + // the same package in subsequent iterations, but appInfo shouldn't be null + // here, so we don't bother. + continue; + } + if (appInfos == null) { + appInfos = new ArrayMap<>(4); + } + appInfos.put(pkg.getPackageName(), appInfo); + } + // At this point, appInfo != null. + final ProviderInfo info = PackageInfoUtils.generateProviderInfo( - pkg, p, flags, ps.readUserState(userId), userId, ps); + pkg, p, flags, state, appInfo, userId, ps); if (info == null) { continue; } diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java index 23bdf5f101f37..f5ce0804236d1 100644 --- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java +++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java @@ -312,8 +312,14 @@ public class PackageInfoUtils { /** * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage. + * + * @deprecated use {@link #generateProviderInfo( + * AndroidPackage, ParsedProvider, int, PackageUserState, ApplicationInfo, int, PackageSetting)} + * instead and pass {@link ApplicationInfo} explicitly to avoid generating duplicate instances + * of it. */ @Nullable + @Deprecated public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, int userId, @Nullable PackageSetting pkgSetting) { @@ -324,7 +330,7 @@ public class PackageInfoUtils { * @param pkgSetting See {@link PackageInfoUtils} for description of pkgSetting usage. */ @Nullable - private static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p, + public static ProviderInfo generateProviderInfo(AndroidPackage pkg, ParsedProvider p, @PackageManager.ComponentInfoFlags int flags, PackageUserState state, @Nullable ApplicationInfo applicationInfo, int userId, @Nullable PackageSetting pkgSetting) { diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java index d2ec50001789e..a19d919763079 100644 --- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java @@ -430,13 +430,6 @@ public class PackageParserTest { ParsedProvider b ) { assertComponentsEqual(a, b); - - // Sanity check for ProviderInfo - ProviderInfo aInfo = PackageInfoUtils.generateProviderInfo(aPkg, a, 0, - new PackageUserState(), 0, mockPkgSetting(aPkg)); - ProviderInfo bInfo = PackageInfoUtils.generateProviderInfo(bPkg, b, 0, - new PackageUserState(), 0, mockPkgSetting(bPkg)); - assertApplicationInfoEqual(aInfo.applicationInfo, bInfo.applicationInfo); assertEquals(a.getName(), b.getName()); }