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