Rework ParceledListSlice to be much easier to use.

Take advantage of this to return better information about
packages filtered by permissions -- include the permissions
they have in the requested array.

Also fix issue #8026793 (Contact picture shows default pic
while searching for a contact in qsb) by using the base
package name of the Context when reporting the app name
of an operation.  Otherwise you could make a resource-only
context for another application and do calls through that
and get reported as the wrong app.

Change-Id: I5e0488bf773acea5a3d22f245641828e1a106fb8
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index 42c9d34..39539b4 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -321,17 +321,8 @@
     @SuppressWarnings("unchecked")
     private List<PackageInfo> getInstalledPackages(IPackageManager pm, int flags, int userId)
             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, userId);
-            lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
-        } while (!slice.isLastSlice());
-
-        return packageInfos;
+        ParceledListSlice<PackageInfo> slice = pm.getInstalledPackages(flags, userId);
+        return slice.getList();
     }
 
     /**
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index 4cea6a0..679c91d 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -228,7 +228,7 @@
     }
 
     public int noteOp(int op) {
-        return noteOp(op, Process.myUid(), mContext.getPackageName());
+        return noteOp(op, Process.myUid(), mContext.getBasePackageName());
     }
 
     public int startOp(int op, int uid, String packageName) {
@@ -252,7 +252,7 @@
     }
 
     public int startOp(int op) {
-        return startOp(op, Process.myUid(), mContext.getPackageName());
+        return startOp(op, Process.myUid(), mContext.getBasePackageName());
     }
 
     public void finishOp(int op, int uid, String packageName) {
@@ -263,6 +263,6 @@
     }
 
     public void finishOp(int op) {
-        finishOp(op, Process.myUid(), mContext.getPackageName());
+        finishOp(op, Process.myUid(), mContext.getBasePackageName());
     }
 }
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 2ef3944..f09c2fe 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -426,17 +426,8 @@
     @Override
     public List<PackageInfo> getInstalledPackages(int flags, int userId) {
         try {
-            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, userId);
-                lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
-            } while (!slice.isLastSlice());
-
-            return packageInfos;
+            ParceledListSlice<PackageInfo> slice = mPM.getInstalledPackages(flags, userId);
+            return slice.getList();
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -448,17 +439,9 @@
             String[] permissions, int flags) {
         final int userId = mContext.getUserId();
         try {
-            final List<PackageInfo> packageInfos = new ArrayList<PackageInfo>();
-            PackageInfo lastItem = null;
-            ParceledListSlice<PackageInfo> slice;
-
-            do {
-                final String lastKey = lastItem != null ? lastItem.packageName : null;
-                slice = mPM.getPackagesHoldingPermissions(permissions, flags, lastKey, userId);
-                lastItem = slice.populateList(packageInfos, PackageInfo.CREATOR);
-            } while (!slice.isLastSlice());
-
-            return packageInfos;
+            ParceledListSlice<PackageInfo> slice = mPM.getPackagesHoldingPermissions(
+                    permissions, flags, userId);
+            return slice.getList();
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
@@ -469,17 +452,8 @@
     public List<ApplicationInfo> getInstalledApplications(int flags) {
         final int userId = mContext.getUserId();
         try {
-            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, userId);
-                lastItem = slice.populateList(applicationInfos, ApplicationInfo.CREATOR);
-            } while (!slice.isLastSlice());
-
-            return applicationInfos;
+            ParceledListSlice<ApplicationInfo> slice = mPM.getInstalledApplications(flags, userId);
+            return slice.getList();
         } catch (RemoteException e) {
             throw new RuntimeException("Package manager has died", e);
         }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4925bf1..fd4389e 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -628,6 +628,12 @@
         return "android";
     }
 
+    /** @hide */
+    @Override
+    public String getBasePackageName() {
+        return mBasePackageName != null ? mBasePackageName : getPackageName();
+    }
+
     @Override
     public ApplicationInfo getApplicationInfo() {
         if (mPackageInfo != null) {
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index fb73529..63c97ba 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -208,7 +208,7 @@
 
     public ContentResolver(Context context) {
         mContext = context != null ? context : ActivityThread.currentApplication();
-        mPackageName = mContext.getPackageName();
+        mPackageName = context.getBasePackageName();
     }
 
     /** @hide */
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 14f2847..f7c28b6 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -418,6 +418,9 @@
     /** Return the name of this application's package. */
     public abstract String getPackageName();
 
+    /** @hide Return the name of the base context this context is derived from. */
+    public abstract String getBasePackageName();
+
     /** Return the full application info for this context's package. */
     public abstract ApplicationInfo getApplicationInfo();
 
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 6a61884..b63f45e 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -135,6 +135,12 @@
         return mBase.getPackageName();
     }
 
+    /** @hide */
+    @Override
+    public String getBasePackageName() {
+        return mBase.getBasePackageName();
+    }
+
     @Override
     public ApplicationInfo getApplicationInfo() {
         return mBase.getApplicationInfo();
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 4c9c278..0445b39 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -127,7 +127,7 @@
      * limit that kicks in when flags are included that bloat up the data
      * returned.
      */
-    ParceledListSlice getInstalledPackages(int flags, in String lastRead, in int userId);
+    ParceledListSlice getInstalledPackages(int flags, in int userId);
 
     /**
      * This implements getPackagesHoldingPermissions via a "last returned row"
@@ -136,7 +136,7 @@
      * returned.
      */
     ParceledListSlice getPackagesHoldingPermissions(in String[] permissions,
-            int flags, in String lastRead, int userId);
+            int flags, int userId);
 
     /**
      * This implements getInstalledApplications via a "last returned row"
@@ -144,7 +144,7 @@
      * limit that kicks in when flags are included that bloat up the data
      * returned.
      */
-    ParceledListSlice getInstalledApplications(int flags, in String lastRead, int userId);
+    ParceledListSlice getInstalledApplications(int flags, int userId);
 
     /**
      * Retrieve all applications that are marked as persistent.
diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java
index f3a98db4..8a43472 100644
--- a/core/java/android/content/pm/ParceledListSlice.java
+++ b/core/java/android/content/pm/ParceledListSlice.java
@@ -16,44 +16,92 @@
 
 package android.content.pm;
 
+import android.os.Binder;
+import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
 
+import java.util.ArrayList;
 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.
+ * Transfer a large list of Parcelable objects across an IPC.  Splits into
+ * multiple transactions if needed.
  *
  * @hide
  */
 public class ParceledListSlice<T extends Parcelable> implements Parcelable {
+    private static String TAG = "ParceledListSlice";
+    private static boolean DEBUG = false;
+
     /*
      * 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 static final int MAX_FIRST_IPC_SIZE = MAX_IPC_SIZE / 2;
 
-    private Parcel mParcel;
+    private final List<T> mList;
 
-    private int mNumItems;
-
-    private boolean mIsLastSlice;
-
-    public ParceledListSlice() {
-        mParcel = Parcel.obtain();
+    public ParceledListSlice(List<T> list) {
+        mList = list;
     }
 
-    private ParceledListSlice(Parcel p, int numItems, boolean lastSlice) {
-        mParcel = p;
-        mNumItems = numItems;
-        mIsLastSlice = lastSlice;
+    private ParceledListSlice(Parcel p, ClassLoader loader) {
+        final int N = p.readInt();
+        mList = new ArrayList<T>(N);
+        if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
+        if (N <= 0) {
+            return;
+        }
+        Parcelable.Creator<T> creator = p.readParcelableCreator(loader);
+        int i = 0;
+        while (i < N) {
+            if (p.readInt() == 0) {
+                break;
+            }
+            mList.add(p.readCreator(creator, loader));
+            if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
+            i++;
+        }
+        if (i >= N) {
+            return;
+        }
+        final IBinder retriever = p.readStrongBinder();
+        while (i < N) {
+            if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
+            Parcel data = Parcel.obtain();
+            Parcel reply = Parcel.obtain();
+            data.writeInt(i);
+            try {
+                retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
+                return;
+            }
+            while (i < N && reply.readInt() != 0) {
+                mList.add(reply.readCreator(creator, loader));
+                if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
+                i++;
+            }
+            reply.recycle();
+            data.recycle();
+        }
+    }
+
+    public List<T> getList() {
+        return mList;
     }
 
     @Override
     public int describeContents() {
-        return 0;
+        int contents = 0;
+        for (int i=0; i<mList.size(); i++) {
+            contents |= mList.get(i).describeContents();
+        }
+        return contents;
     }
 
     /**
@@ -63,104 +111,59 @@
      */
     @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);
+        final int N = mList.size();
+        final int callFlags = flags;
+        dest.writeInt(N);
+        if (DEBUG) Log.d(TAG, "Writing " + N + " items");
+        if (N > 0) {
+            dest.writeParcelableCreator(mList.get(0));
+            int i = 0;
+            while (i < N && dest.dataSize() < MAX_FIRST_IPC_SIZE) {
+                dest.writeInt(1);
+                mList.get(i).writeToParcel(dest, callFlags);
+                if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
+                i++;
+            }
+            if (i < N) {
+                dest.writeInt(0);
+                Binder retriever = new Binder() {
+                    @Override
+                    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+                            throws RemoteException {
+                        if (code != FIRST_CALL_TRANSACTION) {
+                            return super.onTransact(code, data, reply, flags);
+                        }
+                        int i = data.readInt();
+                        if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
+                        while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
+                            reply.writeInt(1);
+                            mList.get(i).writeToParcel(reply, callFlags);
+                            if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
+                            i++;
+                        }
+                        if (i < N) {
+                            if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
+                            reply.writeInt(0);
+                        }
+                        return true;
+                    }
+                };
+                if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
+                dest.writeStrongBinder(retriever);
+            }
         }
-
-        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 static final Parcelable.ClassLoaderCreator<ParceledListSlice> CREATOR =
+            new Parcelable.ClassLoaderCreator<ParceledListSlice>() {
         public ParceledListSlice createFromParcel(Parcel in) {
-            final int numItems = in.readInt();
-            final boolean lastSlice = in.readInt() == 1;
+            return new ParceledListSlice(in, null);
+        }
 
-            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();
-            }
+        @Override
+        public ParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
+            return new ParceledListSlice(in, loader);
         }
 
         public ParceledListSlice[] newArray(int size) {
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index d69fef0..31d323b 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -1254,6 +1254,12 @@
         p.writeToParcel(this, parcelableFlags);
     }
 
+    /** @hide */
+    public final void writeParcelableCreator(Parcelable p) {
+        String name = p.getClass().getName();
+        writeString(name);
+    }
+
     /**
      * Write a generic serializable object in to a Parcel.  It is strongly
      * recommended that this method be avoided, since the serialization
@@ -2046,6 +2052,28 @@
      * was an error trying to instantiate the Parcelable.
      */
     public final <T extends Parcelable> T readParcelable(ClassLoader loader) {
+        Parcelable.Creator<T> creator = readParcelableCreator(loader);
+        if (creator == null) {
+            return null;
+        }
+        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+            return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader);
+        }
+        return creator.createFromParcel(this);
+    }
+
+    /** @hide */
+    public final <T extends Parcelable> T readCreator(Parcelable.Creator<T> creator,
+            ClassLoader loader) {
+        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
+            return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader);
+        }
+        return creator.createFromParcel(this);
+    }
+
+    /** @hide */
+    public final <T extends Parcelable> Parcelable.Creator<T> readParcelableCreator(
+            ClassLoader loader) {
         String name = readString();
         if (name == null) {
             return null;
@@ -2101,10 +2129,7 @@
             }
         }
 
-        if (creator instanceof Parcelable.ClassLoaderCreator<?>) {
-            return ((Parcelable.ClassLoaderCreator<T>)creator).createFromParcel(this, loader);
-        }
-        return creator.createFromParcel(this);
+        return creator;
     }
 
     /**
diff --git a/core/java/android/os/SystemVibrator.java b/core/java/android/os/SystemVibrator.java
index 54ea385..08eba4f 100644
--- a/core/java/android/os/SystemVibrator.java
+++ b/core/java/android/os/SystemVibrator.java
@@ -38,7 +38,7 @@
     }
 
     public SystemVibrator(Context context) {
-        mPackageName = context.getPackageName();
+        mPackageName = context.getBasePackageName();
         mService = IVibratorService.Stub.asInterface(
                 ServiceManager.getService("vibrator"));
     }
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 9b3795a..47987f1 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -2878,187 +2878,144 @@
         }
     }
 
-    private static final int getContinuationPoint(final String[] keys, final String key) {
-        final int index;
-        if (key == null) {
-            index = 0;
-        } else {
-            final int insertPoint = Arrays.binarySearch(keys, key);
-            if (insertPoint < 0) {
-                index = -insertPoint;
-            } else {
-                index = insertPoint + 1;
-            }
-        }
-        return index;
-    }
-
     @Override
-    public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, String lastRead,
-            int userId) {
-        final ParceledListSlice<PackageInfo> list = new ParceledListSlice<PackageInfo>();
+    public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
         final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
-        final String[] keys;
 
         enforceCrossUserPermission(Binder.getCallingUid(), userId, true, "get installed packages");
 
         // writer
         synchronized (mPackages) {
+            ArrayList<PackageInfo> list;
             if (listUninstalled) {
-                keys = mSettings.mPackages.keySet().toArray(new String[mSettings.mPackages.size()]);
-            } else {
-                keys = mPackages.keySet().toArray(new String[mPackages.size()]);
-            }
-
-            Arrays.sort(keys);
-            int i = getContinuationPoint(keys, lastRead);
-            final int N = keys.length;
-
-            while (i < N) {
-                final String packageName = keys[i++];
-
-                PackageInfo pi = null;
-                if (listUninstalled) {
-                    final PackageSetting ps = mSettings.mPackages.get(packageName);
-                    if (ps != null) {
+                list = new ArrayList<PackageInfo>(mSettings.mPackages.size());
+                for (PackageSetting ps : mSettings.mPackages.values()) {
+                    PackageInfo pi;
+                    if (ps.pkg != null) {
+                        pi = generatePackageInfo(ps.pkg, flags, userId);
+                    } else {
                         pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
                     }
-                } else {
-                    final PackageParser.Package p = mPackages.get(packageName);
-                    if (p != null) {
-                        pi = generatePackageInfo(p, flags, userId);
+                    if (pi != null) {
+                        list.add(pi);
                     }
                 }
-
-                if (pi != null && list.append(pi)) {
-                    break;
+            } else {
+                list = new ArrayList<PackageInfo>(mPackages.size());
+                for (PackageParser.Package p : mPackages.values()) {
+                    PackageInfo pi = generatePackageInfo(p, flags, userId);
+                    if (pi != null) {
+                        list.add(pi);
+                    }
                 }
             }
 
-            if (i == N) {
-                list.setLastSlice(true);
+            return new ParceledListSlice<PackageInfo>(list);
+        }
+    }
+
+    private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps,
+            String[] permissions, boolean[] tmp, int flags, int userId) {
+        int numMatch = 0;
+        for (int i=0; i<permissions.length; i++) {
+            if (ps.grantedPermissions.contains(permissions[i])) {
+                tmp[i] = true;
+                numMatch++;
+            } else {
+                tmp[i] = false;
             }
         }
-
-        return list;
+        if (numMatch == 0) {
+            return;
+        }
+        PackageInfo pi;
+        if (ps.pkg != null) {
+            pi = generatePackageInfo(ps.pkg, flags, userId);
+        } else {
+            pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
+        }
+        if ((flags&PackageManager.GET_PERMISSIONS) == 0) {
+            if (numMatch == permissions.length) {
+                pi.requestedPermissions = permissions;
+            } else {
+                pi.requestedPermissions = new String[numMatch];
+                numMatch = 0;
+                for (int i=0; i<permissions.length; i++) {
+                    if (tmp[i]) {
+                        pi.requestedPermissions[numMatch] = permissions[i];
+                        numMatch++;
+                    }
+                }
+            }
+        }
+        list.add(pi);
     }
 
     @Override
     public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
-            String[] permissions, int flags, String lastRead, int userId) {
+            String[] permissions, int flags, int userId) {
         if (!sUserManager.exists(userId)) return null;
-        final ParceledListSlice<PackageInfo> list = new ParceledListSlice<PackageInfo>();
         final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
 
         // writer
         synchronized (mPackages) {
-            ArrayList<String> keysList = new ArrayList<String>();
+            ArrayList<PackageInfo> list = new ArrayList<PackageInfo>();
+            boolean[] tmpBools = new boolean[permissions.length];
             if (listUninstalled) {
                 for (PackageSetting ps : mSettings.mPackages.values()) {
-                    for (String perm : permissions) {
-                        if (ps.grantedPermissions.contains(perm)) {
-                            keysList.add(ps.name);
-                            break;
-                        }
-                    }
+                    addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId);
                 }
             } else {
                 for (PackageParser.Package pkg : mPackages.values()) {
                     PackageSetting ps = (PackageSetting)pkg.mExtras;
                     if (ps != null) {
-                        for (String perm : permissions) {
-                            if (ps.grantedPermissions.contains(perm)) {
-                                keysList.add(ps.name);
-                                break;
-                            }
+                        addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags,
+                                userId);
+                    }
+                }
+            }
+
+            return new ParceledListSlice<PackageInfo>(list);
+        }
+    }
+
+    @Override
+    public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) {
+        if (!sUserManager.exists(userId)) return null;
+        final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
+
+        // writer
+        synchronized (mPackages) {
+            ArrayList<ApplicationInfo> list;
+            if (listUninstalled) {
+                list = new ArrayList<ApplicationInfo>(mSettings.mPackages.size());
+                for (PackageSetting ps : mSettings.mPackages.values()) {
+                    ApplicationInfo ai;
+                    if (ps.pkg != null) {
+                        ai = PackageParser.generateApplicationInfo(ps.pkg, flags,
+                                ps.readUserState(userId), userId);
+                    } else {
+                        ai = generateApplicationInfoFromSettingsLPw(ps.name, flags, userId);
+                    }
+                    if (ai != null) {
+                        list.add(ai);
+                    }
+                }
+            } else {
+                list = new ArrayList<ApplicationInfo>(mPackages.size());
+                for (PackageParser.Package p : mPackages.values()) {
+                    if (p.mExtras != null) {
+                        ApplicationInfo ai = PackageParser.generateApplicationInfo(p, flags,
+                                ((PackageSetting)p.mExtras).readUserState(userId), userId);
+                        if (ai != null) {
+                            list.add(ai);
                         }
                     }
                 }
             }
 
-            String[] keys = new String[keysList.size()];
-            keysList.toArray(keys);
-            Arrays.sort(keys);
-            int i = getContinuationPoint(keys, lastRead);
-            final int N = keys.length;
-
-            while (i < N) {
-                final String packageName = keys[i++];
-
-                PackageInfo pi = null;
-                if (listUninstalled) {
-                    final PackageSetting ps = mSettings.mPackages.get(packageName);
-                    if (ps != null) {
-                        pi = generatePackageInfoFromSettingsLPw(ps.name, flags, userId);
-                    }
-                } else {
-                    final PackageParser.Package p = mPackages.get(packageName);
-                    if (p != null) {
-                        pi = generatePackageInfo(p, flags, userId);
-                    }
-                }
-
-                if (pi != null && list.append(pi)) {
-                    break;
-                }
-            }
-
-            if (i == N) {
-                list.setLastSlice(true);
-            }
+            return new ParceledListSlice<ApplicationInfo>(list);
         }
-
-        return list;
-    }
-
-    @Override
-    public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags,
-            String lastRead, int userId) {
-        if (!sUserManager.exists(userId)) return null;
-        final ParceledListSlice<ApplicationInfo> list = new ParceledListSlice<ApplicationInfo>();
-        final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
-        final String[] keys;
-
-        // writer
-        synchronized (mPackages) {
-            if (listUninstalled) {
-                keys = mSettings.mPackages.keySet().toArray(new String[mSettings.mPackages.size()]);
-            } else {
-                keys = mPackages.keySet().toArray(new String[mPackages.size()]);
-            }
-
-            Arrays.sort(keys);
-            int i = getContinuationPoint(keys, lastRead);
-            final int N = keys.length;
-
-            while (i < N) {
-                final String packageName = keys[i++];
-
-                ApplicationInfo ai = null;
-                final PackageSetting ps = mSettings.mPackages.get(packageName);
-                if (listUninstalled) {
-                    if (ps != null) {
-                        ai = generateApplicationInfoFromSettingsLPw(ps.name, flags, userId);
-                    }
-                } else {
-                    final PackageParser.Package p = mPackages.get(packageName);
-                    if (p != null && ps != null) {
-                        ai = PackageParser.generateApplicationInfo(p, flags,
-                                ps.readUserState(userId), userId);
-                    }
-                }
-
-                if (ai != null && list.append(ai)) {
-                    break;
-                }
-            }
-
-            if (i == N) {
-                list.setLastSlice(true);
-            }
-        }
-
-        return list;
     }
 
     public List<ApplicationInfo> getPersistentApplications(int flags) {
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 3097811..248fbf1 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -106,6 +106,12 @@
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public String getBasePackageName() {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public ApplicationInfo getApplicationInfo() {
         throw new UnsupportedOperationException();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index e2fced6..e682da7 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -1080,6 +1080,12 @@
     }
 
     @Override
+    public String getBasePackageName() {
+        // pass
+        return null;
+    }
+
+    @Override
     public ApplicationInfo getApplicationInfo() {
         return mApplicationInfo;
     }