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 if (poolPosition < 0) { 60 throw new IllegalStateException("Invalid string pool position: " + poolPosition); 61 } 62 final int startPosition = mParcel.dataPosition(); 63 64 // The pool is at the end of the parcel. 65 mParcel.setDataPosition(poolPosition); 66 mParcel.readStringList(mStrings); 67 68 // Then move back. 69 mParcel.setDataPosition(startPosition); 70 71 if (DEBUG) { 72 Log.i(TAG, "Read " + mStrings.size() + " strings"); 73 for (int i = 0; i < mStrings.size(); i++) { 74 Log.i(TAG, " " + i + ": \"" + mStrings.get(i) + "\""); 75 } 76 } 77 78 mParcel.setReadWriteHelper(this); 79 } 80 81 /** 82 * Read an string index from a parcel, and returns the corresponding string from the pool. 83 */ readString(Parcel p)84 public String readString(Parcel p) { 85 return mStrings.get(p.readInt()); 86 } 87 88 @Override readString8(Parcel p)89 public String readString8(Parcel p) { 90 return readString(p); 91 } 92 93 @Override readString16(Parcel p)94 public String readString16(Parcel p) { 95 return readString(p); 96 } 97 } 98 99 /** 100 * Parcel write helper with a string pool. 101 */ 102 public static class WriteHelper extends Parcel.ReadWriteHelper { 103 private final ArrayList<String> mStrings = new ArrayList<>(); 104 105 private final HashMap<String, Integer> mIndexes = new HashMap<>(); 106 107 private final Parcel mParcel; 108 private final int mStartPos; 109 110 /** 111 * Constructor. Prepare a parcel, and install it self as a read-write helper. 112 */ WriteHelper(Parcel p)113 public WriteHelper(Parcel p) { 114 mParcel = p; 115 mStartPos = p.dataPosition(); 116 mParcel.writeInt(0); // We come back later here and write the pool position. 117 118 mParcel.setReadWriteHelper(this); 119 } 120 121 /** 122 * Instead of writing a string directly to a parcel, this method adds it to the pool, 123 * and write the index in the pool to the parcel. 124 */ writeString(Parcel p, String s)125 public void writeString(Parcel p, String s) { 126 final Integer cur = mIndexes.get(s); 127 if (cur != null) { 128 // String already in the pool. Just write the index. 129 p.writeInt(cur); // Already in the pool. 130 if (DEBUG) { 131 Log.i(TAG, "Duplicate '" + s + "' at " + cur); 132 } 133 } else { 134 // Not in the pool. Add to the pool, and write the index. 135 final int index = mStrings.size(); 136 mIndexes.put(s, index); 137 mStrings.add(s); 138 139 if (DEBUG) { 140 Log.i(TAG, "New '" + s + "' at " + index); 141 } 142 143 p.writeInt(index); 144 } 145 } 146 147 @Override writeString8(Parcel p, String s)148 public void writeString8(Parcel p, String s) { 149 writeString(p, s); 150 } 151 152 @Override writeString16(Parcel p, String s)153 public void writeString16(Parcel p, String s) { 154 writeString(p, s); 155 } 156 157 /** 158 * Closes a parcel by appending the string pool at the end and updating the pool offset, 159 * which it assumes is at the first byte. It also uninstalls itself as a read-write helper. 160 */ finishAndUninstall()161 public void finishAndUninstall() { 162 // Uninstall first, so that writeStringList() uses the native writeString. 163 mParcel.setReadWriteHelper(null); 164 165 final int poolPosition = mParcel.dataPosition(); 166 mParcel.writeStringList(mStrings); 167 168 mParcel.setDataPosition(mStartPos); 169 mParcel.writeInt(poolPosition); 170 171 // Move back to the end. 172 mParcel.setDataPosition(mParcel.dataSize()); 173 if (DEBUG) { 174 Log.i(TAG, "Wrote " + mStrings.size() + " strings"); 175 } 176 } 177 } 178 } 179