Home | History | Annotate | Download | only in pm
      1 /*
      2  * Copyright (C) 2017 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.Parcel;
     20 import android.util.Log;
     21 
     22 import java.util.ArrayList;
     23 import java.util.HashMap;
     24 
     25 /**
     26  * Helper classes to read from and write to Parcel with pooled strings.
     27  *
     28  * @hide
     29  */
     30 public class PackageParserCacheHelper {
     31     private PackageParserCacheHelper() {
     32     }
     33 
     34     private static final String TAG = "PackageParserCacheHelper";
     35     private static final boolean DEBUG = false;
     36 
     37     /**
     38      * Parcel read helper with a string pool.
     39      */
     40     public static class ReadHelper extends Parcel.ReadWriteHelper {
     41         private final ArrayList<String> mStrings = new ArrayList<>();
     42 
     43         private final Parcel mParcel;
     44 
     45         public ReadHelper(Parcel p) {
     46             mParcel = p;
     47         }
     48 
     49         /**
     50          * Prepare to read from a parcel, and install itself as a read-write helper.
     51          *
     52          * (We don't do it in the constructor to avoid calling methods before the constructor
     53          * finishes.)
     54          */
     55         public void startAndInstall() {
     56             mStrings.clear();
     57 
     58             final int poolPosition = mParcel.readInt();
     59             final int startPosition = mParcel.dataPosition();
     60 
     61             // The pool is at the end of the parcel.
     62             mParcel.setDataPosition(poolPosition);
     63             mParcel.readStringList(mStrings);
     64 
     65             // Then move back.
     66             mParcel.setDataPosition(startPosition);
     67 
     68             if (DEBUG) {
     69                 Log.i(TAG, "Read " + mStrings.size() + " strings");
     70                 for (int i = 0; i < mStrings.size(); i++) {
     71                     Log.i(TAG, "  " + i + ": \"" + mStrings.get(i) + "\"");
     72                 }
     73             }
     74 
     75             mParcel.setReadWriteHelper(this);
     76         }
     77 
     78         /**
     79          * Read an string index from a parcel, and returns the corresponding string from the pool.
     80          */
     81         @Override
     82         public String readString(Parcel p) {
     83             return mStrings.get(p.readInt());
     84         }
     85     }
     86 
     87     /**
     88      * Parcel write helper with a string pool.
     89      */
     90     public static class WriteHelper extends Parcel.ReadWriteHelper {
     91         private final ArrayList<String> mStrings = new ArrayList<>();
     92 
     93         private final HashMap<String, Integer> mIndexes = new HashMap<>();
     94 
     95         private final Parcel mParcel;
     96         private final int mStartPos;
     97 
     98         /**
     99          * Constructor.  Prepare a parcel, and install it self as a read-write helper.
    100          */
    101         public WriteHelper(Parcel p) {
    102             mParcel = p;
    103             mStartPos = p.dataPosition();
    104             mParcel.writeInt(0); // We come back later here and write the pool position.
    105 
    106             mParcel.setReadWriteHelper(this);
    107         }
    108 
    109         /**
    110          * Instead of writing a string directly to a parcel, this method adds it to the pool,
    111          * and write the index in the pool to the parcel.
    112          */
    113         @Override
    114         public void writeString(Parcel p, String s) {
    115             final Integer cur = mIndexes.get(s);
    116             if (cur != null) {
    117                 // String already in the pool. Just write the index.
    118                 p.writeInt(cur); // Already in the pool.
    119                 if (DEBUG) {
    120                     Log.i(TAG, "Duplicate '" + s + "' at " + cur);
    121                 }
    122             } else {
    123                 // Not in the pool. Add to the pool, and write the index.
    124                 final int index = mStrings.size();
    125                 mIndexes.put(s, index);
    126                 mStrings.add(s);
    127 
    128                 if (DEBUG) {
    129                     Log.i(TAG, "New '" + s + "' at " + index);
    130                 }
    131 
    132                 p.writeInt(index);
    133             }
    134         }
    135 
    136         /**
    137          * Closes a parcel by appending the string pool at the end and updating the pool offset,
    138          * which it assumes is at the first byte.  It also uninstalls itself as a read-write helper.
    139          */
    140         public void finishAndUninstall() {
    141             // Uninstall first, so that writeStringList() uses the native writeString.
    142             mParcel.setReadWriteHelper(null);
    143 
    144             final int poolPosition = mParcel.dataPosition();
    145             mParcel.writeStringList(mStrings);
    146 
    147             mParcel.setDataPosition(mStartPos);
    148             mParcel.writeInt(poolPosition);
    149 
    150             // Move back to the end.
    151             mParcel.setDataPosition(mParcel.dataSize());
    152             if (DEBUG) {
    153                 Log.i(TAG, "Wrote " + mStrings.size() + " strings");
    154             }
    155         }
    156     }
    157 }
    158