/// <summary> /// tells adb to talk to a specific device /// </summary> /// <param name="adbChan"> the socket connection to adb </param> /// <param name="device"> The device to talk to. </param> /// <exception cref="TimeoutException"> in case of timeout on the connection. </exception> /// <exception cref="AdbCommandRejectedException"> if adb rejects the command </exception> /// <exception cref="IOException"> in case of I/O error on the connection. </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: static void setDevice(java.nio.channels.SocketChannel adbChan, IDevice device) throws TimeoutException, AdbCommandRejectedException, java.io.IOException internal static void setDevice(SocketChannel adbChan, IDevice device) { // if the device is not -1, then we first tell adb we're looking to talk // to a specific device if (device != null) { string msg = "host:transport:" + device.serialNumber; //$NON-NLS-1$ var device_query = formAdbRequest(msg); write(adbChan, device_query); AdbResponse resp = readAdbResponse(adbChan, false); // readDiagString if (resp.okay == false) { throw new AdbCommandRejectedException(resp.message, true); //errorDuringDeviceSelection } } }
/// <summary> /// Closes the connection. /// </summary> public void close() { if (mChannel != null) { try { mChannel.close(); } catch (IOException) { // nothing to be done really... } mChannel = null; } }
/// <summary> /// Write until all data in "data" is written or the connection fails or times out. /// <p/>This uses the default time out value. </summary> /// <param name="chan"> the opened socket to write to. </param> /// <param name="data"> the buffer to send. </param> /// <exception cref="TimeoutException"> in case of timeout on the connection. </exception> /// <exception cref="IOException"> in case of I/O error on the connection. </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: static void write(java.nio.channels.SocketChannel chan, byte[] data) throws TimeoutException, java.io.IOException internal static void write(SocketChannel chan, byte[] data) { write(chan, data, -1, DdmPreferences.timeOut); }
/// <summary> /// Write until all data in "data" is written, the optional length is reached, /// the timeout expires, or the connection fails. Returns "true" if all /// data was written. </summary> /// <param name="chan"> the opened socket to write to. </param> /// <param name="data"> the buffer to send. </param> /// <param name="length"> the length to write or -1 to send the whole buffer. </param> /// <param name="timeout"> The timeout value. A timeout of zero means "wait forever". </param> /// <exception cref="TimeoutException"> in case of timeout on the connection. </exception> /// <exception cref="IOException"> in case of I/O error on the connection. </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: static void write(java.nio.channels.SocketChannel chan, byte[] data, int length, int timeout) throws TimeoutException, java.io.IOException internal static void write(SocketChannel chan, byte[] data, int length, int timeout) { ByteBuffer buf = ByteBuffer.wrap(data, 0, length != -1 ? length : data.Length); int numWaits = 0; while (buf.position != buf.limit) { int count; count = chan.write(buf); if (count < 0) { Log.d("ddms", "write: channel EOF"); throw new IOException("channel EOF"); } else if (count == 0) { // TODO: need more accurate timeout? if (timeout != 0 && numWaits * WAIT_TIME > timeout) { Log.d("ddms", "write: timeout"); throw new TimeoutException(); } // non-blocking spin Thread.Sleep(WAIT_TIME); numWaits++; } else { numWaits = 0; } } }
/// <summary> /// Fills a buffer from a socket. </summary> /// <param name="socket"> </param> /// <param name="buffer"> </param> /// <returns> the content of the buffer as a string, or null if it failed to convert the buffer. </returns> /// <exception cref="IOException"> </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: private String read(java.nio.channels.SocketChannel socket, byte[] buffer) throws java.io.IOException private string read(SocketChannel socket, byte[] buffer) { ByteBuffer buf = ByteBuffer.wrap(buffer, 0, buffer.Length); while (buf.position != buf.limit) { int count; count = socket.read(buf); if (count < 0) { throw new IOException("EOF"); } } try { return buffer.getString(0, buf.position, AdbHelper.DEFAULT_ENCODING); } catch (ArgumentException) { // we'll return null below. } return null; }
/// <summary> /// Reads the response from ADB after a command. </summary> /// <param name="chan"> The socket channel that is connected to adb. </param> /// <param name="readDiagString"> If true, we're expecting an OKAY response to be /// followed by a diagnostic string. Otherwise, we only expect the /// diagnostic string to follow a FAIL. </param> /// <exception cref="TimeoutException"> in case of timeout on the connection. </exception> /// <exception cref="IOException"> in case of I/O error on the connection. </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: static AdbResponse readAdbResponse(java.nio.channels.SocketChannel chan, boolean readDiagString) throws TimeoutException, java.io.IOException internal static AdbResponse readAdbResponse(SocketChannel chan, bool readDiagString) { AdbResponse resp = new AdbResponse(); var reply = new byte[4]; read(chan, reply); if (isOkay(reply)) { resp.okay = true; } else { readDiagString = true; // look for a reason after the FAIL resp.okay = false; } // not a loop -- use "while" so we can use "break" try { while (readDiagString) { // length string is in next 4 bytes var lenBuf = new byte[4]; read(chan, lenBuf); string lenStr = replyToString(lenBuf); int len; try { len = Convert.ToInt32(lenStr, 16); } catch (SystemException) { Log.w("ddms", "Expected digits, got '" + lenStr + "': " + lenBuf[0] + " " + lenBuf[1] + " " + lenBuf[2] + " " + lenBuf[3]); Log.w("ddms", "reply was " + replyToString(reply)); break; } var msg = new byte[len]; read(chan, msg); resp.message = replyToString(msg); Log.v("ddms", "Got reply '" + replyToString(reply) + "', diag='" + resp.message + "'"); break; } } catch (Exception) { // ignore those, since it's just reading the diagnose string, the response will // contain okay==false anyway. } return resp; }
/// <summary> /// Create an object for a new client connection. /// </summary> /// <param name="device"> the device this client belongs to </param> /// <param name="chan"> the connected <seealso cref="SocketChannel"/>. </param> /// <param name="pid"> the client pid. </param> internal Client(Device device, SocketChannel chan, int pid) { mDevice = device; mChan = chan; mReadBuffer = ByteBuffer.allocate(INITIAL_BUF_SIZE); mWriteBuffer = ByteBuffer.allocate(WRITE_BUF_SIZE); mOutstandingReqs = new Dictionary<int?, ChunkHandler>(); mConnState = ST_INIT; mClientData = new ClientData(pid); mThreadUpdateEnabled = DdmPreferences.initialThreadUpdate; mHeapUpdateEnabled = DdmPreferences.initialHeapUpdate; }
/// <summary> /// Accept a new connection from the specified listen channel. This /// is so we can listen on a dedicated port for the "current" client, /// where "current" is constantly in flux. /// /// Must be synchronized with other uses of mChannel and mPreBuffer. /// /// Returns "null" if we're already talking to somebody. /// </summary> internal SocketChannel accept(ServerSocketChannel listenChan) { if(listenChan != null) { SocketChannel newChan; newChan = listenChan.accept(); if(mChannel != null) { Log.w("ddms", "debugger already talking to " + mClient + " on " + mListenPort); newChan.close(); return null; } mChannel = newChan; mChannel.configureBlocking(false); // required for Selector mConnState = ST_AWAIT_SHAKE; return mChannel; } return null; }
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: private void processIncomingJdwpData(Device device, java.nio.channels.SocketChannel monitorSocket, int length) throws java.io.IOException private void processIncomingJdwpData(Device device, SocketChannel monitorSocket, int length) { if (length >= 0) { // array for the current pids. List<int?> pidList = new List<int?>(); // get the string data if there are any if (length > 0) { var buffer = new byte[length]; string result = read(monitorSocket, buffer); // split each line in its own list and create an array of integer pid string[] pids = StringHelperClass.StringSplit(result, "\n", true); //$NON-NLS-1$ foreach (string pid in pids) { try { pidList.Add(Convert.ToInt32(pid)); } catch (SystemException) { // looks like this pid is not really a number. Lets ignore it. continue; } } } MonitorThread monitorThread = MonitorThread.instance; // Now we merge the current list with the old one. // this is the same mechanism as the merging of the device list. // For each client in the current list, we look for a matching the pid in the new list. // * if we find it, we do nothing, except removing the pid from its list, // to mark it as "processed" // * if we do not find any match, we remove the client from the current list. // Once this is done, the new list contains pids for which we don't have clients yet, // so we create clients for them, add them to the list, and start monitoring them. IList<Client> clients = device.clientList; bool changed = false; // because MonitorThread#dropClient acquires first the monitorThread lock and then the // Device client list lock (when removing the Client from the list), we have to make // sure we acquire the locks in the same order, since another thread (MonitorThread), // could call dropClient itself. lock (monitorThread) { lock (clients) { for (int c = 0; c < clients.Count; ) { Client client = clients[c]; int pid = client.clientData.pid; // look for a matching pid int? match = null; foreach (int? matchingPid in pidList) { if (pid == (int)matchingPid) { match = matchingPid; break; } } if (match != null) { pidList.Remove(match); c++; // move on to the next client. } else { // we need to drop the client. the client will remove itself from the // list of its device which is 'clients', so there's no need to // increment c. // We ask the monitor thread to not send notification, as we'll do // it once at the end. monitorThread.dropClient(client, false); // notify changed = true; } } } } // at this point whatever pid is left in the list needs to be converted into Clients. foreach (int newPid in pidList) { openClient(device, newPid, nextDebuggerPort, monitorThread); changed = true; } if (changed) { mServer.deviceChanged(device, DeviceConstants.CHANGE_CLIENT_LIST); } } }
/// <summary> /// Creates a client and register it to the monitor thread </summary> /// <param name="device"> </param> /// <param name="pid"> </param> /// <param name="socket"> </param> /// <param name="debuggerPort"> the debugger port. </param> /// <param name="monitorThread"> the <seealso cref="MonitorThread"/> object. </param> private void createClient(Device device, int pid, SocketChannel socket, int debuggerPort, MonitorThread monitorThread) { /* * Successfully connected to something. Create a Client object, add * it to the list, and initiate the JDWP handshake. */ Client client = new Client(device, socket, pid); if (client.sendHandshake()) { try { if (AndroidDebugBridge.clientSupport) { client.listenForDebugger(debuggerPort); } } catch (IOException) { client.clientData.debuggerConnectionStatus = ClientData.DebuggerStatus.ERROR; Log.e("ddms", "Can't bind to local " + debuggerPort + " for debugger"); // oh well } client.requestAllocationStatus(); } else { Log.e("ddms", "Handshake with " + client + " failed!"); /* * The handshake send failed. We could remove it now, but if the * failure is "permanent" we'll just keep banging on it and * getting the same result. Keep it in the list with its "error" * state so we don't try to reopen it. */ } if (client.valid) { device.addClient(client); monitorThread.addClient(client); } else { client = null; } }
//JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: private boolean sendDeviceMonitoringRequest(java.nio.channels.SocketChannel socket, Device device) throws TimeoutException, AdbCommandRejectedException, java.io.IOException private bool sendDeviceMonitoringRequest(SocketChannel socket, Device device) { try { AdbHelper.setDevice(socket, device); var request = AdbHelper.formAdbRequest("track-jdwp"); //$NON-NLS-1$ AdbHelper.write(socket, request); AdbHelper.AdbResponse resp = AdbHelper.readAdbResponse(socket, false); // readDiagString if (resp.okay == false) { // request was refused by adb! Log.e("DeviceMonitor", "adb refused request: " + resp.message); } return resp.okay; } catch (TimeoutException e) { Log.e("DeviceMonitor", "Sending jdwp tracking request timed out!"); throw e; } catch (IOException e) { Log.e("DeviceMonitor", "Sending jdwp tracking request failed!"); throw e; } }
private void handleExpectioninMonitorLoop(Exception e) { if (mQuit == false) { if (e is TimeoutException) { Log.e("DeviceMonitor", "Adb connection Error: timeout"); } else { Log.e("DeviceMonitor", "Adb connection Error:" + e.Message); } mMonitoring = false; if (mMainAdbConnection != null) { try { mMainAdbConnection.close(); } catch (IOException) { // we can safely ignore that one. } mMainAdbConnection = null; // remove all devices from list // because we are going to call mServer.deviceDisconnected which will acquire this // lock we lock it first, so that the AndroidDebugBridge lock is always locked // first. lock (AndroidDebugBridge.@lock) { lock (mDevices) { for (int n = mDevices.Count - 1; n >= 0; n--) { Device device = mDevices[0]; removeDevice(device); mServer.deviceDisconnected(device); } } } } } }
/// <summary> /// Monitors the devices. This connects to the Debug Bridge /// </summary> private void deviceMonitorLoop() { do { try { if (mMainAdbConnection == null) { Log.d("DeviceMonitor", "Opening adb connection"); mMainAdbConnection = openAdbConnection(); if (mMainAdbConnection == null) { mConnectionAttempt++; Log.e("DeviceMonitor", "Connection attempts: " + mConnectionAttempt); if (mConnectionAttempt > 10) { if (mServer.startAdb() == false) { mRestartAttemptCount++; Log.e("DeviceMonitor", "adb restart attempts: " + mRestartAttemptCount); } else { mRestartAttemptCount = 0; } } waitABit(); } else { Log.d("DeviceMonitor", "Connected to adb for device monitoring"); mConnectionAttempt = 0; } } if (mMainAdbConnection != null && mMonitoring == false) { mMonitoring = sendDeviceListMonitoringRequest(); } if (mMonitoring) { // read the length of the incoming message int length = readLength(mMainAdbConnection, mLengthBuffer); if (length >= 0) { // read the incoming message processIncomingDeviceData(length); // flag the fact that we have build the list at least once. mInitialDeviceListDone = true; } } } /*catch (AsynchronousCloseException ace) { // this happens because of a call to Quit. We do nothing, and the loop will break. }*/ catch (TimeoutException ioe) { handleExpectioninMonitorLoop(ioe); } catch (IOException ioe) { handleExpectioninMonitorLoop(ioe); } } while (mQuit == false); }
/// <summary> /// Opens the sync connection. This must be called before any calls to push[File] / pull[File]. </summary> /// <returns> true if the connection opened, false if adb refuse the connection. This can happen /// if the <seealso cref="Device"/> is invalid. </returns> /// <exception cref="TimeoutException"> in case of timeout on the connection. </exception> /// <exception cref="AdbCommandRejectedException"> if adb rejects the command </exception> /// <exception cref="IOException"> If the connection to adb failed. </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: boolean openSync() throws TimeoutException, AdbCommandRejectedException, java.io.IOException internal bool openSync() { try { mChannel = SocketChannel.open(mAddress); mChannel.configureBlocking(false); // target a specific device AdbHelper.setDevice(mChannel, mDevice); var request = AdbHelper.formAdbRequest("sync:"); //$NON-NLS-1$ AdbHelper.write(mChannel, request, -1, DdmPreferences.timeOut); AdbHelper.AdbResponse resp = AdbHelper.readAdbResponse(mChannel, false); // readDiagString if (resp.okay == false) { Log.w("ddms", "Got unhappy response from ADB sync req: " + resp.message); mChannel.close(); mChannel = null; return false; } } catch (TimeoutException e) { if (mChannel != null) { try { mChannel.close(); } catch (IOException) { // we want to throw the original exception, so we ignore this one. } mChannel = null; } throw e; } catch (IOException e) { if (mChannel != null) { try { mChannel.close(); } catch (IOException) { // we want to throw the original exception, so we ignore this one. } mChannel = null; } throw e; } return true; }
/// <summary> /// Close the client socket channel. If there is a debugger associated /// with us, close that too. /// /// Closing a channel automatically unregisters it from the selector. /// However, we have to iterate through the selector loop before it /// actually lets them go and allows the file descriptors to close. /// The caller is expected to manage that. </summary> /// <param name="notify"> Whether or not to notify the listeners of a change. </param> internal virtual void close(bool notify) { Log.d("ddms", "Closing " + this.ToString()); mOutstandingReqs.Clear(); try { if (mChan != null) { mChan.close(); mChan = null; } if (mDebugger != null) { mDebugger.close(); mDebugger = null; } } catch (IOException) { Log.w("ddms", "failed to close " + this); // swallow it -- not much else to do } mDevice.removeClient(this, notify); }
/// <summary> /// Starts the connection of the console. </summary> /// <returns> true if success. </returns> private bool start() { DnsEndPoint socketAddr; try { socketAddr = new DnsEndPoint(HOST, mPort); } catch (ArgumentException) { return false; } try { mSocketChannel = SocketChannel.open(socketAddr); } catch (IOException) { return false; } // read some stuff from it readLines(); return true; }
/// <summary> /// Write our packet to "chan". Consumes the packet as part of the /// write. /// /// The JDWP packet starts at offset 0 and ends at mBuffer.position(). /// </summary> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: void writeAndConsume(java.nio.channels.SocketChannel chan) throws java.io.IOException internal void writeAndConsume(SocketChannel chan) { int oldLimit; //Log.i("ddms", "writeAndConsume: pos=" + mBuffer.position() // + ", limit=" + mBuffer.limit()); Debug.Assert(mLength > 0); mBuffer.flip(); // limit<-posn, posn<-0 oldLimit = mBuffer.limit; mBuffer.limit = (mLength); while (mBuffer.position != mBuffer.limit) { chan.write(mBuffer); } // position should now be at end of packet Debug.Assert(mBuffer.position == mLength); mBuffer.limit= (oldLimit); mBuffer.compact(); // shift posn...limit, posn<-pending data //Log.i("ddms", " : pos=" + mBuffer.position() // + ", limit=" + mBuffer.limit()); }
/// <summary> /// Close the data connection only. /// </summary> /*lock*/ internal void closeData() { try { if (mChannel != null) { mChannel.close(); mChannel = null; mConnState = ST_NOT_CONNECTED; ClientData cd = mClient.clientData; cd.debuggerConnectionStatus = ClientData.DebuggerStatus.DEFAULT; mClient.update(Client.CHANGE_DEBUGGER_STATUS); } } catch (IOException) { Log.w("ddms", "Failed to close data " + this); } }
/// <summary> /// Reads the length of the next message from a socket. </summary> /// <param name="socket"> The <seealso cref="SocketChannel"/> to read from. </param> /// <returns> the length, or 0 (zero) if no data is available from the socket. </returns> /// <exception cref="IOException"> if the connection failed. </exception> //JAVA TO C# CONVERTER WARNING: Method 'throws' clauses are not available in .NET: //ORIGINAL LINE: private int readLength(java.nio.channels.SocketChannel socket, byte[] buffer) throws java.io.IOException private int readLength(SocketChannel socket, byte[] buffer) { string msg = read(socket, buffer); if (msg != null) { try { return Convert.ToInt32(msg, 16); } catch (SystemException) { // we'll throw an exception below. } } // we receive something we can't read. It's better to reset the connection at this point. throw new IOException("Unable to read length"); }