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