DDMS now queries the VM for its features.

This is a first step to support method profiling on/off and hprof dump from
DDMS.

Change-Id: Id95767b458a8405a31bcbe295bb969597f0e6e03
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java
index 64fbef6..c8e5498 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/Client.java
@@ -604,7 +604,7 @@
                         "Good handshake from client, sending HELO to " + mClientData.getPid());
                     JdwpPacket.consumeHandshake(mReadBuffer);
                     mConnState = ST_NEED_DDM_PKT;
-                    HandleHello.sendHELO(this, SERVER_PROTOCOL_VERSION);
+                    HandleHello.sendHelloCommands(this, SERVER_PROTOCOL_VERSION);
                     // see if we have another packet in the buffer
                     return getJdwpPacket();
                 case JdwpPacket.HANDSHAKE_BAD:
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java
index 2b46b6f..d396d15 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/ClientData.java
@@ -24,6 +24,7 @@
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -46,7 +47,7 @@
     * access should be synchronized against the ClientData object.
     */
 
-    
+
     /** Temporary name of VM to be ignored. */
     private final static String PRE_INITIALIZED = "<pre-initialized>"; //$NON-NLS-1$
 
@@ -62,7 +63,7 @@
     /** Debugger connection status: The listening port for debugger connection failed to listen.
      * No debugger will be able to connect. */
     public static final int DEBUGGER_ERROR = 4;
-    
+
     /**
      * Allocation tracking status: unknown.
      * <p/>This happens right after a {@link Client} is discovered
@@ -99,6 +100,18 @@
      */
     public final static String HEAP_OBJECTS_ALLOCATED = "objectsAllocated"; // $NON-NLS-1$
 
+    /**
+     * String for feature enabling starting/stopping method profiling
+     * @see #hasFeature(String)
+     */
+    public final static String FEATURE_PROFILING = "method-trace-profiling"; // $NON-NLS-1$
+
+    /**
+     * String for feature allowing to dump hprof files
+     * @see #hasFeature(String)
+     */
+    public final static String FEATURE_HPROF = "hprof-heap-dump"; // $NON-NLS-1$
+
     // is this a DDM-aware client?
     private boolean mIsDdmAware;
 
@@ -114,6 +127,9 @@
     // how interested are we in a debugger?
     private int mDebuggerInterest;
 
+    // List of supported feature by the client.
+    private final HashSet<String> mFeatures = new HashSet<String>();
+
     // Thread tracking (THCR, THDE).
     private TreeMap<Integer,ThreadInfo> mThreadMap;
 
@@ -232,11 +248,11 @@
         public void setProcessedHeapMap(Map<Integer, ArrayList<HeapSegmentElement>> heapMap) {
             mProcessedHeapMap = heapMap;
         }
-        
+
         public Map<Integer, ArrayList<HeapSegmentElement>> getProcessedHeapMap() {
             return mProcessedHeapMap;
         }
-        
+
 
     }
 
@@ -250,7 +266,7 @@
         mDebuggerInterest = DEBUGGER_DEFAULT;
         mThreadMap = new TreeMap<Integer,ThreadInfo>();
     }
-    
+
     /**
      * Returns whether the process is DDM-aware.
      */
@@ -290,7 +306,7 @@
      * Returns the client description.
      * <p/>This is generally the name of the package defined in the
      * <code>AndroidManifest.xml</code>.
-     * 
+     *
      * @return the client description or <code>null</code> if not the description was not yet
      * sent by the client.
      */
@@ -319,7 +335,7 @@
             }
         }
     }
-    
+
     /**
      * Returns the debugger connection status. Possible values are {@link #DEBUGGER_DEFAULT},
      * {@link #DEBUGGER_WAITING}, {@link #DEBUGGER_ATTACHED}, and {@link #DEBUGGER_ERROR}.
@@ -422,7 +438,7 @@
     synchronized ThreadInfo getThread(int threadId) {
         return mThreadMap.get(threadId);
     }
-    
+
     synchronized void clearThreads() {
         mThreadMap.clear();
     }
@@ -474,7 +490,7 @@
     public synchronized Iterator<NativeLibraryMapInfo> getNativeLibraryMapInfo() {
         return mNativeLibMapInfo.iterator();
     }
-    
+
     synchronized void setAllocationStatus(boolean enabled) {
         mAllocationStatus = enabled ? ALLOCATION_TRACKING_ON : ALLOCATION_TRACKING_OFF;
     }
@@ -486,11 +502,11 @@
     public synchronized int getAllocationStatus() {
         return mAllocationStatus;
     }
-    
+
     synchronized void setAllocations(AllocationInfo[] allocs) {
         mAllocations = allocs;
     }
-    
+
     /**
      * Returns the list of tracked allocations.
      * @see Client#requestAllocationDetails()
@@ -498,5 +514,21 @@
     public synchronized AllocationInfo[] getAllocations() {
         return mAllocations;
     }
+
+    void addFeature(String feature) {
+        mFeatures.add(feature);
+    }
+
+    /**
+     * Returns true if the {@link Client} supports the given <var>feature</var>
+     * @param feature The feature to test.
+     * @return true if the feature is supported
+     *
+     * @see ClientData#FEATURE_PROFILING
+     * @see ClientData#FEATURE_HPROF
+     */
+    public boolean hasFeature(String feature) {
+        return mFeatures.contains(feature);
+    }
 }
 
diff --git a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java
index fb9697c..4818bd0 100644
--- a/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java
+++ b/tools/ddms/libs/ddmlib/src/com/android/ddmlib/HandleHello.java
@@ -29,7 +29,6 @@
 
     private static final HandleHello mInst = new HandleHello();
 
-
     private HandleHello() {}
 
     /**
@@ -56,6 +55,18 @@
     }
 
     /**
+     * Sends HELLO-type commands to the VM after a good handshake.
+     * @param client
+     * @param serverProtocolVersion
+     * @throws IOException
+     */
+    public static void sendHelloCommands(Client client, int serverProtocolVersion)
+            throws IOException {
+        sendHELO(client, serverProtocolVersion);
+        sendFEAT(client);
+    }
+
+    /**
      * Chunk handler entry point.
      */
     @Override
@@ -87,12 +98,12 @@
 
         vmIdent = getString(data, vmIdentLen);
         appName = getString(data, appNameLen);
-        
+
         Log.d("ddm-hello", "HELO: v=" + version + ", pid=" + pid
             + ", vm='" + vmIdent + "', app='" + appName + "'");
 
         ClientData cd = client.getClientData();
-        
+
         synchronized (cd) {
             if (cd.getPid() == pid) {
                 cd.setVmIdentifier(vmIdent);
@@ -141,6 +152,7 @@
         for (i = 0; i < featureCount; i++) {
             int len = data.getInt();
             String feature = getString(data, len);
+            client.getClientData().addFeature(feature);
 
             Log.d("ddm-hello", "Feature: " + feature);
         }
@@ -160,6 +172,5 @@
         Log.d("ddm-heap", "Sending " + name(CHUNK_FEAT));
         client.sendAndConsume(packet, mInst);
     }
-
 }
 
diff --git a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java
index 72cbb4a..35e071d 100644
--- a/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java
+++ b/tools/ddms/libs/ddmuilib/src/com/android/ddmuilib/InfoPanel.java
@@ -39,11 +39,15 @@
         "App description:",
         "VM version:",
         "Process ID:",
+        "Supports Profiling Control:",
+        "Supports HPROF Control:",
     };
-    private static final int ENT_DDM_AWARE = 0;
-    private static final int ENT_APP_DESCR = 1;
-    private static final int ENT_VM_VERSION = 2;
-    private static final int ENT_PROCESS_ID = 3;
+    private static final int ENT_DDM_AWARE          = 0;
+    private static final int ENT_APP_DESCR          = 1;
+    private static final int ENT_VM_VERSION         = 2;
+    private static final int ENT_PROCESS_ID         = 3;
+    private static final int ENT_SUPPORTS_PROFILING = 4;
+    private static final int ENT_SUPPORTS_HPROF     = 5;
 
     /**
      * Create our control(s).
@@ -71,7 +75,7 @@
 
         return mTable;
     }
-    
+
     /**
      * Sets the focus to the proper control inside the panel.
      */
@@ -160,6 +164,10 @@
             item.setText(1, isDdmAware);
             item = mTable.getItem(ENT_PROCESS_ID);
             item.setText(1, pid);
+            item = mTable.getItem(ENT_SUPPORTS_PROFILING);
+            item.setText(1, Boolean.toString(cd.hasFeature(ClientData.FEATURE_PROFILING)));
+            item = mTable.getItem(ENT_SUPPORTS_HPROF);
+            item.setText(1, Boolean.toString(cd.hasFeature(ClientData.FEATURE_HPROF)));
         }
 
         mCol2.pack();