Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(148)

Unified Diff: chrome/browser/devtools/device/usb/android_usb_device.cc

Issue 980023002: Move device/usb classes from the FILE thread to UI thread. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Add more thread assertions. Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/devtools/device/usb/android_usb_device.cc
diff --git a/chrome/browser/devtools/device/usb/android_usb_device.cc b/chrome/browser/devtools/device/usb/android_usb_device.cc
index 9e5d700e885f5df9e14a128a9a87be722b508721..5da44582cd671f4654ea93e7be423b9702af53eb 100644
--- a/chrome/browser/devtools/device/usb/android_usb_device.cc
+++ b/chrome/browser/devtools/device/usb/android_usb_device.cc
@@ -9,11 +9,12 @@
#include "base/barrier_closure.h"
#include "base/base64.h"
#include "base/lazy_instance.h"
-#include "base/message_loop/message_loop.h"
+#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
+#include "base/thread_task_runner_handle.h"
#include "chrome/browser/devtools/device/usb/android_rsa.h"
#include "chrome/browser/devtools/device/usb/android_usb_socket.h"
#include "content/public/browser/browser_thread.h"
@@ -69,38 +70,22 @@ bool IsAndroidInterface(const UsbInterfaceDescriptor& interface) {
return true;
}
-scoped_refptr<AndroidUsbDevice> ClaimInterface(
- crypto::RSAPrivateKey* rsa_key,
- scoped_refptr<UsbDeviceHandle> usb_handle,
- const base::string16& serial,
- const UsbInterfaceDescriptor& interface) {
- int inbound_address = 0;
- int outbound_address = 0;
- int zero_mask = 0;
-
- for (const UsbEndpointDescriptor& endpoint : interface.endpoints) {
- if (endpoint.transfer_type != device::USB_TRANSFER_BULK)
- continue;
- if (endpoint.direction == device::USB_DIRECTION_INBOUND)
- inbound_address = endpoint.address;
- else
- outbound_address = endpoint.address;
- zero_mask = endpoint.maximum_packet_size - 1;
+void CountAndroidDevices(const base::Callback<void(int)>& callback,
+ const UsbDevices& devices) {
+ int device_count = 0;
+ for (const scoped_refptr<UsbDevice>& device : devices) {
+ const UsbConfigDescriptor* config = device->GetConfiguration();
+ if (config) {
+ for (const UsbInterfaceDescriptor& iface : config->interfaces) {
+ if (IsAndroidInterface(iface)) {
+ ++device_count;
+ }
+ }
+ }
}
- if (inbound_address == 0 || outbound_address == 0)
- return NULL;
-
- if (!usb_handle->ClaimInterface(interface.interface_number))
- return NULL;
-
- return new AndroidUsbDevice(rsa_key,
- usb_handle,
- base::UTF16ToASCII(serial),
- inbound_address,
- outbound_address,
- zero_mask,
- interface.interface_number);
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(callback, device_count));
}
uint32 Checksum(const std::string& data) {
@@ -147,23 +132,8 @@ void ReleaseInterface(scoped_refptr<UsbDeviceHandle> usb_device,
usb_device->Close();
}
-} // namespace
-
-AdbMessage::AdbMessage(uint32 command,
- uint32 arg0,
- uint32 arg1,
- const std::string& body)
- : command(command),
- arg0(arg0),
- arg1(arg1),
- body(body) {
-}
-
-AdbMessage::~AdbMessage() {
-}
-
-static void RespondOnCallerThread(const AndroidUsbDevicesCallback& callback,
- AndroidUsbDevices* new_devices) {
+void RespondOnCallerThread(const AndroidUsbDevicesCallback& callback,
+ AndroidUsbDevices* new_devices) {
scoped_ptr<AndroidUsbDevices> devices(new_devices);
// Add raw pointers to the newly claimed devices.
@@ -176,82 +146,115 @@ static void RespondOnCallerThread(const AndroidUsbDevicesCallback& callback,
callback.Run(result);
}
-static void RespondOnFileThread(
+void RespondOnUIThread(
const AndroidUsbDevicesCallback& callback,
AndroidUsbDevices* devices,
- scoped_refptr<base::MessageLoopProxy> caller_message_loop_proxy) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- caller_message_loop_proxy->PostTask(
- FROM_HERE,
- base::Bind(&RespondOnCallerThread, callback, devices));
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ caller_task_runner->PostTask(
+ FROM_HERE, base::Bind(&RespondOnCallerThread, callback, devices));
}
-static void OpenAndroidDeviceOnFileThread(
- AndroidUsbDevices* devices,
- crypto::RSAPrivateKey* rsa_key,
- const base::Closure& barrier,
- scoped_refptr<UsbDevice> device,
- int interface_id,
- bool success) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+void CreateDeviceOnInterfaceClaimed(AndroidUsbDevices* devices,
+ crypto::RSAPrivateKey* rsa_key,
+ scoped_refptr<UsbDeviceHandle> usb_handle,
+ int inbound_address,
+ int outbound_address,
+ int zero_mask,
+ int interface_number,
+ const base::Closure& barrier,
+ bool success) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (success) {
- base::string16 serial;
- if (device->GetSerialNumber(&serial) && !serial.empty()) {
- const UsbConfigDescriptor* config = device->GetConfiguration();
- if (config) {
- scoped_refptr<UsbDeviceHandle> usb_handle = device->Open();
- if (usb_handle.get()) {
- scoped_refptr<AndroidUsbDevice> android_device = ClaimInterface(
- rsa_key, usb_handle, serial, config->interfaces[interface_id]);
- if (android_device.get())
- devices->push_back(android_device);
- else
- usb_handle->Close();
- }
- }
- }
+ devices->push_back(new AndroidUsbDevice(
+ rsa_key, usb_handle,
+ base::UTF16ToASCII(usb_handle->GetDevice()->serial_number()),
+ inbound_address, outbound_address, zero_mask, interface_number));
+ } else {
+ usb_handle->Close();
}
barrier.Run();
}
-static int CountOnFileThread() {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- UsbService* service = device::DeviceClient::Get()->GetUsbService();
- UsbDevices usb_devices;
- if (service != NULL)
- service->GetDevices(&usb_devices);
- int device_count = 0;
- for (const scoped_refptr<UsbDevice>& device : usb_devices) {
- const UsbConfigDescriptor* config = device->GetConfiguration();
- if (config) {
- for (const UsbInterfaceDescriptor& iface : config->interfaces) {
- if (IsAndroidInterface(iface)) {
- ++device_count;
- }
- }
- }
+void OnDeviceOpened(AndroidUsbDevices* devices,
+ crypto::RSAPrivateKey* rsa_key,
+ int inbound_address,
+ int outbound_address,
+ int zero_mask,
+ int interface_number,
+ const base::Closure& barrier,
+ scoped_refptr<UsbDeviceHandle> usb_handle) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (usb_handle.get()) {
+ usb_handle->ClaimInterface(
+ interface_number,
+ base::Bind(&CreateDeviceOnInterfaceClaimed, devices, rsa_key,
+ usb_handle, inbound_address, outbound_address, zero_mask,
+ interface_number, barrier));
+ } else {
+ barrier.Run();
}
- return device_count;
}
-static void EnumerateOnFileThread(
- crypto::RSAPrivateKey* rsa_key,
- const AndroidUsbDevicesCallback& callback,
- scoped_refptr<base::MessageLoopProxy> caller_message_loop_proxy) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
+void OpenAndroidDevice(AndroidUsbDevices* devices,
+ crypto::RSAPrivateKey* rsa_key,
+ const base::Closure& barrier,
+ scoped_refptr<UsbDevice> device,
+ int interface_id,
+ bool success) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (!success) {
+ barrier.Run();
+ return;
+ }
- UsbService* service = device::DeviceClient::Get()->GetUsbService();
- UsbDevices usb_devices;
- if (service != NULL)
- service->GetDevices(&usb_devices);
+ if (device->serial_number().empty()) {
+ barrier.Run();
+ return;
+ }
+
+ const UsbConfigDescriptor* config = device->GetConfiguration();
+ if (!config) {
+ barrier.Run();
+ return;
+ }
+ const UsbInterfaceDescriptor& interface = config->interfaces[interface_id];
+ int inbound_address = 0;
+ int outbound_address = 0;
+ int zero_mask = 0;
+
+ for (const UsbEndpointDescriptor& endpoint : interface.endpoints) {
+ if (endpoint.transfer_type != device::USB_TRANSFER_BULK)
+ continue;
+ if (endpoint.direction == device::USB_DIRECTION_INBOUND)
+ inbound_address = endpoint.address;
+ else
+ outbound_address = endpoint.address;
+ zero_mask = endpoint.maximum_packet_size - 1;
+ }
+
+ if (inbound_address == 0 || outbound_address == 0) {
+ barrier.Run();
+ return;
+ }
+
+ device->Open(base::Bind(&OnDeviceOpened, devices, rsa_key, inbound_address,
+ outbound_address, zero_mask,
+ interface.interface_number, barrier));
+}
+
+void OpenAndroidDevices(
+ crypto::RSAPrivateKey* rsa_key,
+ const AndroidUsbDevicesCallback& callback,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ const UsbDevices& usb_devices) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Add new devices.
AndroidUsbDevices* devices = new AndroidUsbDevices();
base::Closure barrier = base::BarrierClosure(
- usb_devices.size(), base::Bind(&RespondOnFileThread,
- callback,
- devices,
- caller_message_loop_proxy));
+ usb_devices.size(),
+ base::Bind(&RespondOnUIThread, callback, devices, caller_task_runner));
for (const scoped_refptr<UsbDevice>& device : usb_devices) {
const UsbConfigDescriptor* config = device->GetConfiguration();
@@ -265,45 +268,73 @@ static void EnumerateOnFileThread(
continue;
}
- device->RequestUsbAccess(
- j, base::Bind(&OpenAndroidDeviceOnFileThread, devices, rsa_key,
- barrier, device, j));
+ device->RequestUsbAccess(j, base::Bind(&OpenAndroidDevice, devices,
+ rsa_key, barrier, device, j));
has_android_interface = true;
break;
}
- if (!has_android_interface)
+ if (!has_android_interface) {
barrier.Run();
+ }
+ }
+}
+
+void EnumerateOnUIThread(
+ crypto::RSAPrivateKey* rsa_key,
+ const AndroidUsbDevicesCallback& callback,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner) {
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+
+ UsbService* service = device::DeviceClient::Get()->GetUsbService();
+ if (service == NULL) {
+ caller_task_runner->PostTask(FROM_HERE,
+ base::Bind(callback, AndroidUsbDevices()));
+ } else {
+ service->GetDevices(
+ base::Bind(&OpenAndroidDevices, rsa_key, callback, caller_task_runner));
}
}
+} // namespace
+
+AdbMessage::AdbMessage(uint32 command,
+ uint32 arg0,
+ uint32 arg1,
+ const std::string& body)
+ : command(command), arg0(arg0), arg1(arg1), body(body) {
+}
+
+AdbMessage::~AdbMessage() {
+}
+
// static
-void AndroidUsbDevice::CountDevices(
- const base::Callback<void(int)>& callback) {
- BrowserThread::PostTaskAndReplyWithResult(
- BrowserThread::FILE,
- FROM_HERE,
- base::Bind(&CountOnFileThread),
- callback);
+void AndroidUsbDevice::CountDevices(const base::Callback<void(int)>& callback) {
+ UsbService* service = device::DeviceClient::Get()->GetUsbService();
+ if (service != NULL) {
+ service->GetDevices(base::Bind(&CountAndroidDevices, callback));
+ } else {
+ base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
+ base::Bind(callback, 0));
+ }
}
// static
void AndroidUsbDevice::Enumerate(crypto::RSAPrivateKey* rsa_key,
const AndroidUsbDevicesCallback& callback) {
-
// Collect devices with closed handles.
for (AndroidUsbDevice* device : g_devices.Get()) {
if (device->usb_handle_.get()) {
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- base::Bind(&AndroidUsbDevice::TerminateIfReleased, device,
- device->usb_handle_));
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&AndroidUsbDevice::TerminateIfReleased,
+ device, device->usb_handle_));
}
}
// Then look for the new devices.
- BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
- base::Bind(&EnumerateOnFileThread, rsa_key, callback,
- base::MessageLoopProxy::current()));
+ BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
+ base::Bind(&EnumerateOnUIThread, rsa_key, callback,
+ base::ThreadTaskRunnerHandle::Get()));
}
AndroidUsbDevice::AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key,
@@ -313,8 +344,7 @@ AndroidUsbDevice::AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key,
int outbound_address,
int zero_mask,
int interface_id)
- : message_loop_(NULL),
- rsa_key_(rsa_key->Copy()),
+ : rsa_key_(rsa_key->Copy()),
usb_handle_(usb_device),
serial_(serial),
inbound_address_(inbound_address),
@@ -328,9 +358,9 @@ AndroidUsbDevice::AndroidUsbDevice(crypto::RSAPrivateKey* rsa_key,
}
void AndroidUsbDevice::InitOnCallerThread() {
- if (message_loop_)
+ if (task_runner_)
return;
- message_loop_ = base::MessageLoop::current();
+ task_runner_ = base::ThreadTaskRunnerHandle::Get();
Queue(make_scoped_ptr(new AdbMessage(AdbMessage::kCommandCNXN, kVersion,
kMaxPayload, kHostConnectMessage)));
ReadHeader();
@@ -360,12 +390,12 @@ void AndroidUsbDevice::Send(uint32 command,
}
AndroidUsbDevice::~AndroidUsbDevice() {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
Terminate();
}
void AndroidUsbDevice::Queue(scoped_ptr<AdbMessage> message) {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
// Queue header.
std::vector<uint32> header;
@@ -407,7 +437,7 @@ void AndroidUsbDevice::Queue(scoped_ptr<AdbMessage> message) {
}
void AndroidUsbDevice::ProcessOutgoing() {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
if (outgoing_queue_.empty() || !usb_handle_.get())
return;
@@ -415,11 +445,9 @@ void AndroidUsbDevice::ProcessOutgoing() {
BulkMessage message = outgoing_queue_.front();
outgoing_queue_.pop();
DumpMessage(true, message->data(), message->size());
- usb_handle_->BulkTransfer(device::USB_DIRECTION_OUTBOUND,
- outbound_address_,
- message.get(),
- message->size(),
- kUsbTimeout,
+
+ usb_handle_->BulkTransfer(device::USB_DIRECTION_OUTBOUND, outbound_address_,
+ message, message->size(), kUsbTimeout,
base::Bind(&AndroidUsbDevice::OutgoingMessageSent,
weak_factory_.GetWeakPtr()));
}
@@ -427,25 +455,24 @@ void AndroidUsbDevice::ProcessOutgoing() {
void AndroidUsbDevice::OutgoingMessageSent(UsbTransferStatus status,
scoped_refptr<net::IOBuffer> buffer,
size_t result) {
- DCHECK(message_loop_ == base::MessageLoop::current());
-
- if (status != device::USB_TRANSFER_COMPLETED)
+ if (status != device::USB_TRANSFER_COMPLETED) {
return;
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&AndroidUsbDevice::ProcessOutgoing, this));
+ }
+
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&AndroidUsbDevice::ProcessOutgoing, this));
}
void AndroidUsbDevice::ReadHeader() {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
- if (!usb_handle_.get())
+ if (!usb_handle_.get()) {
return;
+ }
+
scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kHeaderSize);
usb_handle_->BulkTransfer(
- device::USB_DIRECTION_INBOUND,
- inbound_address_,
- buffer.get(),
- kHeaderSize,
+ device::USB_DIRECTION_INBOUND, inbound_address_, buffer, kHeaderSize,
kUsbTimeout,
base::Bind(&AndroidUsbDevice::ParseHeader, weak_factory_.GetWeakPtr()));
}
@@ -453,11 +480,11 @@ void AndroidUsbDevice::ReadHeader() {
void AndroidUsbDevice::ParseHeader(UsbTransferStatus status,
scoped_refptr<net::IOBuffer> buffer,
size_t result) {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
if (status == device::USB_TRANSFER_TIMEOUT) {
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&AndroidUsbDevice::ReadHeader, this));
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&AndroidUsbDevice::ReadHeader, this));
return;
}
@@ -480,35 +507,31 @@ void AndroidUsbDevice::ParseHeader(UsbTransferStatus status,
}
if (data_length == 0) {
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&AndroidUsbDevice::HandleIncoming, this,
- base::Passed(&message)));
- return;
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&AndroidUsbDevice::HandleIncoming, this,
+ base::Passed(&message)));
+ } else {
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&AndroidUsbDevice::ReadBody, this,
+ base::Passed(&message), data_length, data_check));
}
-
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&AndroidUsbDevice::ReadBody, this,
- base::Passed(&message), data_length, data_check));
}
void AndroidUsbDevice::ReadBody(scoped_ptr<AdbMessage> message,
uint32 data_length,
uint32 data_check) {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
- if (!usb_handle_.get())
+ if (!usb_handle_.get()) {
return;
+ }
+
scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(data_length);
- usb_handle_->BulkTransfer(device::USB_DIRECTION_INBOUND,
- inbound_address_,
- buffer.get(),
- data_length,
- kUsbTimeout,
- base::Bind(&AndroidUsbDevice::ParseBody,
- weak_factory_.GetWeakPtr(),
- base::Passed(&message),
- data_length,
- data_check));
+ usb_handle_->BulkTransfer(
+ device::USB_DIRECTION_INBOUND, inbound_address_, buffer, data_length,
+ kUsbTimeout,
+ base::Bind(&AndroidUsbDevice::ParseBody, weak_factory_.GetWeakPtr(),
+ base::Passed(&message), data_length, data_check));
}
void AndroidUsbDevice::ParseBody(scoped_ptr<AdbMessage> message,
@@ -517,12 +540,12 @@ void AndroidUsbDevice::ParseBody(scoped_ptr<AdbMessage> message,
UsbTransferStatus status,
scoped_refptr<net::IOBuffer> buffer,
size_t result) {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
if (status == device::USB_TRANSFER_TIMEOUT) {
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&AndroidUsbDevice::ReadBody, this,
- base::Passed(&message), data_length, data_check));
+ task_runner_->PostTask(
+ FROM_HERE, base::Bind(&AndroidUsbDevice::ReadBody, this,
+ base::Passed(&message), data_length, data_check));
return;
}
@@ -539,13 +562,13 @@ void AndroidUsbDevice::ParseBody(scoped_ptr<AdbMessage> message,
return;
}
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&AndroidUsbDevice::HandleIncoming, this,
- base::Passed(&message)));
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&AndroidUsbDevice::HandleIncoming, this,
+ base::Passed(&message)));
}
void AndroidUsbDevice::HandleIncoming(scoped_ptr<AdbMessage> message) {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
switch (message->command) {
case AdbMessage::kCommandAUTH:
@@ -599,23 +622,24 @@ void AndroidUsbDevice::HandleIncoming(scoped_ptr<AdbMessage> message) {
}
void AndroidUsbDevice::TransferError(UsbTransferStatus status) {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&AndroidUsbDevice::Terminate, this));
+ Terminate();
}
void AndroidUsbDevice::TerminateIfReleased(
scoped_refptr<UsbDeviceHandle> usb_handle) {
- DCHECK_CURRENTLY_ON(BrowserThread::FILE);
- if (usb_handle->GetDevice().get())
+ DCHECK_CURRENTLY_ON(BrowserThread::UI);
+ if (usb_handle->GetDevice().get()) {
return;
- message_loop_->PostTask(FROM_HERE,
- base::Bind(&AndroidUsbDevice::Terminate, this));
+ }
+
+ task_runner_->PostTask(FROM_HERE,
+ base::Bind(&AndroidUsbDevice::Terminate, this));
}
void AndroidUsbDevice::Terminate() {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
std::vector<AndroidUsbDevice*>::iterator it =
std::find(g_devices.Get().begin(), g_devices.Get().end(), this);
@@ -639,12 +663,12 @@ void AndroidUsbDevice::Terminate() {
DCHECK(sockets_.empty());
BrowserThread::PostTask(
- BrowserThread::FILE, FROM_HERE,
+ BrowserThread::UI, FROM_HERE,
base::Bind(&ReleaseInterface, usb_handle, interface_id_));
}
void AndroidUsbDevice::SocketDeleted(uint32 socket_id) {
- DCHECK(message_loop_ == base::MessageLoop::current());
+ DCHECK(task_runner_->BelongsToCurrentThread());
sockets_.erase(socket_id);
}

Powered by Google App Engine
This is Rietveld 408576698