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

Unified Diff: chromeos/dbus/mtpd_client.cc

Issue 10825170: chromeos: Add dbus MTPDClient. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 8 years, 4 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
« chromeos/dbus/mtpd_client.h ('K') | « chromeos/dbus/mtpd_client.h ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chromeos/dbus/mtpd_client.cc
===================================================================
--- chromeos/dbus/mtpd_client.cc (revision 0)
+++ chromeos/dbus/mtpd_client.cc (revision 0)
@@ -0,0 +1,581 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/dbus/mtpd_client.h"
+
+#include <map>
+
+#include "base/bind.h"
+#include "base/stl_util.h"
+#include "base/stringprintf.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+
+// Pops a string value when |reader| is not NULL.
+// Returns true when a value is popped, false otherwise.
+bool MaybePopString(dbus::MessageReader* reader, std::string* value) {
+ if (!reader)
+ return false;
+ return reader->PopString(value);
+}
+
+// Pops a uint16 value when |reader| is not NULL.
+// Returns true when a value is popped, false otherwise.
+bool MaybePopUint16(dbus::MessageReader* reader, uint16* value) {
+ if (!reader)
+ return false;
+ return reader->PopUint16(value);
+}
+
+// Pops a uint32 value when |reader| is not NULL.
+// Returns true when a value is popped, false otherwise.
+bool MaybePopUint32(dbus::MessageReader* reader, uint32* value) {
+ if (!reader)
+ return false;
+ return reader->PopUint32(value);
+}
+
+// Pops a uint64 value when |reader| is not NULL.
+// Returns true when a value is popped, false otherwise.
+bool MaybePopUint64(dbus::MessageReader* reader, uint64* value) {
+ if (!reader)
+ return false;
+ return reader->PopUint64(value);
+}
+
+// Pops a int64 value when |reader| is not NULL.
+// Returns true when a value is popped, false otherwise.
+bool MaybePopInt64(dbus::MessageReader* reader, int64* value) {
+ if (!reader)
+ return false;
+ return reader->PopInt64(value);
+}
+
+// The MTPDClient implementation.
+class MTPDClientImpl : public MTPDClient {
+ public:
+ // TODO(thestig) Use mtpd::kMTPDServiceName and mtpd::kMTPDServicePath below.
+ explicit MTPDClientImpl(dbus::Bus* bus)
+ : proxy_(bus->GetObjectProxy(
+ "org.chromium.MTPD",
satorux1 2012/08/03 05:26:07 Constants like this should be defined in service_c
Lei Zhang 2012/08/03 06:29:30 I should have it fixed tomorrow once I get the con
+ dbus::ObjectPath("/org/chromium/MTPD"))),
+ weak_ptr_factory_(this) {
+ }
+
+ // MTPDClient override.
+ virtual void EnumerateStorage(const EnumerateStorageCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ // TODO(thestig) Use constants here.
+ dbus::MethodCall method_call("org.chromium.MTPD", "EnumerateStorage");
+ proxy_->CallMethod(
+ &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&MTPDClientImpl::OnEnumerateStorage,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+ }
+
+ // MTPDClient override.
+ virtual void GetStorageInfo(const std::string& storage_name,
+ const GetStorageInfoCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ // TODO(thestig) Use constants here.
+ dbus::MethodCall method_call("org.chromium.MTPD", "GetStorageInfo");
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString(storage_name);
+ proxy_->CallMethod(&method_call,
+ dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&MTPDClientImpl::OnGetStorageInfo,
+ weak_ptr_factory_.GetWeakPtr(),
+ storage_name,
+ callback,
+ error_callback));
+ }
+
+ // MTPDClient override.
+ virtual void OpenStorage(const std::string& storage_name,
+ OpenStorageMode mode,
+ const OpenStorageCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ // TODO(thestig) Use constants here.
+ dbus::MethodCall method_call("org.chromium.MTPD", "OpenStorage");
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString(storage_name);
+ DCHECK_EQ(OPEN_STORAGE_MODE_READ_ONLY, mode);
+ // TODO(thestig) Use constants here.
+ writer.AppendString("readonly");
+ proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&MTPDClientImpl::OnOpenStorage,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+ }
+
+ // MTPDClient override.
+ virtual void CloseStorage(const std::string& handle,
+ const CloseStorageCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ // TODO(thestig) Use constants here.
+ dbus::MethodCall method_call("org.chromium.MTPD", "CloseStorage");
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString(handle);
+ proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&MTPDClientImpl::OnCloseStorage,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+ }
+
+ virtual void ReadDirectoryByPath(
satorux1 2012/08/03 05:26:07 // MTPDClient override.
Lei Zhang 2012/08/03 06:29:30 Done.
+ const std::string& handle,
+ const std::string& path,
+ const ReadDirectoryCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ // TODO(thestig) Use constants here.
+ dbus::MethodCall method_call("org.chromium.MTPD", "ReadDirectoryByPath");
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString(handle);
+ writer.AppendString(path);
+ proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&MTPDClientImpl::OnReadDirectory,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+ }
+
+ virtual void ReadDirectoryById(
satorux1 2012/08/03 05:26:07 // MTPDClient override.
Lei Zhang 2012/08/03 06:29:30 Done.
+ const std::string& handle,
+ uint32 file_id,
+ const ReadDirectoryCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ // TODO(thestig) Use constants here.
+ dbus::MethodCall method_call("org.chromium.MTPD", "ReadDirectoryById");
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString(handle);
+ writer.AppendUint32(file_id);
+ proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&MTPDClientImpl::OnReadDirectory,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+ }
+
+ // MTPDClient override.
+ virtual void ReadFileByPath(const std::string& handle,
+ const std::string& path,
+ const ReadFileCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ // TODO(thestig) Use constants here.
+ dbus::MethodCall method_call("org.chromium.MTPD", "ReadFileByPath");
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString(handle);
+ writer.AppendString(path);
+ proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&MTPDClientImpl::OnReadFile,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+ }
+
+ // MTPDClient override.
+ virtual void ReadFileById(const std::string& handle,
+ uint32 file_id,
+ const ReadFileCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {
+ // TODO(thestig) Use constants here.
+ dbus::MethodCall method_call("org.chromium.MTPD", "ReadFileById");
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendString(handle);
+ writer.AppendUint32(file_id);
+ proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ base::Bind(&MTPDClientImpl::OnReadFile,
+ weak_ptr_factory_.GetWeakPtr(),
+ callback,
+ error_callback));
+ }
+
+ // MTPDClient override.
+ virtual void SetUpConnections(
+ const MTPStorageEventHandler& handler) OVERRIDE {
+ static const SignalEventTuple kSignalEventTuples[] = {
+ // TODO(thestig) Use constants.
+ { "MTPStorageAttached", true },
+ { "MTPStorageDetached", false },
+ };
+ const size_t kNumSignalEventTuples = arraysize(kSignalEventTuples);
+
+ for (size_t i = 0; i < kNumSignalEventTuples; ++i) {
+ // TODO(thestig) Use mtpd::kMTPDInterface here.
+ proxy_->ConnectToSignal(
+ "org.chromium.MTPD",
+ kSignalEventTuples[i].signal_name,
+ base::Bind(&MTPDClientImpl::OnMTPStorageSignal,
+ weak_ptr_factory_.GetWeakPtr(),
+ handler,
+ kSignalEventTuples[i].is_attach),
+ base::Bind(&MTPDClientImpl::OnSignalConnected,
+ weak_ptr_factory_.GetWeakPtr()));
+ }
+ }
+
+ private:
+ // A struct to contain a pair of signal name and attachment event type.
+ // Used by SetUpConnections.
+ struct SignalEventTuple {
+ const char *signal_name;
+ bool is_attach;
+ };
+
+ // Handles the result of EnumerateStorage and calls |callback| or
+ // |error_callback|.
+ void OnEnumerateStorage(const EnumerateStorageCallback& callback,
+ const ErrorCallback& error_callback,
+ dbus::Response* response) {
+ if (!response) {
+ error_callback.Run();
+ return;
+ }
+ dbus::MessageReader reader(response);
+ std::vector<std::string> storage_names;
+ if (!reader.PopArrayOfStrings(&storage_names)) {
+ LOG(ERROR) << "Invalid response: " << response->ToString();
+ error_callback.Run();
+ return;
+ }
+ callback.Run(storage_names);
+ }
+
+ // Handles the result of GetStorageInfo and calls |callback| or
+ // |error_callback|.
+ void OnGetStorageInfo(const std::string& storage_name,
+ const GetStorageInfoCallback& callback,
+ const ErrorCallback& error_callback,
+ dbus::Response* response) {
+ if (!response) {
+ error_callback.Run();
+ return;
+ }
+ StorageInfo storage_info(storage_name, response);
+ callback.Run(storage_info);
+ }
+
+ // Handles the result of OpenStorage and calls |callback| or |error_callback|.
+ void OnOpenStorage(const OpenStorageCallback& callback,
+ const ErrorCallback& error_callback,
+ dbus::Response* response) {
+ if (!response) {
+ error_callback.Run();
+ return;
+ }
+ dbus::MessageReader reader(response);
+ std::string handle;
+ if (!reader.PopString(&handle)) {
+ LOG(ERROR) << "Invalid response: " << response->ToString();
+ error_callback.Run();
+ return;
+ }
+ callback.Run(handle);
+ }
+
+ // Handles the result of CloseStorage and calls |callback| or
+ // |error_callback|.
+ void OnCloseStorage(const CloseStorageCallback& callback,
+ const ErrorCallback& error_callback,
+ dbus::Response* response) {
+ if (!response) {
+ error_callback.Run();
+ return;
+ }
+ callback.Run();
+ }
+
+ // Handles the result of ReadDirectoryByPath/Id and calls |callback| or
+ // |error_callback|.
+ void OnReadDirectory(const ReadDirectoryCallback& callback,
+ const ErrorCallback& error_callback,
+ dbus::Response* response) {
+ if (!response) {
+ error_callback.Run();
+ return;
+ }
+
+ std::vector<FileEntry> file_entries;
+ dbus::MessageReader response_reader(response);
+ dbus::MessageReader array_reader(response);
+ if (!response_reader.PopArray(&array_reader)) {
+ LOG(ERROR) << "Invalid response: " << response->ToString();
+ error_callback.Run();
+ return;
+ }
+ while (array_reader.HasMoreData()) {
+ FileEntry entry(response);
+ file_entries.push_back(entry);
+ }
+ callback.Run(file_entries);
+ }
+
+ // Handles the result of ReadFileByPath/Id and calls |callback| or
+ // |error_callback|.
+ void OnReadFile(const ReadFileCallback& callback,
+ const ErrorCallback& error_callback,
+ dbus::Response* response) {
+ if (!response) {
+ error_callback.Run();
+ return;
+ }
+
+ uint8* data_bytes = NULL;
+ size_t data_length = 0;
+ dbus::MessageReader reader(response);
+ if (!reader.PopArrayOfBytes(&data_bytes, &data_length)) {
+ error_callback.Run();
+ return;
+ }
+ std::string data(reinterpret_cast<const char*>(data_bytes), data_length);
+ callback.Run(data);
+ }
+
+ // Handles MTPStorageAttached/Dettached signals and calls |handler|.
+ void OnMTPStorageSignal(MTPStorageEventHandler handler,
+ bool is_attach,
+ dbus::Signal* signal) {
+ dbus::MessageReader reader(signal);
+ std::string storage_name;
+ if (!reader.PopString(&storage_name)) {
+ LOG(ERROR) << "Invalid signal: " << signal->ToString();
+ return;
+ }
+ DCHECK(!storage_name.empty());
+ handler.Run(is_attach, storage_name);
+ }
+
+
+ // Handles the result of signal connection setup.
+ void OnSignalConnected(const std::string& interface,
+ const std::string& signal,
+ bool successed) {
+ LOG_IF(ERROR, !successed) << "Connect to " << interface << " "
+ << signal << " failed.";
+ }
+
+ dbus::ObjectProxy* proxy_;
+ base::WeakPtrFactory<MTPDClientImpl> weak_ptr_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(MTPDClientImpl);
+};
+
+// A stub implementaion of MTPDClient.
+class MTPDClientStubImpl : public MTPDClient {
+ public:
+ MTPDClientStubImpl() {}
+ virtual ~MTPDClientStubImpl() {}
+
+ virtual void EnumerateStorage(
+ const EnumerateStorageCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {}
+ virtual void GetStorageInfo(
+ const std::string& storage_name,
+ const GetStorageInfoCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {}
+ virtual void OpenStorage(const std::string& storage_name,
+ OpenStorageMode mode,
+ const OpenStorageCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {}
+ virtual void CloseStorage(const std::string& handle,
+ const CloseStorageCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {}
+ virtual void ReadDirectoryByPath(
+ const std::string& handle,
+ const std::string& path,
+ const ReadDirectoryCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {}
+ virtual void ReadDirectoryById(
+ const std::string& handle,
+ uint32 file_id,
+ const ReadDirectoryCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {}
+ virtual void ReadFileByPath(const std::string& handle,
+ const std::string& path,
+ const ReadFileCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {}
+ virtual void ReadFileById(const std::string& handle,
+ uint32 file_id,
+ const ReadFileCallback& callback,
+ const ErrorCallback& error_callback) OVERRIDE {}
+ virtual void SetUpConnections(
+ const MTPStorageEventHandler& handler) OVERRIDE {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MTPDClientStubImpl);
+};
+
+} // namespace
+
+////////////////////////////////////////////////////////////////////////////////
+// StorageInfo
+
+StorageInfo::StorageInfo(const std::string& storage_name,
+ dbus::Response* response)
+ : vendor_id_(0),
+ product_id_(0),
+ device_flags_(0),
+ storage_name_(storage_name),
+ storage_type_(0),
+ filesystem_type_(0),
+ access_capability_(0),
+ max_capacity_(0),
+ free_space_in_bytes_(0),
+ free_space_in_objects_(0) {
+ InitializeFromResponse(response);
+}
+
+StorageInfo::~StorageInfo() {
+}
+
+// Initialize |this| from |response| given by the mtpd service.
satorux1 2012/08/03 05:26:07 Initializes
Lei Zhang 2012/08/03 06:29:30 Done.
+void StorageInfo::InitializeFromResponse(dbus::Response* response) {
+ dbus::MessageReader response_reader(response);
+ dbus::MessageReader array_reader(response);
+ if (!response_reader.PopArray(&array_reader)) {
+ LOG(ERROR) << "Invalid response: " << response->ToString();
+ return;
+ }
+ // TODO(satorux): Rework this code using Protocol Buffers. crosbug.com/22626
+ typedef std::map<std::string, dbus::MessageReader*> PropertiesMap;
+ PropertiesMap properties;
+ STLValueDeleter<PropertiesMap> properties_value_deleter(&properties);
+ while (array_reader.HasMoreData()) {
+ dbus::MessageReader* value_reader = new dbus::MessageReader(response);
+ dbus::MessageReader dict_entry_reader(response);
+ std::string key;
+ if (!array_reader.PopDictEntry(&dict_entry_reader) ||
+ !dict_entry_reader.PopString(&key) ||
+ !dict_entry_reader.PopVariant(value_reader)) {
+ LOG(ERROR) << "Invalid response: " << response->ToString();
+ return;
+ }
+ properties[key] = value_reader;
+ }
+ // TODO(thestig) Add enums for fields below as appropriate.
+ MaybePopString(properties["Vendor"], &vendor_);
+ MaybePopString(properties["Product"], &product_);
+ MaybePopString(properties["StorageDescription"], &storage_description_);
+ MaybePopString(properties["VolumeIdentifier"], &volume_identifier_);
+ MaybePopUint16(properties["VendorId"], &vendor_id_);
+ MaybePopUint16(properties["ProductId"], &product_id_);
+ MaybePopUint16(properties["StorageType"], &storage_type_);
+ MaybePopUint16(properties["FilesystemType"], &filesystem_type_);
+ MaybePopUint16(properties["AccessCapability"], &access_capability_);
+ MaybePopUint32(properties["DeviceFlags"], &device_flags_);
+ MaybePopUint64(properties["MaxCapacity"], &max_capacity_);
+ MaybePopUint64(properties["FreeSpaceInBytes"], &free_space_in_bytes_);
+ MaybePopUint64(properties["FreeSpaceInObjects"], &free_space_in_objects_);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FileEntry
+
+FileEntry::FileEntry(dbus::Response* response)
+ : item_id_(0),
+ parent_id_(0),
+ filesize_(0),
+ file_type_(FILE_TYPE_UNKNOWN) {
+ InitializeFromResponse(response);
+}
+
+FileEntry::~FileEntry() {
+}
+
+// Initialize |this| from |response| given by the mtpd service.
+void FileEntry::InitializeFromResponse(dbus::Response* response) {
+ dbus::MessageReader response_reader(response);
+ dbus::MessageReader array_reader(response);
+ if (!response_reader.PopArray(&array_reader)) {
+ LOG(ERROR) << "Invalid response: " << response->ToString();
+ return;
+ }
+ // TODO(satorux): Rework this code using Protocol Buffers. crosbug.com/22626
+ typedef std::map<std::string, dbus::MessageReader*> PropertiesMap;
+ PropertiesMap properties;
+ STLValueDeleter<PropertiesMap> properties_value_deleter(&properties);
+ while (array_reader.HasMoreData()) {
+ dbus::MessageReader* value_reader = new dbus::MessageReader(response);
+ dbus::MessageReader dict_entry_reader(response);
+ std::string key;
+ if (!array_reader.PopDictEntry(&dict_entry_reader) ||
+ !dict_entry_reader.PopString(&key) ||
+ !dict_entry_reader.PopVariant(value_reader)) {
+ LOG(ERROR) << "Invalid response: " << response->ToString();
+ return;
+ }
+ properties[key] = value_reader;
+ }
+
+ MaybePopString(properties["FileName"], &filename_);
+ MaybePopUint32(properties["ItemId"], &item_id_);
+ MaybePopUint32(properties["ParentId"], &parent_id_);
+ MaybePopUint64(properties["FileSize"], &filesize_);
+
+ int64 modification_date = -1;
+ if (MaybePopInt64(properties["ModificationDate"], &modification_date))
+ modification_date_ = base::Time::FromTimeT(modification_date);
+
+ uint16 file_type = FILE_TYPE_OTHER;
+ if (MaybePopUint16(properties["FileType"], &file_type)) {
+ switch (file_type) {
+ case FILE_TYPE_FOLDER:
+ file_type_ = FILE_TYPE_FOLDER;
satorux1 2012/08/03 05:26:07 You might want to just fall through for known type
Lei Zhang 2012/08/03 06:29:30 Good idea, thanks.
+ case FILE_TYPE_JPEG:
+ file_type_ = FILE_TYPE_JPEG;
+ case FILE_TYPE_JFIF:
+ file_type_ = FILE_TYPE_JFIF;
+ case FILE_TYPE_TIFF:
+ file_type_ = FILE_TYPE_TIFF;
+ case FILE_TYPE_BMP:
+ file_type_ = FILE_TYPE_BMP;
+ case FILE_TYPE_GIF:
+ file_type_ = FILE_TYPE_GIF;
+ case FILE_TYPE_PICT:
+ file_type_ = FILE_TYPE_PICT;
+ case FILE_TYPE_PNG:
+ file_type_ = FILE_TYPE_PNG;
+ case FILE_TYPE_WINDOWSIMAGEFORMAT:
+ file_type_ = FILE_TYPE_WINDOWSIMAGEFORMAT;
+ case FILE_TYPE_JP2:
+ file_type_ = FILE_TYPE_JP2;
+ case FILE_TYPE_JPX:
+ file_type_ = FILE_TYPE_JPX;
+ case FILE_TYPE_UNKNOWN:
+ file_type_ = FILE_TYPE_UNKNOWN;
+ default:
+ file_type_ = FILE_TYPE_OTHER;
+ break;
+ }
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// MTPDClient
+
+MTPDClient::MTPDClient() {}
+
+MTPDClient::~MTPDClient() {}
+
+// static
+MTPDClient* MTPDClient::Create(DBusClientImplementationType type,
+ dbus::Bus* bus) {
+ if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
+ return new MTPDClientImpl(bus);
+ DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
+ return new MTPDClientStubImpl();
+}
+
+} // namespace chromeos
Property changes on: chromeos/dbus/mtpd_client.cc
___________________________________________________________________
Added: svn:eol-style
+ LF
« chromeos/dbus/mtpd_client.h ('K') | « chromeos/dbus/mtpd_client.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698