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 { 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 ReadHelper(Parcel p)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 */ startAndInstall()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 readString(Parcel p)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 */ WriteHelper(Parcel p)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 writeString(Parcel p, String s)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 */ finishAndUninstall()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