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

Commit 0e2c0f37 authored by Kenny Root's avatar Kenny Root
Browse files

Break apart queries to getInstalled* API

To avoid blowing past the Binder IPC limit, change the
PackageManagerService to have a DB-like interaction where the client
tells the service the last "row" that it read.

The fact that we use a HashMap instead of a TreeMap makes this
problematic. For now we're just making a new ArrayList for the keys and
then sorting them for each call. This can make the API slower for callers
of this, but it's probably greatly overshadowed by the cost of the data
transfer itself.

Bug: 4064282
Change-Id: Ic370fd148d4c3813ae4f2daffa1a7c28d63d5a09
parent 71a556f2
Loading
Loading
Loading
Loading
+19 −2
Original line number Diff line number Diff line
@@ -30,14 +30,15 @@ import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
import android.os.Parcel;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;

import java.io.File;
import java.lang.reflect.Field;
@@ -223,7 +224,7 @@ public final class Pm {
        String filter = nextArg();

        try {
            List<PackageInfo> packages = mPm.getInstalledPackages(getFlags);
            final List<PackageInfo> packages = getInstalledPackages(mPm, getFlags);

            int count = packages.size();
            for (int p = 0 ; p < count ; p++) {
@@ -247,6 +248,22 @@ public final class Pm {
        }
    }

    @SuppressWarnings("unchecked")
    private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags)
            throws RemoteException {
        final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
        PackageInfo lastItem = null;
        ParceledListSlice<PackageInfo> slice;

        do {
            final String lastKey = lastItem != null ? lastItem.packageName : null;
            slice = pm.getInstalledPackages(flags, lastKey);
            lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
        } while (!slice.isLastSlice());

        return packageInfos;
    }

    /**
     * Lists all of the features supported by the current device.
     *
+26 −2
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@ import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
@@ -84,6 +85,7 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IPowerManager;
import android.os.Looper;
import android.os.Parcel;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
@@ -2000,19 +2002,41 @@ class ContextImpl extends Context {
            throw new NameNotFoundException("No shared userid for user:"+sharedUserName);
        }

        @SuppressWarnings("unchecked")
        @Override
        public List<PackageInfo> getInstalledPackages(int flags) {
            try {
                return mPM.getInstalledPackages(flags);
                final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
                PackageInfo lastItem = null;
                ParceledListSlice<PackageInfo> slice;

                do {
                    final String lastKey = lastItem != null ? lastItem.packageName : null;
                    slice = mPM.getInstalledPackages(flags, lastKey);
                    lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
                } while (!slice.isLastSlice());

                return packageInfos;
            } catch (RemoteException e) {
                throw new RuntimeException("Package manager has died", e);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public List<ApplicationInfo> getInstalledApplications(int flags) {
            try {
                return mPM.getInstalledApplications(flags);
                final List<ApplicationInfo> applicationInfos = new ArrayList<ApplicationInfo>();
                ApplicationInfo lastItem = null;
                ParceledListSlice<ApplicationInfo> slice;

                do {
                    final String lastKey = lastItem != null ? lastItem.packageName : null;
                    slice = mPM.getInstalledApplications(flags, lastKey);
                    lastItem = slice.populateList(applicationInfos, ApplicationInfo.CREATOR);
                } while (!slice.isLastSlice());

                return applicationInfos;
            } catch (RemoteException e) {
                throw new RuntimeException("Package manager has died", e);
            }
+15 −2
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.content.pm.IPackageMoveObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProviderInfo;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
@@ -109,9 +110,21 @@ interface IPackageManager {
    List<ResolveInfo> queryIntentServices(in Intent intent,
            String resolvedType, int flags);

    List<PackageInfo> getInstalledPackages(int flags);
    /**
     * This implements getInstalledPackages via a "last returned row"
     * mechanism that is not exposed in the API. This is to get around the IPC
     * limit that kicks in when flags are included that bloat up the data
     * returned.
     */
    ParceledListSlice getInstalledPackages(int flags, in String lastRead);

    List<ApplicationInfo> getInstalledApplications(int flags);
    /**
     * This implements getInstalledApplications via a "last returned row"
     * mechanism that is not exposed in the API. This is to get around the IPC
     * limit that kicks in when flags are included that bloat up the data
     * returned.
     */
    ParceledListSlice getInstalledApplications(int flags, in String lastRead);

    /**
     * Retrieve all applications that are marked as persistent.
+19 −0
Original line number Diff line number Diff line
/*
 * Copyright 2011, 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 ParceledListSlice;
+170 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2011 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.os.Parcel;
import android.os.Parcelable;

import java.util.List;

/**
 * Builds up a parcel that is discarded when written to another parcel or
 * written to a list. This is useful for API that sends huge lists across a
 * Binder that may be larger than the IPC limit.
 *
 * @hide
 */
public class ParceledListSlice<T extends Parcelable> implements Parcelable {
    /*
     * TODO get this number from somewhere else. For now set it to a quarter of
     * the 1MB limit.
     */
    private static final int MAX_IPC_SIZE = 256 * 1024;

    private Parcel mParcel;

    private int mNumItems;

    private boolean mIsLastSlice;

    public ParceledListSlice() {
        mParcel = Parcel.obtain();
    }

    private ParceledListSlice(Parcel p, int numItems, boolean lastSlice) {
        mParcel = p;
        mNumItems = numItems;
        mIsLastSlice = lastSlice;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    /**
     * Write this to another Parcel. Note that this discards the internal Parcel
     * and should not be used anymore. This is so we can pass this to a Binder
     * where we won't have a chance to call recycle on this.
     */
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mNumItems);
        dest.writeInt(mIsLastSlice ? 1 : 0);

        if (mNumItems > 0) {
            final int parcelSize = mParcel.dataSize();
            dest.writeInt(parcelSize);
            dest.appendFrom(mParcel, 0, parcelSize);
        }

        mNumItems = 0;
        mParcel.recycle();
        mParcel = null;
    }

    /**
     * Appends a parcel to this list slice.
     *
     * @param item Parcelable item to append to this list slice
     * @return true when the list slice is full and should not be appended to
     *         anymore
     */
    public boolean append(T item) {
        if (mParcel == null) {
            throw new IllegalStateException("ParceledListSlice has already been recycled");
        }

        item.writeToParcel(mParcel, PARCELABLE_WRITE_RETURN_VALUE);
        mNumItems++;

        return mParcel.dataSize() > MAX_IPC_SIZE;
    }

    /**
     * Populates a list and discards the internal state of the
     * ParceledListSlice in the process. The instance should
     * not be used anymore.
     *
     * @param list list to insert items from this slice.
     * @param creator creator that knows how to unparcel the
     *        target object type.
     * @return the last item inserted into the list or null if none.
     */
    public T populateList(List<T> list, Creator<T> creator) {
        mParcel.setDataPosition(0);

        T item = null;
        for (int i = 0; i < mNumItems; i++) {
            item = creator.createFromParcel(mParcel);
            list.add(item);
        }

        mParcel.recycle();
        mParcel = null;

        return item;
    }

    /**
     * Sets whether this is the last list slice in the series.
     *
     * @param lastSlice
     */
    public void setLastSlice(boolean lastSlice) {
        mIsLastSlice = lastSlice;
    }

    /**
     * Returns whether this is the last slice in a series of slices.
     *
     * @return true if this is the last slice in the series.
     */
    public boolean isLastSlice() {
        return mIsLastSlice;
    }

    @SuppressWarnings("unchecked")
    public static final Parcelable.Creator<ParceledListSlice> CREATOR =
            new Parcelable.Creator<ParceledListSlice>() {
        public ParceledListSlice createFromParcel(Parcel in) {
            final int numItems = in.readInt();
            final boolean lastSlice = in.readInt() == 1;

            if (numItems > 0) {
                final int parcelSize = in.readInt();

                // Advance within this Parcel
                int offset = in.dataPosition();
                in.setDataPosition(offset + parcelSize);

                Parcel p = Parcel.obtain();
                p.setDataPosition(0);
                p.appendFrom(in, offset, parcelSize);
                p.setDataPosition(0);

                return new ParceledListSlice(p, numItems, lastSlice);
            } else {
                return new ParceledListSlice();
            }
        }

        public ParceledListSlice[] newArray(int size) {
            return new ParceledListSlice[size];
        }
    };
}
Loading