content: /*
   * Copyright (C) 2017 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.content.pm;
  
  import android.os.Parcel;
  import android.util.Log;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  
  /**
   * Helper classes to read from and write to Parcel with pooled strings.
   *
   * @hide
   */
  public class PackageParserCacheHelper {
      private PackageParserCacheHelper() {
      }
  
      private static final String TAG = "PackageParserCacheHelper";
      private static final boolean DEBUG = false;
  
      /**
       * Parcel read helper with a string pool.
       */
      public static class ReadHelper extends Parcel.ReadWriteHelper {
          private final ArrayList<String> mStrings = new ArrayList<>();
  
          private final Parcel mParcel;
  
          public ReadHelper(Parcel p) {
              mParcel = p;
          }
  
          /**
           * Prepare to read from a parcel, and install itself as a read-write helper.
           *
           * (We don't do it in the constructor to avoid calling methods before the constructor
           * finishes.)
           */
          public void startAndInstall() {
              mStrings.clear();
  
              final int poolPosition = mParcel.readInt();
              final int startPosition = mParcel.dataPosition();
  
              // The pool is at the end of the parcel.
              mParcel.setDataPosition(poolPosition);
              mParcel.readStringList(mStrings);
  
              // Then move back.
              mParcel.setDataPosition(startPosition);
  
              if (DEBUG) {
                  Log.i(TAG, "Read " + mStrings.size() + " strings");
                  for (int i = 0; i < mStrings.size(); i++) {
                      Log.i(TAG, "  " + i + ": \"" + mStrings.get(i) + "\"");
                  }
              }
  
              mParcel.setReadWriteHelper(this);
          }
  
          /**
           * Read an string index from a parcel, and returns the corresponding string from the pool.
           */
          @Override
          public String readString(Parcel p) {
              return mStrings.get(p.readInt());
          }
      }
  
      /**
       * Parcel write helper with a string pool.
       */
      public static class WriteHelper extends Parcel.ReadWriteHelper {
          private final ArrayList<String> mStrings = new ArrayList<>();
  
          private final HashMap<String, Integer> mIndexes = new HashMap<>();
  
          private final Parcel mParcel;
          private final int mStartPos;
  
          /**
           * Constructor.  Prepare a parcel, and install it self as a read-write helper.
           */
          public WriteHelper(Parcel p) {
              mParcel = p;
              mStartPos = p.dataPosition();
              mParcel.writeInt(0); // We come back later here and write the pool position.
  
              mParcel.setReadWriteHelper(this);
          }
  
          /**
           * Instead of writing a string directly to a parcel, this method adds it to the pool,
           * and write the index in the pool to the parcel.
           */
          @Override
          public void writeString(Parcel p, String s) {
              final Integer cur = mIndexes.get(s);
              if (cur != null) {
                  // String already in the pool. Just write the index.
                  p.writeInt(cur); // Already in the pool.
                  if (DEBUG) {
                      Log.i(TAG, "Duplicate '" + s + "' at " + cur);
                  }
              } else {
                  // Not in the pool. Add to the pool, and write the index.
                  final int index = mStrings.size();
                  mIndexes.put(s, index);
                  mStrings.add(s);
  
                  if (DEBUG) {
                      Log.i(TAG, "New '" + s + "' at " + index);
                  }
  
                  p.writeInt(index);
              }
          }
  
          /**
           * Closes a parcel by appending the string pool at the end and updating the pool offset,
           * which it assumes is at the first byte.  It also uninstalls itself as a read-write helper.
           */
          public void finishAndUninstall() {
              // Uninstall first, so that writeStringList() uses the native writeString.
              mParcel.setReadWriteHelper(null);
  
              final int poolPosition = mParcel.dataPosition();
              mParcel.writeStringList(mStrings);
  
              mParcel.setDataPosition(mStartPos);
              mParcel.writeInt(poolPosition);
  
              // Move back to the end.
              mParcel.setDataPosition(mParcel.dataSize());
              if (DEBUG) {
                  Log.i(TAG, "Wrote " + mStrings.size() + " strings");
              }
          }
      }
  }
  