/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.dalvik.ddmc; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; /** * This represents our connection to the DDM Server. */ public class DdmServer { public static final int CLIENT_PROTOCOL_VERSION = 1; private static HashMap mHandlerMap = new HashMap(); private static final int CONNECTED = 1; private static final int DISCONNECTED = 2; private static volatile boolean mRegistrationComplete = false; private static boolean mRegistrationTimedOut = false; /** * Don't instantiate; all members and methods are static. */ private DdmServer() {} /** * Register an instance of the ChunkHandler class to handle a specific * chunk type. * * Throws an exception if the type already has a handler registered. */ public static void registerHandler(int type, ChunkHandler handler) { if (handler == null) throw new NullPointerException(); synchronized (mHandlerMap) { if (mHandlerMap.get(type) != null) throw new RuntimeException("type " + Integer.toHexString(type) + " already registered"); mHandlerMap.put(type, handler); } } /** * Unregister the existing handler for the specified type. * * Returns the old handler. */ public static ChunkHandler unregisterHandler(int type) { synchronized (mHandlerMap) { return mHandlerMap.remove(type); } } /** * The application must call here after it finishes registering * handlers. */ public static void registrationComplete() { // sync on mHandlerMap because it's convenient and makes a kind of // sense synchronized (mHandlerMap) { mRegistrationComplete = true; mHandlerMap.notifyAll(); } } /** * Send a chunk of data to the DDM server. This takes the form of a * JDWP "event", which does not elicit a response from the server. * * Use this for "unsolicited" chunks. */ public static void sendChunk(Chunk chunk) { nativeSendChunk(chunk.type, chunk.data, chunk.offset, chunk.length); } /* send a chunk to the DDM server */ native private static void nativeSendChunk(int type, byte[] data, int offset, int length); /* * Called by the VM when the DDM server connects or disconnects. */ private static void broadcast(int event) { synchronized (mHandlerMap) { Collection values = mHandlerMap.values(); Iterator iter = values.iterator(); while (iter.hasNext()) { ChunkHandler handler = (ChunkHandler) iter.next(); switch (event) { case CONNECTED: handler.connected(); break; case DISCONNECTED: handler.disconnected(); break; default: throw new UnsupportedOperationException(); } } } } /* * This is called by the VM when a chunk arrives. * * For a DDM-aware application, we want to wait until the app has had * a chance to register all of its chunk handlers. Otherwise, we'll * end up dropping early-arriving packets on the floor. * * For a non-DDM-aware application, we'll end up waiting here forever * if DDMS happens to connect. It's hard to know for sure that * registration isn't going to happen, so we settle for a timeout. */ private static Chunk dispatch(int type, byte[] data, int offset, int length) { ChunkHandler handler; synchronized (mHandlerMap) { /* * If registration hasn't completed, and we haven't timed out * waiting for it, wait a bit. */ while (!mRegistrationComplete && !mRegistrationTimedOut) { //System.out.println("dispatch() waiting for reg"); try { mHandlerMap.wait(1000); // 1.0 sec } catch (InterruptedException ie) { continue; } if (!mRegistrationComplete) { /* timed out, don't wait again */ mRegistrationTimedOut = true; } } handler = mHandlerMap.get(type); } //System.out.println(" dispatch cont"); if (handler == null) { return null; } Chunk chunk = new Chunk(type, data, offset, length); return handler.handleChunk(chunk); } }