Home | History | Annotate | Download | only in ddmlib
      1 /*
      2  * Copyright (C) 2007 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 com.android.ddmlib;
     18 
     19 import com.android.ddmlib.ClientData.MethodProfilingStatus;
     20 import com.android.ddmlib.DebugPortManager.IDebugPortProvider;
     21 import com.android.ddmlib.AndroidDebugBridge.IClientChangeListener;
     22 
     23 import java.io.IOException;
     24 import java.nio.BufferOverflowException;
     25 import java.nio.ByteBuffer;
     26 import java.nio.channels.SelectionKey;
     27 import java.nio.channels.Selector;
     28 import java.nio.channels.SocketChannel;
     29 import java.util.HashMap;
     30 
     31 /**
     32  * This represents a single client, usually a DAlvik VM process.
     33  * <p/>This class gives access to basic client information, as well as methods to perform actions
     34  * on the client.
     35  * <p/>More detailed information, usually updated in real time, can be access through the
     36  * {@link ClientData} class. Each <code>Client</code> object has its own <code>ClientData</code>
     37  * accessed through {@link #getClientData()}.
     38  */
     39 public class Client {
     40 
     41     private static final int SERVER_PROTOCOL_VERSION = 1;
     42 
     43     /** Client change bit mask: application name change */
     44     public static final int CHANGE_NAME                       = 0x0001;
     45     /** Client change bit mask: debugger status change */
     46     public static final int CHANGE_DEBUGGER_STATUS            = 0x0002;
     47     /** Client change bit mask: debugger port change */
     48     public static final int CHANGE_PORT                       = 0x0004;
     49     /** Client change bit mask: thread update flag change */
     50     public static final int CHANGE_THREAD_MODE                = 0x0008;
     51     /** Client change bit mask: thread data updated */
     52     public static final int CHANGE_THREAD_DATA                = 0x0010;
     53     /** Client change bit mask: heap update flag change */
     54     public static final int CHANGE_HEAP_MODE                  = 0x0020;
     55     /** Client change bit mask: head data updated */
     56     public static final int CHANGE_HEAP_DATA                  = 0x0040;
     57     /** Client change bit mask: native heap data updated */
     58     public static final int CHANGE_NATIVE_HEAP_DATA           = 0x0080;
     59     /** Client change bit mask: thread stack trace updated */
     60     public static final int CHANGE_THREAD_STACKTRACE          = 0x0100;
     61     /** Client change bit mask: allocation information updated */
     62     public static final int CHANGE_HEAP_ALLOCATIONS           = 0x0200;
     63     /** Client change bit mask: allocation information updated */
     64     public static final int CHANGE_HEAP_ALLOCATION_STATUS     = 0x0400;
     65     /** Client change bit mask: allocation information updated */
     66     public static final int CHANGE_METHOD_PROFILING_STATUS    = 0x0800;
     67 
     68     /** Client change bit mask: combination of {@link Client#CHANGE_NAME},
     69      * {@link Client#CHANGE_DEBUGGER_STATUS}, and {@link Client#CHANGE_PORT}.
     70      */
     71     public static final int CHANGE_INFO = CHANGE_NAME | CHANGE_DEBUGGER_STATUS | CHANGE_PORT;
     72 
     73     private SocketChannel mChan;
     74 
     75     // debugger we're associated with, if any
     76     private Debugger mDebugger;
     77     private int mDebuggerListenPort;
     78 
     79     // list of IDs for requests we have sent to the client
     80     private HashMap<Integer,ChunkHandler> mOutstandingReqs;
     81 
     82     // chunk handlers stash state data in here
     83     private ClientData mClientData;
     84 
     85     // User interface state.  Changing the value causes a message to be
     86     // sent to the client.
     87     private boolean mThreadUpdateEnabled;
     88     private boolean mHeapUpdateEnabled;
     89 
     90     /*
     91      * Read/write buffers.  We can get large quantities of data from the
     92      * client, e.g. the response to a "give me the list of all known classes"
     93      * request from the debugger.  Requests from the debugger, and from us,
     94      * are much smaller.
     95      *
     96      * Pass-through debugger traffic is sent without copying.  "mWriteBuffer"
     97      * is only used for data generated within Client.
     98      */
     99     private static final int INITIAL_BUF_SIZE = 2*1024;
    100     private static final int MAX_BUF_SIZE = 200*1024*1024;
    101     private ByteBuffer mReadBuffer;
    102 
    103     private static final int WRITE_BUF_SIZE = 256;
    104     private ByteBuffer mWriteBuffer;
    105 
    106     private Device mDevice;
    107 
    108     private int mConnState;
    109 
    110     private static final int ST_INIT         = 1;
    111     private static final int ST_NOT_JDWP     = 2;
    112     private static final int ST_AWAIT_SHAKE  = 10;
    113     private static final int ST_NEED_DDM_PKT = 11;
    114     private static final int ST_NOT_DDM      = 12;
    115     private static final int ST_READY        = 13;
    116     private static final int ST_ERROR        = 20;
    117     private static final int ST_DISCONNECTED = 21;
    118 
    119 
    120     /**
    121      * Create an object for a new client connection.
    122      *
    123      * @param device the device this client belongs to
    124      * @param chan the connected {@link SocketChannel}.
    125      * @param pid the client pid.
    126      */
    127     Client(Device device, SocketChannel chan, int pid) {
    128         mDevice = device;
    129         mChan = chan;
    130 
    131         mReadBuffer = ByteBuffer.allocate(INITIAL_BUF_SIZE);
    132         mWriteBuffer = ByteBuffer.allocate(WRITE_BUF_SIZE);
    133 
    134         mOutstandingReqs = new HashMap<Integer,ChunkHandler>();
    135 
    136         mConnState = ST_INIT;
    137 
    138         mClientData = new ClientData(pid);
    139 
    140         mThreadUpdateEnabled = DdmPreferences.getInitialThreadUpdate();
    141         mHeapUpdateEnabled = DdmPreferences.getInitialHeapUpdate();
    142     }
    143 
    144     /**
    145      * Returns a string representation of the {@link Client} object.
    146      */
    147     @Override
    148     public String toString() {
    149         return "[Client pid: " + mClientData.getPid() + "]";
    150     }
    151 
    152     /**
    153      * Returns the {@link IDevice} on which this Client is running.
    154      */
    155     public IDevice getDevice() {
    156         return mDevice;
    157     }
    158 
    159     /** Returns the {@link Device} on which this Client is running.
    160      */
    161     Device getDeviceImpl() {
    162         return mDevice;
    163     }
    164 
    165     /**
    166      * Returns the debugger port for this client.
    167      */
    168     public int getDebuggerListenPort() {
    169         return mDebuggerListenPort;
    170     }
    171 
    172     /**
    173      * Returns <code>true</code> if the client VM is DDM-aware.
    174      *
    175      * Calling here is only allowed after the connection has been
    176      * established.
    177      */
    178     public boolean isDdmAware() {
    179         switch (mConnState) {
    180             case ST_INIT:
    181             case ST_NOT_JDWP:
    182             case ST_AWAIT_SHAKE:
    183             case ST_NEED_DDM_PKT:
    184             case ST_NOT_DDM:
    185             case ST_ERROR:
    186             case ST_DISCONNECTED:
    187                 return false;
    188             case ST_READY:
    189                 return true;
    190             default:
    191                 assert false;
    192                 return false;
    193         }
    194     }
    195 
    196     /**
    197      * Returns <code>true</code> if a debugger is currently attached to the client.
    198      */
    199     public boolean isDebuggerAttached() {
    200         return mDebugger.isDebuggerAttached();
    201     }
    202 
    203     /**
    204      * Return the Debugger object associated with this client.
    205      */
    206     Debugger getDebugger() {
    207         return mDebugger;
    208     }
    209 
    210     /**
    211      * Returns the {@link ClientData} object containing this client information.
    212      */
    213     public ClientData getClientData() {
    214         return mClientData;
    215     }
    216 
    217     /**
    218      * Forces the client to execute its garbage collector.
    219      */
    220     public void executeGarbageCollector() {
    221         try {
    222             HandleHeap.sendHPGC(this);
    223         } catch (IOException ioe) {
    224             Log.w("ddms", "Send of HPGC message failed");
    225             // ignore
    226         }
    227     }
    228 
    229     /**
    230      * Makes the VM dump an HPROF file
    231      */
    232     public void dumpHprof() {
    233         boolean canStream = mClientData.hasFeature(ClientData.FEATURE_HPROF_STREAMING);
    234         try {
    235             if (canStream) {
    236                 HandleHeap.sendHPDS(this);
    237             } else {
    238                 String file = "/sdcard/" + mClientData.getClientDescription().replaceAll(
    239                         "\\:.*", "") + ".hprof";
    240                 HandleHeap.sendHPDU(this, file);
    241             }
    242         } catch (IOException e) {
    243             Log.w("ddms", "Send of HPDU message failed");
    244             // ignore
    245         }
    246     }
    247 
    248     public void toggleMethodProfiling() {
    249         boolean canStream = mClientData.hasFeature(ClientData.FEATURE_PROFILING_STREAMING);
    250         try {
    251             if (mClientData.getMethodProfilingStatus() == MethodProfilingStatus.ON) {
    252                 if (canStream) {
    253                     HandleProfiling.sendMPSE(this);
    254                 } else {
    255                     HandleProfiling.sendMPRE(this);
    256                 }
    257             } else {
    258                 if (canStream) {
    259                     HandleProfiling.sendMPSS(this, 8*1024*1024, 0 /*flags*/);
    260                 } else {
    261                     String file = "/sdcard/" + mClientData.getClientDescription().replaceAll("\\:.*", "") +
    262                     ".trace";
    263                     HandleProfiling.sendMPRS(this, file, 8*1024*1024, 0 /*flags*/);
    264                 }
    265             }
    266         } catch (IOException e) {
    267             Log.w("ddms", "Toggle method profiling failed");
    268             // ignore
    269         }
    270     }
    271 
    272     /**
    273      * Sends a request to the VM to send the enable status of the method profiling.
    274      * This is asynchronous.
    275      * <p/>The allocation status can be accessed by {@link ClientData#getAllocationStatus()}.
    276      * The notification that the new status is available will be received through
    277      * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code>
    278      * containing the mask {@link #CHANGE_HEAP_ALLOCATION_STATUS}.
    279      */
    280     public void requestMethodProfilingStatus() {
    281         try {
    282             HandleHeap.sendREAQ(this);
    283         } catch (IOException e) {
    284             Log.e("ddmlib", e);
    285         }
    286     }
    287 
    288 
    289     /**
    290      * Enables or disables the thread update.
    291      * <p/>If <code>true</code> the VM will be able to send thread information. Thread information
    292      * must be requested with {@link #requestThreadUpdate()}.
    293      * @param enabled the enable flag.
    294      */
    295     public void setThreadUpdateEnabled(boolean enabled) {
    296         mThreadUpdateEnabled = enabled;
    297         if (enabled == false) {
    298             mClientData.clearThreads();
    299         }
    300 
    301         try {
    302             HandleThread.sendTHEN(this, enabled);
    303         } catch (IOException ioe) {
    304             // ignore it here; client will clean up shortly
    305             ioe.printStackTrace();
    306         }
    307 
    308         update(CHANGE_THREAD_MODE);
    309     }
    310 
    311     /**
    312      * Returns whether the thread update is enabled.
    313      */
    314     public boolean isThreadUpdateEnabled() {
    315         return mThreadUpdateEnabled;
    316     }
    317 
    318     /**
    319      * Sends a thread update request. This is asynchronous.
    320      * <p/>The thread info can be accessed by {@link ClientData#getThreads()}. The notification
    321      * that the new data is available will be received through
    322      * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code>
    323      * containing the mask {@link #CHANGE_THREAD_DATA}.
    324      */
    325     public void requestThreadUpdate() {
    326         HandleThread.requestThreadUpdate(this);
    327     }
    328 
    329     /**
    330      * Sends a thread stack trace update request. This is asynchronous.
    331      * <p/>The thread info can be accessed by {@link ClientData#getThreads()} and
    332      * {@link ThreadInfo#getStackTrace()}.
    333      * <p/>The notification that the new data is available
    334      * will be received through {@link IClientChangeListener#clientChanged(Client, int)}
    335      * with a <code>changeMask</code> containing the mask {@link #CHANGE_THREAD_STACKTRACE}.
    336      */
    337     public void requestThreadStackTrace(int threadId) {
    338         HandleThread.requestThreadStackCallRefresh(this, threadId);
    339     }
    340 
    341     /**
    342      * Enables or disables the heap update.
    343      * <p/>If <code>true</code>, any GC will cause the client to send its heap information.
    344      * <p/>The heap information can be accessed by {@link ClientData#getVmHeapData()}.
    345      * <p/>The notification that the new data is available
    346      * will be received through {@link IClientChangeListener#clientChanged(Client, int)}
    347      * with a <code>changeMask</code> containing the value {@link #CHANGE_HEAP_DATA}.
    348      * @param enabled the enable flag
    349      */
    350     public void setHeapUpdateEnabled(boolean enabled) {
    351         mHeapUpdateEnabled = enabled;
    352 
    353         try {
    354             HandleHeap.sendHPIF(this,
    355                     enabled ? HandleHeap.HPIF_WHEN_EVERY_GC : HandleHeap.HPIF_WHEN_NEVER);
    356 
    357             HandleHeap.sendHPSG(this,
    358                     enabled ? HandleHeap.WHEN_GC : HandleHeap.WHEN_DISABLE,
    359                     HandleHeap.WHAT_MERGE);
    360         } catch (IOException ioe) {
    361             // ignore it here; client will clean up shortly
    362         }
    363 
    364         update(CHANGE_HEAP_MODE);
    365     }
    366 
    367     /**
    368      * Returns whether the heap update is enabled.
    369      * @see #setHeapUpdateEnabled(boolean)
    370      */
    371     public boolean isHeapUpdateEnabled() {
    372         return mHeapUpdateEnabled;
    373     }
    374 
    375     /**
    376      * Sends a native heap update request. this is asynchronous.
    377      * <p/>The native heap info can be accessed by {@link ClientData#getNativeAllocationList()}.
    378      * The notification that the new data is available will be received through
    379      * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code>
    380      * containing the mask {@link #CHANGE_NATIVE_HEAP_DATA}.
    381      */
    382     public boolean requestNativeHeapInformation() {
    383         try {
    384             HandleNativeHeap.sendNHGT(this);
    385             return true;
    386         } catch (IOException e) {
    387             Log.e("ddmlib", e);
    388         }
    389 
    390         return false;
    391     }
    392 
    393     /**
    394      * Enables or disables the Allocation tracker for this client.
    395      * <p/>If enabled, the VM will start tracking allocation informations. A call to
    396      * {@link #requestAllocationDetails()} will make the VM sends the information about all the
    397      * allocations that happened between the enabling and the request.
    398      * @param enable
    399      * @see #requestAllocationDetails()
    400      */
    401     public void enableAllocationTracker(boolean enable) {
    402         try {
    403             HandleHeap.sendREAE(this, enable);
    404         } catch (IOException e) {
    405             Log.e("ddmlib", e);
    406         }
    407     }
    408 
    409     /**
    410      * Sends a request to the VM to send the enable status of the allocation tracking.
    411      * This is asynchronous.
    412      * <p/>The allocation status can be accessed by {@link ClientData#getAllocationStatus()}.
    413      * The notification that the new status is available will be received through
    414      * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code>
    415      * containing the mask {@link #CHANGE_HEAP_ALLOCATION_STATUS}.
    416      */
    417     public void requestAllocationStatus() {
    418         try {
    419             HandleHeap.sendREAQ(this);
    420         } catch (IOException e) {
    421             Log.e("ddmlib", e);
    422         }
    423     }
    424 
    425     /**
    426      * Sends a request to the VM to send the information about all the allocations that have
    427      * happened since the call to {@link #enableAllocationTracker(boolean)} with <var>enable</var>
    428      * set to <code>null</code>. This is asynchronous.
    429      * <p/>The allocation information can be accessed by {@link ClientData#getAllocations()}.
    430      * The notification that the new data is available will be received through
    431      * {@link IClientChangeListener#clientChanged(Client, int)} with a <code>changeMask</code>
    432      * containing the mask {@link #CHANGE_HEAP_ALLOCATIONS}.
    433      */
    434     public void requestAllocationDetails() {
    435         try {
    436             HandleHeap.sendREAL(this);
    437         } catch (IOException e) {
    438             Log.e("ddmlib", e);
    439         }
    440     }
    441 
    442     /**
    443      * Sends a kill message to the VM.
    444      */
    445     public void kill() {
    446         try {
    447             HandleExit.sendEXIT(this, 1);
    448         } catch (IOException ioe) {
    449             Log.w("ddms", "Send of EXIT message failed");
    450             // ignore
    451         }
    452     }
    453 
    454     /**
    455      * Registers the client with a Selector.
    456      */
    457     void register(Selector sel) throws IOException {
    458         if (mChan != null) {
    459             mChan.register(sel, SelectionKey.OP_READ, this);
    460         }
    461     }
    462 
    463     /**
    464      * Sets the client to accept debugger connection on the "selected debugger port".
    465      *
    466      * @see AndroidDebugBridge#setSelectedClient(Client)
    467      * @see DdmPreferences#setSelectedDebugPort(int)
    468      */
    469     public void setAsSelectedClient() {
    470         MonitorThread monitorThread = MonitorThread.getInstance();
    471         if (monitorThread != null) {
    472             monitorThread.setSelectedClient(this);
    473         }
    474     }
    475 
    476     /**
    477      * Returns whether this client is the current selected client, accepting debugger connection
    478      * on the "selected debugger port".
    479      *
    480      * @see #setAsSelectedClient()
    481      * @see AndroidDebugBridge#setSelectedClient(Client)
    482      * @see DdmPreferences#setSelectedDebugPort(int)
    483      */
    484     public boolean isSelectedClient() {
    485         MonitorThread monitorThread = MonitorThread.getInstance();
    486         if (monitorThread != null) {
    487             return monitorThread.getSelectedClient() == this;
    488         }
    489 
    490         return false;
    491     }
    492 
    493     /**
    494      * Tell the client to open a server socket channel and listen for
    495      * connections on the specified port.
    496      */
    497     void listenForDebugger(int listenPort) throws IOException {
    498         mDebuggerListenPort = listenPort;
    499         mDebugger = new Debugger(this, listenPort);
    500     }
    501 
    502     /**
    503      * Initiate the JDWP handshake.
    504      *
    505      * On failure, closes the socket and returns false.
    506      */
    507     boolean sendHandshake() {
    508         assert mWriteBuffer.position() == 0;
    509 
    510         try {
    511             // assume write buffer can hold 14 bytes
    512             JdwpPacket.putHandshake(mWriteBuffer);
    513             int expectedLen = mWriteBuffer.position();
    514             mWriteBuffer.flip();
    515             if (mChan.write(mWriteBuffer) != expectedLen)
    516                 throw new IOException("partial handshake write");
    517         }
    518         catch (IOException ioe) {
    519             Log.e("ddms-client", "IO error during handshake: " + ioe.getMessage());
    520             mConnState = ST_ERROR;
    521             close(true /* notify */);
    522             return false;
    523         }
    524         finally {
    525             mWriteBuffer.clear();
    526         }
    527 
    528         mConnState = ST_AWAIT_SHAKE;
    529 
    530         return true;
    531     }
    532 
    533 
    534     /**
    535      * Send a non-DDM packet to the client.
    536      *
    537      * Equivalent to sendAndConsume(packet, null).
    538      */
    539     void sendAndConsume(JdwpPacket packet) throws IOException {
    540         sendAndConsume(packet, null);
    541     }
    542 
    543     /**
    544      * Send a DDM packet to the client.
    545      *
    546      * Ideally, we can do this with a single channel write.  If that doesn't
    547      * happen, we have to prevent anybody else from writing to the channel
    548      * until this packet completes, so we synchronize on the channel.
    549      *
    550      * Another goal is to avoid unnecessary buffer copies, so we write
    551      * directly out of the JdwpPacket's ByteBuffer.
    552      */
    553     void sendAndConsume(JdwpPacket packet, ChunkHandler replyHandler)
    554         throws IOException {
    555 
    556         if (mChan == null) {
    557             // can happen for e.g. THST packets
    558             Log.v("ddms", "Not sending packet -- client is closed");
    559             return;
    560         }
    561 
    562         if (replyHandler != null) {
    563             /*
    564              * Add the ID to the list of outstanding requests.  We have to do
    565              * this before sending the packet, in case the response comes back
    566              * before our thread returns from the packet-send function.
    567              */
    568             addRequestId(packet.getId(), replyHandler);
    569         }
    570 
    571         synchronized (mChan) {
    572             try {
    573                 packet.writeAndConsume(mChan);
    574             }
    575             catch (IOException ioe) {
    576                 removeRequestId(packet.getId());
    577                 throw ioe;
    578             }
    579         }
    580     }
    581 
    582     /**
    583      * Forward the packet to the debugger (if still connected to one).
    584      *
    585      * Consumes the packet.
    586      */
    587     void forwardPacketToDebugger(JdwpPacket packet)
    588         throws IOException {
    589 
    590         Debugger dbg = mDebugger;
    591 
    592         if (dbg == null) {
    593             Log.d("ddms", "Discarding packet");
    594             packet.consume();
    595         } else {
    596             dbg.sendAndConsume(packet);
    597         }
    598     }
    599 
    600     /**
    601      * Read data from our channel.
    602      *
    603      * This is called when data is known to be available, and we don't yet
    604      * have a full packet in the buffer.  If the buffer is at capacity,
    605      * expand it.
    606      */
    607     void read()
    608         throws IOException, BufferOverflowException {
    609 
    610         int count;
    611 
    612         if (mReadBuffer.position() == mReadBuffer.capacity()) {
    613             if (mReadBuffer.capacity() * 2 > MAX_BUF_SIZE) {
    614                 Log.e("ddms", "Exceeded MAX_BUF_SIZE!");
    615                 throw new BufferOverflowException();
    616             }
    617             Log.d("ddms", "Expanding read buffer to "
    618                 + mReadBuffer.capacity() * 2);
    619 
    620             ByteBuffer newBuffer = ByteBuffer.allocate(mReadBuffer.capacity() * 2);
    621 
    622             // copy entire buffer to new buffer
    623             mReadBuffer.position(0);
    624             newBuffer.put(mReadBuffer);  // leaves "position" at end of copied
    625 
    626             mReadBuffer = newBuffer;
    627         }
    628 
    629         count = mChan.read(mReadBuffer);
    630         if (count < 0)
    631             throw new IOException("read failed");
    632 
    633         if (Log.Config.LOGV) Log.v("ddms", "Read " + count + " bytes from " + this);
    634         //Log.hexDump("ddms", Log.DEBUG, mReadBuffer.array(),
    635         //    mReadBuffer.arrayOffset(), mReadBuffer.position());
    636     }
    637 
    638     /**
    639      * Return information for the first full JDWP packet in the buffer.
    640      *
    641      * If we don't yet have a full packet, return null.
    642      *
    643      * If we haven't yet received the JDWP handshake, we watch for it here
    644      * and consume it without admitting to have done so.  Upon receipt
    645      * we send out the "HELO" message, which is why this can throw an
    646      * IOException.
    647      */
    648     JdwpPacket getJdwpPacket() throws IOException {
    649 
    650         /*
    651          * On entry, the data starts at offset 0 and ends at "position".
    652          * "limit" is set to the buffer capacity.
    653          */
    654         if (mConnState == ST_AWAIT_SHAKE) {
    655             /*
    656              * The first thing we get from the client is a response to our
    657              * handshake.  It doesn't look like a packet, so we have to
    658              * handle it specially.
    659              */
    660             int result;
    661 
    662             result = JdwpPacket.findHandshake(mReadBuffer);
    663             //Log.v("ddms", "findHand: " + result);
    664             switch (result) {
    665                 case JdwpPacket.HANDSHAKE_GOOD:
    666                     Log.d("ddms",
    667                         "Good handshake from client, sending HELO to " + mClientData.getPid());
    668                     JdwpPacket.consumeHandshake(mReadBuffer);
    669                     mConnState = ST_NEED_DDM_PKT;
    670                     HandleHello.sendHelloCommands(this, SERVER_PROTOCOL_VERSION);
    671                     // see if we have another packet in the buffer
    672                     return getJdwpPacket();
    673                 case JdwpPacket.HANDSHAKE_BAD:
    674                     Log.d("ddms", "Bad handshake from client");
    675                     if (MonitorThread.getInstance().getRetryOnBadHandshake()) {
    676                         // we should drop the client, but also attempt to reopen it.
    677                         // This is done by the DeviceMonitor.
    678                         mDevice.getMonitor().addClientToDropAndReopen(this,
    679                                 IDebugPortProvider.NO_STATIC_PORT);
    680                     } else {
    681                         // mark it as bad, close the socket, and don't retry
    682                         mConnState = ST_NOT_JDWP;
    683                         close(true /* notify */);
    684                     }
    685                     break;
    686                 case JdwpPacket.HANDSHAKE_NOTYET:
    687                     Log.d("ddms", "No handshake from client yet.");
    688                     break;
    689                 default:
    690                     Log.e("ddms", "Unknown packet while waiting for client handshake");
    691             }
    692             return null;
    693         } else if (mConnState == ST_NEED_DDM_PKT ||
    694             mConnState == ST_NOT_DDM ||
    695             mConnState == ST_READY) {
    696             /*
    697              * Normal packet traffic.
    698              */
    699             if (mReadBuffer.position() != 0) {
    700                 if (Log.Config.LOGV) Log.v("ddms",
    701                     "Checking " + mReadBuffer.position() + " bytes");
    702             }
    703             return JdwpPacket.findPacket(mReadBuffer);
    704         } else {
    705             /*
    706              * Not expecting data when in this state.
    707              */
    708             Log.e("ddms", "Receiving data in state = " + mConnState);
    709         }
    710 
    711         return null;
    712     }
    713 
    714     /*
    715      * Add the specified ID to the list of request IDs for which we await
    716      * a response.
    717      */
    718     private void addRequestId(int id, ChunkHandler handler) {
    719         synchronized (mOutstandingReqs) {
    720             if (Log.Config.LOGV) Log.v("ddms",
    721                 "Adding req 0x" + Integer.toHexString(id) +" to set");
    722             mOutstandingReqs.put(id, handler);
    723         }
    724     }
    725 
    726     /*
    727      * Remove the specified ID from the list, if present.
    728      */
    729     void removeRequestId(int id) {
    730         synchronized (mOutstandingReqs) {
    731             if (Log.Config.LOGV) Log.v("ddms",
    732                 "Removing req 0x" + Integer.toHexString(id) + " from set");
    733             mOutstandingReqs.remove(id);
    734         }
    735 
    736         //Log.w("ddms", "Request " + Integer.toHexString(id)
    737         //    + " could not be removed from " + this);
    738     }
    739 
    740     /**
    741      * Determine whether this is a response to a request we sent earlier.
    742      * If so, return the ChunkHandler responsible.
    743      */
    744     ChunkHandler isResponseToUs(int id) {
    745 
    746         synchronized (mOutstandingReqs) {
    747             ChunkHandler handler = mOutstandingReqs.get(id);
    748             if (handler != null) {
    749                 if (Log.Config.LOGV) Log.v("ddms",
    750                     "Found 0x" + Integer.toHexString(id)
    751                     + " in request set - " + handler);
    752                 return handler;
    753             }
    754         }
    755 
    756         return null;
    757     }
    758 
    759     /**
    760      * An earlier request resulted in a failure.  This is the expected
    761      * response to a HELO message when talking to a non-DDM client.
    762      */
    763     void packetFailed(JdwpPacket reply) {
    764         if (mConnState == ST_NEED_DDM_PKT) {
    765             Log.d("ddms", "Marking " + this + " as non-DDM client");
    766             mConnState = ST_NOT_DDM;
    767         } else if (mConnState != ST_NOT_DDM) {
    768             Log.w("ddms", "WEIRD: got JDWP failure packet on DDM req");
    769         }
    770     }
    771 
    772     /**
    773      * The MonitorThread calls this when it sees a DDM request or reply.
    774      * If we haven't seen a DDM packet before, we advance the state to
    775      * ST_READY and return "false".  Otherwise, just return true.
    776      *
    777      * The idea is to let the MonitorThread know when we first see a DDM
    778      * packet, so we can send a broadcast to the handlers when a client
    779      * connection is made.  This method is synchronized so that we only
    780      * send the broadcast once.
    781      */
    782     synchronized boolean ddmSeen() {
    783         if (mConnState == ST_NEED_DDM_PKT) {
    784             mConnState = ST_READY;
    785             return false;
    786         } else if (mConnState != ST_READY) {
    787             Log.w("ddms", "WEIRD: in ddmSeen with state=" + mConnState);
    788         }
    789         return true;
    790     }
    791 
    792     /**
    793      * Close the client socket channel.  If there is a debugger associated
    794      * with us, close that too.
    795      *
    796      * Closing a channel automatically unregisters it from the selector.
    797      * However, we have to iterate through the selector loop before it
    798      * actually lets them go and allows the file descriptors to close.
    799      * The caller is expected to manage that.
    800      * @param notify Whether or not to notify the listeners of a change.
    801      */
    802     void close(boolean notify) {
    803         Log.d("ddms", "Closing " + this.toString());
    804 
    805         mOutstandingReqs.clear();
    806 
    807         try {
    808             if (mChan != null) {
    809                 mChan.close();
    810                 mChan = null;
    811             }
    812 
    813             if (mDebugger != null) {
    814                 mDebugger.close();
    815                 mDebugger = null;
    816             }
    817         }
    818         catch (IOException ioe) {
    819             Log.w("ddms", "failed to close " + this);
    820             // swallow it -- not much else to do
    821         }
    822 
    823         mDevice.removeClient(this, notify);
    824     }
    825 
    826     /**
    827      * Returns whether this {@link Client} has a valid connection to the application VM.
    828      */
    829     public boolean isValid() {
    830         return mChan != null;
    831     }
    832 
    833     void update(int changeMask) {
    834         mDevice.update(this, changeMask);
    835     }
    836 }
    837 
    838