Home | History | Annotate | Download | only in pm
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.content.pm;
     18 
     19 import android.os.Binder;
     20 import android.os.IBinder;
     21 import android.os.Parcel;
     22 import android.os.Parcelable;
     23 import android.os.RemoteException;
     24 import android.util.Log;
     25 
     26 import java.util.ArrayList;
     27 import java.util.List;
     28 
     29 /**
     30  * Transfer a large list of Parcelable objects across an IPC.  Splits into
     31  * multiple transactions if needed.
     32  *
     33  * @hide
     34  */
     35 public class ParceledListSlice<T extends Parcelable> implements Parcelable {
     36     private static String TAG = "ParceledListSlice";
     37     private static boolean DEBUG = false;
     38 
     39     /*
     40      * TODO get this number from somewhere else. For now set it to a quarter of
     41      * the 1MB limit.
     42      */
     43     private static final int MAX_IPC_SIZE = 256 * 1024;
     44     private static final int MAX_FIRST_IPC_SIZE = MAX_IPC_SIZE / 2;
     45 
     46     private final List<T> mList;
     47 
     48     public ParceledListSlice(List<T> list) {
     49         mList = list;
     50     }
     51 
     52     private ParceledListSlice(Parcel p, ClassLoader loader) {
     53         final int N = p.readInt();
     54         mList = new ArrayList<T>(N);
     55         if (DEBUG) Log.d(TAG, "Retrieving " + N + " items");
     56         if (N <= 0) {
     57             return;
     58         }
     59         Parcelable.Creator<T> creator = p.readParcelableCreator(loader);
     60         int i = 0;
     61         while (i < N) {
     62             if (p.readInt() == 0) {
     63                 break;
     64             }
     65             mList.add(p.readCreator(creator, loader));
     66             if (DEBUG) Log.d(TAG, "Read inline #" + i + ": " + mList.get(mList.size()-1));
     67             i++;
     68         }
     69         if (i >= N) {
     70             return;
     71         }
     72         final IBinder retriever = p.readStrongBinder();
     73         while (i < N) {
     74             if (DEBUG) Log.d(TAG, "Reading more @" + i + " of " + N + ": retriever=" + retriever);
     75             Parcel data = Parcel.obtain();
     76             Parcel reply = Parcel.obtain();
     77             data.writeInt(i);
     78             try {
     79                 retriever.transact(IBinder.FIRST_CALL_TRANSACTION, data, reply, 0);
     80             } catch (RemoteException e) {
     81                 Log.w(TAG, "Failure retrieving array; only received " + i + " of " + N, e);
     82                 return;
     83             }
     84             while (i < N && reply.readInt() != 0) {
     85                 mList.add(reply.readCreator(creator, loader));
     86                 if (DEBUG) Log.d(TAG, "Read extra #" + i + ": " + mList.get(mList.size()-1));
     87                 i++;
     88             }
     89             reply.recycle();
     90             data.recycle();
     91         }
     92     }
     93 
     94     public List<T> getList() {
     95         return mList;
     96     }
     97 
     98     @Override
     99     public int describeContents() {
    100         int contents = 0;
    101         for (int i=0; i<mList.size(); i++) {
    102             contents |= mList.get(i).describeContents();
    103         }
    104         return contents;
    105     }
    106 
    107     /**
    108      * Write this to another Parcel. Note that this discards the internal Parcel
    109      * and should not be used anymore. This is so we can pass this to a Binder
    110      * where we won't have a chance to call recycle on this.
    111      */
    112     @Override
    113     public void writeToParcel(Parcel dest, int flags) {
    114         final int N = mList.size();
    115         final int callFlags = flags;
    116         dest.writeInt(N);
    117         if (DEBUG) Log.d(TAG, "Writing " + N + " items");
    118         if (N > 0) {
    119             dest.writeParcelableCreator(mList.get(0));
    120             int i = 0;
    121             while (i < N && dest.dataSize() < MAX_FIRST_IPC_SIZE) {
    122                 dest.writeInt(1);
    123                 mList.get(i).writeToParcel(dest, callFlags);
    124                 if (DEBUG) Log.d(TAG, "Wrote inline #" + i + ": " + mList.get(i));
    125                 i++;
    126             }
    127             if (i < N) {
    128                 dest.writeInt(0);
    129                 Binder retriever = new Binder() {
    130                     @Override
    131                     protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
    132                             throws RemoteException {
    133                         if (code != FIRST_CALL_TRANSACTION) {
    134                             return super.onTransact(code, data, reply, flags);
    135                         }
    136                         int i = data.readInt();
    137                         if (DEBUG) Log.d(TAG, "Writing more @" + i + " of " + N);
    138                         while (i < N && reply.dataSize() < MAX_IPC_SIZE) {
    139                             reply.writeInt(1);
    140                             mList.get(i).writeToParcel(reply, callFlags);
    141                             if (DEBUG) Log.d(TAG, "Wrote extra #" + i + ": " + mList.get(i));
    142                             i++;
    143                         }
    144                         if (i < N) {
    145                             if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N);
    146                             reply.writeInt(0);
    147                         }
    148                         return true;
    149                     }
    150                 };
    151                 if (DEBUG) Log.d(TAG, "Breaking @" + i + " of " + N + ": retriever=" + retriever);
    152                 dest.writeStrongBinder(retriever);
    153             }
    154         }
    155     }
    156 
    157     @SuppressWarnings("unchecked")
    158     public static final Parcelable.ClassLoaderCreator<ParceledListSlice> CREATOR =
    159             new Parcelable.ClassLoaderCreator<ParceledListSlice>() {
    160         public ParceledListSlice createFromParcel(Parcel in) {
    161             return new ParceledListSlice(in, null);
    162         }
    163 
    164         @Override
    165         public ParceledListSlice createFromParcel(Parcel in, ClassLoader loader) {
    166             return new ParceledListSlice(in, loader);
    167         }
    168 
    169         public ParceledListSlice[] newArray(int size) {
    170             return new ParceledListSlice[size];
    171         }
    172     };
    173 }
    174