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