Merge "Squash same ApplicationInfo's in Parcel" into rvc-dev am: 9577cd78ff

Change-Id: Idd92356c84cf7d891083f08ee2ac230a740e52c9
This commit is contained in:
Automerger Merge Worker
2020-02-25 15:20:36 +00:00
14 changed files with 353 additions and 48 deletions

View File

@@ -981,6 +981,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<android.content.pm.ProviderInfo>);
method @NonNull public java.util.List<android.content.pm.ProviderInfo> getList();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.ProviderInfoList> CREATOR;
}
public final class ShortcutInfo implements android.os.Parcelable {
method public boolean isVisibleToPublisher();
}
@@ -2469,7 +2477,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 {

View File

@@ -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<ProviderInfo> 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;

View File

@@ -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<ProviderInfo> 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,

View File

@@ -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<ApplicationInfo> CREATOR
= new Parcelable.Creator<ApplicationInfo>() {
@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];
}

View File

@@ -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();

View File

@@ -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<PackageInfo> 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;
}
}
}
}

View File

@@ -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;

View File

@@ -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<ProviderInfo>, but it "squashes" the ApplicationInfo in the elements.
*
* @hide
*/
@TestApi
public final class ProviderInfoList implements Parcelable {
private final List<ProviderInfo> mList;
private ProviderInfoList(Parcel source) {
final ArrayList<ProviderInfo> list = new ArrayList<>();
source.readTypedList(list, ProviderInfo.CREATOR);
mList = list;
}
private ProviderInfoList(List<ProviderInfo> 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<ProviderInfoList> CREATOR
= new Parcelable.Creator<ProviderInfoList>() {
@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<ProviderInfo> 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<ProviderInfo> list) {
return new ProviderInfoList(list);
}
}

View File

@@ -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<Parcelable, Integer> 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<Integer, Parcelable> 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<T> {
/** 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 extends Parcelable> T readSquashed(SquashReadHelper<T> 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

View File

@@ -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<ProviderInfo> 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,

View File

@@ -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(),

View File

@@ -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<ProviderInfo> providerList = null;
// Map from a package name to the corresponding app info.
ArrayMap<String, ApplicationInfo> 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;
}

View File

@@ -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) {

View File

@@ -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());
}