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 org.apache.harmony.dalvik.ddmc;
18 
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 
23 
24 /**
25  * This represents our connection to the DDM Server.
26  */
27 public class DdmServer {
28 
29     public static final int CLIENT_PROTOCOL_VERSION = 1;
30 
31     private static HashMap<Integer,ChunkHandler> mHandlerMap =
32         new HashMap<Integer,ChunkHandler>();
33 
34     private static final int CONNECTED = 1;
35     private static final int DISCONNECTED = 2;
36 
37     private static volatile boolean mRegistrationComplete = false;
38     private static boolean mRegistrationTimedOut = false;
39 
40 
41     /**
42      * Don't instantiate; all members and methods are static.
43      */
DdmServer()44     private DdmServer() {}
45 
46     /**
47      * Register an instance of the ChunkHandler class to handle a specific
48      * chunk type.
49      *
50      * Throws an exception if the type already has a handler registered.
51      */
registerHandler(int type, ChunkHandler handler)52     public static void registerHandler(int type, ChunkHandler handler) {
53         if (handler == null) {
54             throw new NullPointerException("handler == null");
55         }
56         synchronized (mHandlerMap) {
57             if (mHandlerMap.get(type) != null)
58                 throw new RuntimeException("type " + Integer.toHexString(type)
59                     + " already registered");
60 
61             mHandlerMap.put(type, handler);
62         }
63     }
64 
65     /**
66      * Unregister the existing handler for the specified type.
67      *
68      * Returns the old handler.
69      */
unregisterHandler(int type)70     public static ChunkHandler unregisterHandler(int type) {
71         synchronized (mHandlerMap) {
72             return mHandlerMap.remove(type);
73         }
74     }
75 
76     /**
77      * The application must call here after it finishes registering
78      * handlers.
79      */
registrationComplete()80     public static void registrationComplete() {
81         // sync on mHandlerMap because it's convenient and makes a kind of
82         // sense
83         synchronized (mHandlerMap) {
84             mRegistrationComplete = true;
85             mHandlerMap.notifyAll();
86         }
87     }
88 
89     /**
90      * Send a chunk of data to the DDM server.  This takes the form of a
91      * JDWP "event", which does not elicit a response from the server.
92      *
93      * Use this for "unsolicited" chunks.
94      */
sendChunk(Chunk chunk)95     public static void sendChunk(Chunk chunk) {
96         nativeSendChunk(chunk.type, chunk.data, chunk.offset, chunk.length);
97     }
98 
99     /* send a chunk to the DDM server */
nativeSendChunk(int type, byte[] data, int offset, int length)100     native private static void nativeSendChunk(int type, byte[] data,
101         int offset, int length);
102 
103     /*
104      * Called by the VM when the DDM server connects or disconnects.
105      */
broadcast(int event)106     private static void broadcast(int event)
107     {
108         synchronized (mHandlerMap) {
109             Collection values = mHandlerMap.values();
110             Iterator iter = values.iterator();
111 
112             while (iter.hasNext()) {
113                 ChunkHandler handler = (ChunkHandler) iter.next();
114                 switch (event) {
115                     case CONNECTED:
116                         handler.connected();
117                         break;
118                     case DISCONNECTED:
119                         handler.disconnected();
120                         break;
121                     default:
122                         throw new UnsupportedOperationException();
123                 }
124             }
125         }
126     }
127 
128     /*
129      * This is called by the VM when a chunk arrives.
130      *
131      * For a DDM-aware application, we want to wait until the app has had
132      * a chance to register all of its chunk handlers.  Otherwise, we'll
133      * end up dropping early-arriving packets on the floor.
134      *
135      * For a non-DDM-aware application, we'll end up waiting here forever
136      * if DDMS happens to connect.  It's hard to know for sure that
137      * registration isn't going to happen, so we settle for a timeout.
138      */
dispatch(int type, byte[] data, int offset, int length)139     private static Chunk dispatch(int type, byte[] data, int offset, int length)
140     {
141         ChunkHandler handler;
142 
143         synchronized (mHandlerMap) {
144             /*
145              * If registration hasn't completed, and we haven't timed out
146              * waiting for it, wait a bit.
147              */
148             while (!mRegistrationComplete && !mRegistrationTimedOut) {
149                 //System.out.println("dispatch() waiting for reg");
150                 try {
151                     mHandlerMap.wait(1000);     // 1.0 sec
152                 } catch (InterruptedException ie) {
153                     continue;
154                 }
155 
156                 if (!mRegistrationComplete) {
157                     /* timed out, don't wait again */
158                     mRegistrationTimedOut = true;
159                 }
160             }
161 
162             handler = mHandlerMap.get(type);
163         }
164         //System.out.println(" dispatch cont");
165 
166         if (handler == null) {
167             return null;
168         }
169 
170         Chunk chunk = new Chunk(type, data, offset, length);
171         return handler.handleChunk(chunk);
172     }
173 }
174