/*
  * Handle a heap segment series start message.
  */
 private void handleHPST(Client client, ByteBuffer data) {
   /* Clear out any data that's sitting around to
    * get ready for the chunks that are about to come.
    */
   // xxx todo: only clear data that belongs to the heap mentioned in <data>.
   client.getClientData().getVmHeapData().clearHeapData();
 }
  /*
   * Handle a heap info message.
   */
  private void handleHPIF(Client client, ByteBuffer data) {
    Log.d("ddm-heap", "HPIF!");
    try {
      int numHeaps = data.getInt();

      for (int i = 0; i < numHeaps; i++) {
        int heapId = data.getInt();
        @SuppressWarnings("unused")
        long timeStamp = data.getLong();
        @SuppressWarnings("unused")
        byte reason = data.get();
        long maxHeapSize = (long) data.getInt() & 0x00ffffffff;
        long heapSize = (long) data.getInt() & 0x00ffffffff;
        long bytesAllocated = (long) data.getInt() & 0x00ffffffff;
        long objectsAllocated = (long) data.getInt() & 0x00ffffffff;

        client
            .getClientData()
            .setHeapInfo(heapId, maxHeapSize, heapSize, bytesAllocated, objectsAllocated);
        client.update(Client.CHANGE_HEAP_DATA);
      }
    } catch (BufferUnderflowException ex) {
      Log.w("ddm-heap", "malformed HPIF chunk from client");
    }
  }
 /*
  * Handle a heap segment message.
  */
 private void handleHPSG(Client client, ByteBuffer data) {
   byte dataCopy[] = new byte[data.limit()];
   data.rewind();
   data.get(dataCopy);
   data = ByteBuffer.wrap(dataCopy);
   client.getClientData().getVmHeapData().addHeapData(data);
   // xxx todo: add to the heap mentioned in <data>
 }
 /*
  * Handle a heap segment series end message.
  */
 private void handleHPEN(Client client, ByteBuffer data) {
   /* Let the UI know that we've received all of the
    * data for this heap.
    */
   // xxx todo: only seal data that belongs to the heap mentioned in <data>.
   client.getClientData().getVmHeapData().sealHeapData();
   client.update(Client.CHANGE_HEAP_DATA);
 }
  /*
   * Handle the response from our REcent Allocation Query message.
   */
  private void handleREAQ(Client client, ByteBuffer data) {
    boolean enabled;

    enabled = (data.get() != 0);
    Log.d("ddm-heap", "REAQ says: enabled=" + enabled);

    client
        .getClientData()
        .setAllocationStatus(enabled ? AllocationTrackingStatus.ON : AllocationTrackingStatus.OFF);
    client.update(Client.CHANGE_HEAP_ALLOCATION_STATUS);
  }
  /**
   * Sends an HPDU request to the client.
   *
   * <p>We will get an HPDU response when the heap dump has completed. On failure we get a generic
   * failure response.
   *
   * @param fileName name of output file (on device)
   */
  public static void sendHPDU(Client client, String fileName) throws IOException {
    ByteBuffer rawBuf = allocBuffer(4 + fileName.length() * 2);
    JdwpPacket packet = new JdwpPacket(rawBuf);
    ByteBuffer buf = getChunkDataBuf(rawBuf);

    buf.putInt(fileName.length());
    putString(buf, fileName);

    finishChunkPacket(packet, CHUNK_HPDU, buf.position());
    Log.d("ddm-heap", "Sending " + name(CHUNK_HPDU) + " '" + fileName + "'");
    client.sendAndConsume(packet, mInst);
    client.getClientData().setPendingHprofDump(fileName);
  }
  /*
   * Handle notification of completion of a HeaP DUmp.
   */
  private void handleHPDU(Client client, ByteBuffer data) {
    byte result;

    // get the filename and make the client not have pending HPROF dump anymore.
    String filename = client.getClientData().getPendingHprofDump();
    client.getClientData().setPendingHprofDump(null);

    // get the dump result
    result = data.get();

    // get the app-level handler for HPROF dump
    IHprofDumpHandler handler = ClientData.getHprofDumpHandler();
    if (handler != null) {
      if (result == 0) {
        handler.onSuccess(filename, client);

        Log.d("ddm-heap", "Heap dump request has finished");
      } else {
        handler.onEndFailure(client, null);
        Log.w("ddm-heap", "Heap dump request failed (check device log)");
      }
    }
  }
  /*
   * Handle a REcent ALlocation response.
   *
   * Message header (all values big-endian):
   *   (1b) message header len (to allow future expansion); includes itself
   *   (1b) entry header len
   *   (1b) stack frame len
   *   (2b) number of entries
   *   (4b) offset to string table from start of message
   *   (2b) number of class name strings
   *   (2b) number of method name strings
   *   (2b) number of source file name strings
   *   For each entry:
   *     (4b) total allocation size
   *     (2b) threadId
   *     (2b) allocated object's class name index
   *     (1b) stack depth
   *     For each stack frame:
   *       (2b) method's class name
   *       (2b) method name
   *       (2b) method source file
   *       (2b) line number, clipped to 32767; -2 if native; -1 if no source
   *   (xb) class name strings
   *   (xb) method name strings
   *   (xb) source file strings
   *
   *   As with other DDM traffic, strings are sent as a 4-byte length
   *   followed by UTF-16 data.
   */
  private void handleREAL(Client client, ByteBuffer data) {
    Log.e("ddm-heap", "*** Received " + name(CHUNK_REAL));
    int messageHdrLen, entryHdrLen, stackFrameLen;
    int numEntries, offsetToStrings;
    int numClassNames, numMethodNames, numFileNames;

    /*
     * Read the header.
     */
    messageHdrLen = (data.get() & 0xff);
    entryHdrLen = (data.get() & 0xff);
    stackFrameLen = (data.get() & 0xff);
    numEntries = (data.getShort() & 0xffff);
    offsetToStrings = data.getInt();
    numClassNames = (data.getShort() & 0xffff);
    numMethodNames = (data.getShort() & 0xffff);
    numFileNames = (data.getShort() & 0xffff);

    /*
     * Skip forward to the strings and read them.
     */
    data.position(offsetToStrings);

    String[] classNames = new String[numClassNames];
    String[] methodNames = new String[numMethodNames];
    String[] fileNames = new String[numFileNames];

    readStringTable(data, classNames);
    readStringTable(data, methodNames);
    // System.out.println("METHODS: "
    //    + java.util.Arrays.deepToString(methodNames));
    readStringTable(data, fileNames);

    /*
     * Skip back to a point just past the header and start reading
     * entries.
     */
    data.position(messageHdrLen);

    ArrayList<AllocationInfo> list = new ArrayList<AllocationInfo>(numEntries);
    for (int i = 0; i < numEntries; i++) {
      int totalSize;
      int threadId, classNameIndex, stackDepth;

      totalSize = data.getInt();
      threadId = (data.getShort() & 0xffff);
      classNameIndex = (data.getShort() & 0xffff);
      stackDepth = (data.get() & 0xff);
      /* we've consumed 9 bytes; gobble up any extra */
      for (int skip = 9; skip < entryHdrLen; skip++) data.get();

      StackTraceElement[] steArray = new StackTraceElement[stackDepth];

      /*
       * Pull out the stack trace.
       */
      for (int sti = 0; sti < stackDepth; sti++) {
        int methodClassNameIndex, methodNameIndex;
        int methodSourceFileIndex;
        short lineNumber;
        String methodClassName, methodName, methodSourceFile;

        methodClassNameIndex = (data.getShort() & 0xffff);
        methodNameIndex = (data.getShort() & 0xffff);
        methodSourceFileIndex = (data.getShort() & 0xffff);
        lineNumber = data.getShort();

        methodClassName = classNames[methodClassNameIndex];
        methodName = methodNames[methodNameIndex];
        methodSourceFile = fileNames[methodSourceFileIndex];

        steArray[sti] =
            new StackTraceElement(methodClassName, methodName, methodSourceFile, lineNumber);

        /* we've consumed 8 bytes; gobble up any extra */
        for (int skip = 9; skip < stackFrameLen; skip++) data.get();
      }

      list.add(
          new AllocationInfo(classNames[classNameIndex], totalSize, (short) threadId, steArray));
    }

    // sort biggest allocations first.
    Collections.sort(list);

    client.getClientData().setAllocations(list.toArray(new AllocationInfo[numEntries]));
    client.update(Client.CHANGE_HEAP_ALLOCATIONS);
  }