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