1 // Copyright 2014 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/devtools/device/usb/android_usb_device.h" 6 7 #include <set> 8 9 #include "base/barrier_closure.h" 10 #include "base/base64.h" 11 #include "base/lazy_instance.h" 12 #include "base/message_loop/message_loop.h" 13 #include "base/stl_util.h" 14 #include "base/strings/string_util.h" 15 #include "base/strings/stringprintf.h" 16 #include "base/strings/utf_string_conversions.h" 17 #include "chrome/browser/devtools/device/usb/android_rsa.h" 18 #include "chrome/browser/devtools/device/usb/android_usb_socket.h" 19 #include "components/usb_service/usb_device.h" 20 #include "components/usb_service/usb_interface.h" 21 #include "components/usb_service/usb_service.h" 22 #include "content/public/browser/browser_thread.h" 23 #include "crypto/rsa_private_key.h" 24 #include "net/base/ip_endpoint.h" 25 #include "net/base/net_errors.h" 26 #include "net/socket/stream_socket.h" 27 28 using usb_service::UsbConfigDescriptor; 29 using usb_service::UsbDevice; 30 using usb_service::UsbDeviceHandle; 31 using usb_service::UsbInterfaceAltSettingDescriptor; 32 using usb_service::UsbInterfaceDescriptor; 33 using usb_service::UsbEndpointDescriptor; 34 using usb_service::UsbService; 35 using usb_service::UsbTransferStatus; 36 37 namespace { 38 39 const size_t kHeaderSize = 24; 40 41 const int kAdbClass = 0xff; 42 const int kAdbSubclass = 0x42; 43 const int kAdbProtocol = 0x1; 44 45 const int kUsbTimeout = 0; 46 47 const uint32 kMaxPayload = 4096; 48 const uint32 kVersion = 0x01000000; 49 50 static const char kHostConnectMessage[] = "host::"; 51 52 using content::BrowserThread; 53 54 typedef std::vector<scoped_refptr<UsbDevice> > UsbDevices; 55 typedef std::set<scoped_refptr<UsbDevice> > UsbDeviceSet; 56 57 // Stores android wrappers around claimed usb devices on caller thread. 58 base::LazyInstance<std::vector<AndroidUsbDevice*> >::Leaky g_devices = 59 LAZY_INSTANCE_INITIALIZER; 60 61 bool IsAndroidInterface( 62 scoped_refptr<const usb_service::UsbInterfaceDescriptor> interface) { 63 if (interface->GetNumAltSettings() == 0) 64 return false; 65 66 scoped_refptr<const UsbInterfaceAltSettingDescriptor> idesc = 67 interface->GetAltSetting(0); 68 69 if (idesc->GetInterfaceClass() != kAdbClass || 70 idesc->GetInterfaceSubclass() != kAdbSubclass || 71 idesc->GetInterfaceProtocol() != kAdbProtocol || 72 idesc->GetNumEndpoints() != 2) { 73 return false; 74 } 75 return true; 76 } 77 78 scoped_refptr<AndroidUsbDevice> ClaimInterface( 79 crypto::RSAPrivateKey* rsa_key, 80 scoped_refptr<UsbDeviceHandle> usb_handle, 81 scoped_refptr<const UsbInterfaceDescriptor> interface, 82 int interface_id) { 83 scoped_refptr<const UsbInterfaceAltSettingDescriptor> idesc = 84 interface->GetAltSetting(0); 85 86 int inbound_address = 0; 87 int outbound_address = 0; 88 int zero_mask = 0; 89 90 for (size_t i = 0; i < idesc->GetNumEndpoints(); ++i) { 91 scoped_refptr<const UsbEndpointDescriptor> edesc = 92 idesc->GetEndpoint(i); 93 if (edesc->GetTransferType() != usb_service::USB_TRANSFER_BULK) 94 continue; 95 if (edesc->GetDirection() == usb_service::USB_DIRECTION_INBOUND) 96 inbound_address = edesc->GetAddress(); 97 else 98 outbound_address = edesc->GetAddress(); 99 zero_mask = edesc->GetMaximumPacketSize() - 1; 100 } 101 102 if (inbound_address == 0 || outbound_address == 0) 103 return NULL; 104 105 if (!usb_handle->ClaimInterface(interface_id)) 106 return NULL; 107 108 base::string16 serial; 109 if (!usb_handle->GetSerial(&serial) || serial.empty()) 110 return NULL; 111 112 return new AndroidUsbDevice(rsa_key, usb_handle, base::UTF16ToASCII(serial), 113 inbound_address, outbound_address, zero_mask, 114 interface_id); 115 } 116 117 uint32 Checksum(const std::string& data) { 118 unsigned char* x = (unsigned char*)data.data(); 119 int count = data.length(); 120 uint32 sum = 0; 121 while (count-- > 0) 122 sum += *x++; 123 return sum; 124 } 125 126 void DumpMessage(bool outgoing, const char* data, size_t length) { 127 #if 0 128 std::string result = ""; 129 if (length == kHeaderSize) { 130 for (size_t i = 0; i < 24; ++i) { 131 result += base::StringPrintf("%02x", 132 data[i] > 0 ? data[i] : (data[i] + 0x100) & 0xFF); 133 if ((i + 1) % 4 == 0) 134 result += " "; 135 } 136 for (size_t i = 0; i < 24; ++i) { 137 if (data[i] >= 0x20 && data[i] <= 0x7E) 138 result += data[i]; 139 else 140 result += "."; 141 } 142 } else { 143 result = base::StringPrintf("%d: ", (int)length); 144 for (size_t i = 0; i < length; ++i) { 145 if (data[i] >= 0x20 && data[i] <= 0x7E) 146 result += data[i]; 147 else 148 result += "."; 149 } 150 } 151 LOG(ERROR) << (outgoing ? "[out] " : "[ in] ") << result; 152 #endif // 0 153 } 154 155 void ReleaseInterface(scoped_refptr<UsbDeviceHandle> usb_device, 156 int interface_id) { 157 usb_device->ReleaseInterface(interface_id); 158 usb_device->Close(); 159 } 160 161 } // namespace 162 163 AdbMessage::AdbMessage(uint32 command, 164 uint32 arg0, 165 uint32 arg1, 166 const std::string& body) 167 : command(command), 168 arg0(arg0), 169 arg1(arg1), 170 body(body) { 171 } 172 173 AdbMessage::~AdbMessage() { 174 } 175 176 static void RespondOnCallerThread(const AndroidUsbDevicesCallback& callback, 177 AndroidUsbDevices* new_devices) { 178 scoped_ptr<AndroidUsbDevices> devices(new_devices); 179 180 // Add raw pointers to the newly claimed devices. 181 for (AndroidUsbDevices::iterator it = devices->begin(); it != devices->end(); 182 ++it) { 183 g_devices.Get().push_back(*it); 184 } 185 186 // Return all claimed devices. 187 AndroidUsbDevices result(g_devices.Get().begin(), g_devices.Get().end()); 188 callback.Run(result); 189 } 190 191 static void RespondOnFileThread( 192 const AndroidUsbDevicesCallback& callback, 193 AndroidUsbDevices* devices, 194 scoped_refptr<base::MessageLoopProxy> caller_message_loop_proxy) { 195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 196 caller_message_loop_proxy->PostTask( 197 FROM_HERE, 198 base::Bind(&RespondOnCallerThread, callback, devices)); 199 } 200 201 static void OpenAndroidDeviceOnFileThread( 202 AndroidUsbDevices* devices, 203 crypto::RSAPrivateKey* rsa_key, 204 const base::Closure& barrier, 205 scoped_refptr<UsbDevice> device, 206 int interface_id, 207 bool success) { 208 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 209 if (success) { 210 scoped_refptr<UsbConfigDescriptor> config = device->ListInterfaces(); 211 scoped_refptr<UsbDeviceHandle> usb_handle = device->Open(); 212 if (usb_handle) { 213 scoped_refptr<AndroidUsbDevice> android_device = 214 ClaimInterface(rsa_key, usb_handle, config->GetInterface(interface_id), 215 interface_id); 216 if (android_device.get()) 217 devices->push_back(android_device.get()); 218 else 219 usb_handle->Close(); 220 } 221 } 222 barrier.Run(); 223 } 224 225 static int CountOnFileThread() { 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 227 UsbService* service = UsbService::GetInstance(); 228 UsbDevices usb_devices; 229 if (service != NULL) 230 service->GetDevices(&usb_devices); 231 int device_count = 0; 232 for (UsbDevices::iterator it = usb_devices.begin(); it != usb_devices.end(); 233 ++it) { 234 scoped_refptr<UsbConfigDescriptor> config = (*it)->ListInterfaces(); 235 if (!config) 236 continue; 237 238 for (size_t j = 0; j < config->GetNumInterfaces(); ++j) { 239 if (IsAndroidInterface(config->GetInterface(j))) 240 ++device_count; 241 } 242 } 243 return device_count; 244 } 245 246 static void EnumerateOnFileThread( 247 crypto::RSAPrivateKey* rsa_key, 248 const AndroidUsbDevicesCallback& callback, 249 scoped_refptr<base::MessageLoopProxy> caller_message_loop_proxy) { 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 251 252 UsbService* service = UsbService::GetInstance(); 253 UsbDevices usb_devices; 254 if (service != NULL) 255 service->GetDevices(&usb_devices); 256 257 // Add new devices. 258 AndroidUsbDevices* devices = new AndroidUsbDevices(); 259 base::Closure barrier = base::BarrierClosure( 260 usb_devices.size(), base::Bind(&RespondOnFileThread, 261 callback, 262 devices, 263 caller_message_loop_proxy)); 264 265 for (UsbDevices::iterator it = usb_devices.begin(); it != usb_devices.end(); 266 ++it) { 267 scoped_refptr<UsbConfigDescriptor> config = (*it)->ListInterfaces(); 268 if (!config) { 269 barrier.Run(); 270 continue; 271 } 272 273 bool has_android_interface = false; 274 for (size_t j = 0; j < config->GetNumInterfaces(); ++j) { 275 if (!IsAndroidInterface(config->GetInterface(j))) 276 continue; 277 278 // Request permission on Chrome OS. 279 #if defined(OS_CHROMEOS) 280 (*it)->RequestUsbAcess(j, base::Bind(&OpenAndroidDeviceOnFileThread, 281 devices, rsa_key, barrier, *it, j)); 282 #else 283 OpenAndroidDeviceOnFileThread(devices, rsa_key, barrier, *it, j, true); 284 #endif // defined(OS_CHROMEOS) 285 286 has_android_interface = true; 287 break; 288 } 289 if (!has_android_interface) 290 barrier.Run(); 291 } 292 } 293 294 // static 295 void AndroidUsbDevice::CountDevices( 296 const base::Callback<void(int)>& callback) { 297 BrowserThread::PostTaskAndReplyWithResult( 298 BrowserThread::FILE, 299 FROM_HERE, 300 base::Bind(&CountOnFileThread), 301 callback); 302 } 303 304 // static 305 void AndroidUsbDevice::Enumerate(crypto::RSAPrivateKey* rsa_key, 306 const AndroidUsbDevicesCallback& callback) { 307 308 // Collect devices with closed handles. 309 for (std::vector<AndroidUsbDevice*>::iterator it = g_devices.Get().begin(); 310 it != g_devices.Get().end(); ++it) { 311 if ((*it)->usb_handle_) { 312 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 313 base::Bind(&AndroidUsbDevice::TerminateIfReleased, *it, 314 (*it)->usb_handle_)); 315 } 316 } 317 318 // Then look for the new devices. 319 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE, 320 base::Bind(&EnumerateOnFileThread, rsa_key, callback, 321 base::MessageLoopProxy::current())); 322 } 323 324 AndroidUsbDevice::AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key, 325 scoped_refptr<UsbDeviceHandle> usb_device, 326 const std::string& serial, 327 int inbound_address, 328 int outbound_address, 329 int zero_mask, 330 int interface_id) 331 : message_loop_(NULL), 332 rsa_key_(rsa_key->Copy()), 333 usb_handle_(usb_device), 334 serial_(serial), 335 inbound_address_(inbound_address), 336 outbound_address_(outbound_address), 337 zero_mask_(zero_mask), 338 interface_id_(interface_id), 339 is_connected_(false), 340 signature_sent_(false), 341 last_socket_id_(256), 342 weak_factory_(this) { 343 } 344 345 void AndroidUsbDevice::InitOnCallerThread() { 346 if (message_loop_) 347 return; 348 message_loop_ = base::MessageLoop::current(); 349 Queue(new AdbMessage(AdbMessage::kCommandCNXN, kVersion, kMaxPayload, 350 kHostConnectMessage)); 351 ReadHeader(); 352 } 353 354 net::StreamSocket* AndroidUsbDevice::CreateSocket(const std::string& command) { 355 if (!usb_handle_) 356 return NULL; 357 358 uint32 socket_id = ++last_socket_id_; 359 sockets_[socket_id] = new AndroidUsbSocket(this, socket_id, command, 360 base::Bind(&AndroidUsbDevice::SocketDeleted, this)); 361 return sockets_[socket_id]; 362 } 363 364 void AndroidUsbDevice::Send(uint32 command, 365 uint32 arg0, 366 uint32 arg1, 367 const std::string& body) { 368 scoped_refptr<AdbMessage> m = new AdbMessage(command, arg0, arg1, body); 369 // Delay open request if not yet connected. 370 if (!is_connected_) { 371 pending_messages_.push_back(m); 372 return; 373 } 374 Queue(m); 375 } 376 377 AndroidUsbDevice::~AndroidUsbDevice() { 378 DCHECK(message_loop_ == base::MessageLoop::current()); 379 Terminate(); 380 } 381 382 void AndroidUsbDevice::Queue(scoped_refptr<AdbMessage> message) { 383 DCHECK(message_loop_ == base::MessageLoop::current()); 384 385 // Queue header. 386 std::vector<uint32> header; 387 header.push_back(message->command); 388 header.push_back(message->arg0); 389 header.push_back(message->arg1); 390 bool append_zero = true; 391 if (message->body.empty()) 392 append_zero = false; 393 if (message->command == AdbMessage::kCommandAUTH && 394 message->arg0 == AdbMessage::kAuthSignature) 395 append_zero = false; 396 if (message->command == AdbMessage::kCommandWRTE) 397 append_zero = false; 398 399 size_t body_length = message->body.length() + (append_zero ? 1 : 0); 400 header.push_back(body_length); 401 header.push_back(Checksum(message->body)); 402 header.push_back(message->command ^ 0xffffffff); 403 scoped_refptr<net::IOBufferWithSize> header_buffer = 404 new net::IOBufferWithSize(kHeaderSize); 405 memcpy(header_buffer.get()->data(), &header[0], kHeaderSize); 406 outgoing_queue_.push(header_buffer); 407 408 // Queue body. 409 if (!message->body.empty()) { 410 scoped_refptr<net::IOBufferWithSize> body_buffer = 411 new net::IOBufferWithSize(body_length); 412 memcpy(body_buffer->data(), message->body.data(), message->body.length()); 413 if (append_zero) 414 body_buffer->data()[body_length - 1] = 0; 415 outgoing_queue_.push(body_buffer); 416 if (zero_mask_ && (body_length & zero_mask_) == 0) { 417 // Send a zero length packet. 418 outgoing_queue_.push(new net::IOBufferWithSize(0)); 419 } 420 } 421 ProcessOutgoing(); 422 } 423 424 void AndroidUsbDevice::ProcessOutgoing() { 425 DCHECK(message_loop_ == base::MessageLoop::current()); 426 427 if (outgoing_queue_.empty() || !usb_handle_) 428 return; 429 430 BulkMessage message = outgoing_queue_.front(); 431 outgoing_queue_.pop(); 432 DumpMessage(true, message->data(), message->size()); 433 usb_handle_->BulkTransfer( 434 usb_service::USB_DIRECTION_OUTBOUND, outbound_address_, 435 message, message->size(), kUsbTimeout, 436 base::Bind(&AndroidUsbDevice::OutgoingMessageSent, 437 weak_factory_.GetWeakPtr())); 438 } 439 440 void AndroidUsbDevice::OutgoingMessageSent(UsbTransferStatus status, 441 scoped_refptr<net::IOBuffer> buffer, 442 size_t result) { 443 DCHECK(message_loop_ == base::MessageLoop::current()); 444 445 if (status != usb_service::USB_TRANSFER_COMPLETED) 446 return; 447 message_loop_->PostTask(FROM_HERE, 448 base::Bind(&AndroidUsbDevice::ProcessOutgoing, this)); 449 } 450 451 void AndroidUsbDevice::ReadHeader() { 452 DCHECK(message_loop_ == base::MessageLoop::current()); 453 454 if (!usb_handle_) 455 return; 456 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kHeaderSize); 457 usb_handle_->BulkTransfer( 458 usb_service::USB_DIRECTION_INBOUND, inbound_address_, 459 buffer, kHeaderSize, kUsbTimeout, 460 base::Bind(&AndroidUsbDevice::ParseHeader, 461 weak_factory_.GetWeakPtr())); 462 } 463 464 void AndroidUsbDevice::ParseHeader(UsbTransferStatus status, 465 scoped_refptr<net::IOBuffer> buffer, 466 size_t result) { 467 DCHECK(message_loop_ == base::MessageLoop::current()); 468 469 if (status == usb_service::USB_TRANSFER_TIMEOUT) { 470 message_loop_->PostTask(FROM_HERE, 471 base::Bind(&AndroidUsbDevice::ReadHeader, this)); 472 return; 473 } 474 475 if (status != usb_service::USB_TRANSFER_COMPLETED || result != kHeaderSize) { 476 TransferError(status); 477 return; 478 } 479 480 DumpMessage(false, buffer->data(), result); 481 std::vector<uint32> header(6); 482 memcpy(&header[0], buffer->data(), result); 483 scoped_refptr<AdbMessage> message = 484 new AdbMessage(header[0], header[1], header[2], ""); 485 uint32 data_length = header[3]; 486 uint32 data_check = header[4]; 487 uint32 magic = header[5]; 488 if ((message->command ^ 0xffffffff) != magic) { 489 TransferError(usb_service::USB_TRANSFER_ERROR); 490 return; 491 } 492 493 if (data_length == 0) { 494 message_loop_->PostTask(FROM_HERE, 495 base::Bind(&AndroidUsbDevice::HandleIncoming, this, 496 message)); 497 return; 498 } 499 500 message_loop_->PostTask(FROM_HERE, 501 base::Bind(&AndroidUsbDevice::ReadBody, this, 502 message, data_length, data_check)); 503 } 504 505 void AndroidUsbDevice::ReadBody(scoped_refptr<AdbMessage> message, 506 uint32 data_length, 507 uint32 data_check) { 508 DCHECK(message_loop_ == base::MessageLoop::current()); 509 510 if (!usb_handle_) 511 return; 512 scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_length); 513 usb_handle_->BulkTransfer( 514 usb_service::USB_DIRECTION_INBOUND, inbound_address_, 515 buffer, data_length, kUsbTimeout, 516 base::Bind(&AndroidUsbDevice::ParseBody, weak_factory_.GetWeakPtr(), 517 message, data_length, data_check)); 518 } 519 520 void AndroidUsbDevice::ParseBody(scoped_refptr<AdbMessage> message, 521 uint32 data_length, 522 uint32 data_check, 523 UsbTransferStatus status, 524 scoped_refptr<net::IOBuffer> buffer, 525 size_t result) { 526 DCHECK(message_loop_ == base::MessageLoop::current()); 527 528 if (status == usb_service::USB_TRANSFER_TIMEOUT) { 529 message_loop_->PostTask(FROM_HERE, 530 base::Bind(&AndroidUsbDevice::ReadBody, this, 531 message, data_length, data_check)); 532 return; 533 } 534 535 if (status != usb_service::USB_TRANSFER_COMPLETED || 536 static_cast<uint32>(result) != data_length) { 537 TransferError(status); 538 return; 539 } 540 541 DumpMessage(false, buffer->data(), data_length); 542 message->body = std::string(buffer->data(), result); 543 if (Checksum(message->body) != data_check) { 544 TransferError(usb_service::USB_TRANSFER_ERROR); 545 return; 546 } 547 548 message_loop_->PostTask(FROM_HERE, 549 base::Bind(&AndroidUsbDevice::HandleIncoming, this, 550 message)); 551 } 552 553 void AndroidUsbDevice::HandleIncoming(scoped_refptr<AdbMessage> message) { 554 DCHECK(message_loop_ == base::MessageLoop::current()); 555 556 switch (message->command) { 557 case AdbMessage::kCommandAUTH: 558 { 559 DCHECK_EQ(message->arg0, static_cast<uint32>(AdbMessage::kAuthToken)); 560 if (signature_sent_) { 561 Queue(new AdbMessage(AdbMessage::kCommandAUTH, 562 AdbMessage::kAuthRSAPublicKey, 0, 563 AndroidRSAPublicKey(rsa_key_.get()))); 564 } else { 565 signature_sent_ = true; 566 std::string signature = AndroidRSASign(rsa_key_.get(), message->body); 567 if (!signature.empty()) { 568 Queue(new AdbMessage(AdbMessage::kCommandAUTH, 569 AdbMessage::kAuthSignature, 0, 570 signature)); 571 } else { 572 Queue(new AdbMessage(AdbMessage::kCommandAUTH, 573 AdbMessage::kAuthRSAPublicKey, 0, 574 AndroidRSAPublicKey(rsa_key_.get()))); 575 } 576 } 577 } 578 break; 579 case AdbMessage::kCommandCNXN: 580 { 581 is_connected_ = true; 582 PendingMessages pending; 583 pending.swap(pending_messages_); 584 for (PendingMessages::iterator it = pending.begin(); 585 it != pending.end(); ++it) { 586 Queue(*it); 587 } 588 } 589 break; 590 case AdbMessage::kCommandOKAY: 591 case AdbMessage::kCommandWRTE: 592 case AdbMessage::kCommandCLSE: 593 { 594 AndroidUsbSockets::iterator it = sockets_.find(message->arg1); 595 if (it != sockets_.end()) 596 it->second->HandleIncoming(message); 597 } 598 break; 599 default: 600 break; 601 } 602 ReadHeader(); 603 } 604 605 void AndroidUsbDevice::TransferError(UsbTransferStatus status) { 606 DCHECK(message_loop_ == base::MessageLoop::current()); 607 608 message_loop_->PostTask(FROM_HERE, 609 base::Bind(&AndroidUsbDevice::Terminate, this)); 610 } 611 612 void AndroidUsbDevice::TerminateIfReleased( 613 scoped_refptr<UsbDeviceHandle> usb_handle) { 614 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 615 if (usb_handle->GetDevice()) 616 return; 617 message_loop_->PostTask(FROM_HERE, 618 base::Bind(&AndroidUsbDevice::Terminate, this)); 619 } 620 621 void AndroidUsbDevice::Terminate() { 622 DCHECK(message_loop_ == base::MessageLoop::current()); 623 624 std::vector<AndroidUsbDevice*>::iterator it = 625 std::find(g_devices.Get().begin(), g_devices.Get().end(), this); 626 if (it != g_devices.Get().end()) 627 g_devices.Get().erase(it); 628 629 if (!usb_handle_) 630 return; 631 632 // Make sure we zero-out handle so that closing connections did not open 633 // new connections. 634 scoped_refptr<UsbDeviceHandle> usb_handle = usb_handle_; 635 usb_handle_ = NULL; 636 637 // Iterate over copy. 638 AndroidUsbSockets sockets(sockets_); 639 for (AndroidUsbSockets::iterator it = sockets.begin(); 640 it != sockets.end(); ++it) { 641 it->second->Terminated(); 642 } 643 DCHECK(sockets_.empty()); 644 645 BrowserThread::PostTask( 646 BrowserThread::FILE, FROM_HERE, 647 base::Bind(&ReleaseInterface, usb_handle, interface_id_)); 648 } 649 650 void AndroidUsbDevice::SocketDeleted(uint32 socket_id) { 651 DCHECK(message_loop_ == base::MessageLoop::current()); 652 653 sockets_.erase(socket_id); 654 } 655