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.os;
  
  import android.util.Slog;
  
  import java.util.ArrayList;
  import java.util.List;
  
  /**
   * Wrapper class for sending data from Android OS to StatsD.
   *
   * @hide
   */
  public final class StatsLogEventWrapper implements Parcelable {
      static final boolean DEBUG = false;
      static final String TAG = "StatsLogEventWrapper";
  
      // Keep in sync with FieldValue.h enums
      private static final int EVENT_TYPE_UNKNOWN = 0;
      private static final int EVENT_TYPE_INT = 1; /* int32_t */
      private static final int EVENT_TYPE_LONG = 2; /* int64_t */
      private static final int EVENT_TYPE_FLOAT = 3;
      private static final int EVENT_TYPE_DOUBLE = 4;
      private static final int EVENT_TYPE_STRING = 5;
      private static final int EVENT_TYPE_STORAGE = 6;
  
      List<Integer> mTypes = new ArrayList<>();
      List<Object> mValues = new ArrayList<>();
      int mTag;
      long mElapsedTimeNs;
      long mWallClockTimeNs;
      WorkSource mWorkSource = null;
  
      public StatsLogEventWrapper(int tag, long elapsedTimeNs, long wallClockTimeNs) {
          this.mTag = tag;
          this.mElapsedTimeNs = elapsedTimeNs;
          this.mWallClockTimeNs = wallClockTimeNs;
      }
  
      /**
       * Boilerplate for Parcel.
       */
      public static final @android.annotation.NonNull Parcelable.Creator<StatsLogEventWrapper> CREATOR = new
              Parcelable.Creator<StatsLogEventWrapper>() {
                  public StatsLogEventWrapper createFromParcel(Parcel in) {
                      return new StatsLogEventWrapper(in);
                  }
  
                  public StatsLogEventWrapper[] newArray(int size) {
                      return new StatsLogEventWrapper[size];
                  }
              };
  
      private StatsLogEventWrapper(Parcel in) {
          readFromParcel(in);
      }
  
      /**
       * Set work source if any.
       */
      public void setWorkSource(WorkSource ws) {
          if (ws.getWorkChains() == null || ws.getWorkChains().size() == 0) {
              Slog.w(TAG, "Empty worksource!");
              return;
          }
          mWorkSource = ws;
      }
  
      /**
       * Write a int value.
       */
      public void writeInt(int val) {
          mTypes.add(EVENT_TYPE_INT);
          mValues.add(val);
      }
  
      /**
       * Write a long value.
       */
      public void writeLong(long val) {
          mTypes.add(EVENT_TYPE_LONG);
          mValues.add(val);
      }
  
      /**
       * Write a string value.
       */
      public void writeString(String val) {
          mTypes.add(EVENT_TYPE_STRING);
          // use empty string for null
          mValues.add(val == null ? "" : val);
      }
  
      /**
       * Write a float value.
       */
      public void writeFloat(float val) {
          mTypes.add(EVENT_TYPE_FLOAT);
          mValues.add(val);
      }
  
      /**
       * Write a storage value.
       */
      public void writeStorage(byte[] val) {
          mTypes.add(EVENT_TYPE_STORAGE);
          mValues.add(val);
      }
  
      /**
       * Write a boolean value.
       */
      public void writeBoolean(boolean val) {
          mTypes.add(EVENT_TYPE_INT);
          mValues.add(val ? 1 : 0);
      }
  
      public void writeToParcel(Parcel out, int flags) {
          if (DEBUG) {
              Slog.d(TAG,
                      "Writing " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs + " and "
                              + mTypes.size() + " elements.");
          }
          out.writeInt(mTag);
          out.writeLong(mElapsedTimeNs);
          out.writeLong(mWallClockTimeNs);
          if (mWorkSource != null) {
              ArrayList<android.os.WorkSource.WorkChain> workChains = mWorkSource.getWorkChains();
              // number of chains
              out.writeInt(workChains.size());
              for (int i = 0; i < workChains.size(); i++) {
                  android.os.WorkSource.WorkChain wc = workChains.get(i);
                  if (wc.getSize() == 0) {
                      Slog.w(TAG, "Empty work chain.");
                      out.writeInt(0);
                      continue;
                  }
                  if (wc.getUids().length != wc.getTags().length
                          || wc.getUids().length != wc.getSize()) {
                      Slog.w(TAG, "Malformated work chain.");
                      out.writeInt(0);
                      continue;
                  }
                  // number of nodes
                  out.writeInt(wc.getSize());
                  for (int j = 0; j < wc.getSize(); j++) {
                      out.writeInt(wc.getUids()[j]);
                      out.writeString(wc.getTags()[j] == null ? "" : wc.getTags()[j]);
                  }
              }
          } else {
              // no chains
              out.writeInt(0);
          }
          out.writeInt(mTypes.size());
          for (int i = 0; i < mTypes.size(); i++) {
              out.writeInt(mTypes.get(i));
              switch (mTypes.get(i)) {
                  case EVENT_TYPE_INT:
                      out.writeInt((int) mValues.get(i));
                      break;
                  case EVENT_TYPE_LONG:
                      out.writeLong((long) mValues.get(i));
                      break;
                  case EVENT_TYPE_FLOAT:
                      out.writeFloat((float) mValues.get(i));
                      break;
                  case EVENT_TYPE_DOUBLE:
                      out.writeDouble((double) mValues.get(i));
                      break;
                  case EVENT_TYPE_STRING:
                      out.writeString((String) mValues.get(i));
                      break;
                  case EVENT_TYPE_STORAGE:
                      out.writeByteArray((byte[]) mValues.get(i));
                      break;
                  default:
                      break;
              }
          }
      }
  
      /**
       * Reads from parcel and appropriately fills member fields.
       */
      public void readFromParcel(Parcel in) {
          mTypes = new ArrayList<>();
          mValues = new ArrayList<>();
          mWorkSource = null;
  
          mTag = in.readInt();
          mElapsedTimeNs = in.readLong();
          mWallClockTimeNs = in.readLong();
  
          // Clear any data.
          if (DEBUG) {
              Slog.d(TAG, "Reading " + mTag + " " + mElapsedTimeNs + " " + mWallClockTimeNs);
          }
          // Set up worksource if present.
          int numWorkChains = in.readInt();
          if (numWorkChains > 0) {
              mWorkSource = new WorkSource();
              for (int i = 0; i < numWorkChains; i++) {
                  android.os.WorkSource.WorkChain workChain = mWorkSource.createWorkChain();
                  int workChainSize = in.readInt();
                  for (int j = 0; j < workChainSize; j++) {
                      int uid = in.readInt();
                      String tag = in.readString();
                      workChain.addNode(uid, tag);
                  }
              }
          }
  
          // Do the rest of the types.
          int numTypes = in.readInt();
          if (DEBUG) {
              Slog.d(TAG, "Reading " + numTypes + " elements");
          }
          for (int i = 0; i < numTypes; i++) {
              int type = in.readInt();
              mTypes.add(type);
              switch (type) {
                  case EVENT_TYPE_INT:
                      mValues.add(in.readInt());
                      break;
                  case EVENT_TYPE_LONG:
                      mValues.add(in.readLong());
                      break;
                  case EVENT_TYPE_FLOAT:
                      mValues.add(in.readFloat());
                      break;
                  case EVENT_TYPE_DOUBLE:
                      mValues.add(in.readDouble());
                      break;
                  case EVENT_TYPE_STRING:
                      mValues.add(in.readString());
                      break;
                  case EVENT_TYPE_STORAGE:
                      mValues.add(in.createByteArray());
                      break;
                  default:
                      break;
              }
          }
      }
  
      /**
       * Boilerplate for Parcel.
       */
      public int describeContents() {
          return 0;
      }
  }
  