diff options
-rw-r--r-- | dbus/bus.cc | 180 | ||||
-rw-r--r-- | dbus/bus.h | 55 | ||||
-rw-r--r-- | dbus/bus_unittest.cc | 122 | ||||
-rw-r--r-- | device/media_transfer_protocol/media_transfer_protocol_manager.cc | 60 |
4 files changed, 395 insertions, 22 deletions
diff --git a/dbus/bus.cc b/dbus/bus.cc index 1ddeeb8..c830f06 100644 --- a/dbus/bus.cc +++ b/dbus/bus.cc @@ -9,10 +9,12 @@ #include "base/message_loop.h" #include "base/message_loop/message_loop_proxy.h" #include "base/stl_util.h" +#include "base/stringprintf.h" #include "base/threading/thread.h" #include "base/threading/thread_restrictions.h" #include "base/time.h" #include "dbus/exported_object.h" +#include "dbus/message.h" #include "dbus/object_manager.h" #include "dbus/object_path.h" #include "dbus/object_proxy.h" @@ -27,6 +29,15 @@ const char kDisconnectedMatchRule[] = "type='signal', path='/org/freedesktop/DBus/Local'," "interface='org.freedesktop.DBus.Local', member='Disconnected'"; +// The NameOwnerChanged member in org.freedesktop.DBus +const char kNameOwnerChangedSignal[] = "NameOwnerChanged"; + +// The match rule used to filter for changes to a given service name owner. +const char kServiceNameOwnerChangeMatchRule[] = + "type='signal',interface='org.freedesktop.DBus'," + "member='NameOwnerChanged',path='/org/freedesktop/DBus'," + "sender='org.freedesktop.DBus',arg0='%s'"; + // The class is used for watching the file descriptor used for D-Bus // communication. class Watch : public base::MessagePumpLibevent::Watcher { @@ -908,6 +919,112 @@ void Bus::GetServiceOwnerInternal(const std::string& service_name, PostTaskToOriginThread(FROM_HERE, base::Bind(callback, service_owner)); } +void Bus::ListenForServiceOwnerChange( + const std::string& service_name, + const GetServiceOwnerCallback& callback) { + AssertOnOriginThread(); + DCHECK(!service_name.empty()); + DCHECK(!callback.is_null()); + + PostTaskToDBusThread(FROM_HERE, + base::Bind(&Bus::ListenForServiceOwnerChangeInternal, + this, service_name, callback)); +} + +void Bus::ListenForServiceOwnerChangeInternal( + const std::string& service_name, + const GetServiceOwnerCallback& callback) { + AssertOnDBusThread(); + DCHECK(!service_name.empty()); + DCHECK(!callback.is_null()); + + if (!Connect() || !SetUpAsyncOperations()) + return; + + if (service_owner_changed_listener_map_.empty()) { + bool filter_added = + AddFilterFunction(Bus::OnServiceOwnerChangedFilter, this); + DCHECK(filter_added); + } + + ServiceOwnerChangedListenerMap::iterator it = + service_owner_changed_listener_map_.find(service_name); + if (it == service_owner_changed_listener_map_.end()) { + // Add a match rule for the new service name. + const std::string name_owner_changed_match_rule = + base::StringPrintf(kServiceNameOwnerChangeMatchRule, + service_name.c_str()); + ScopedDBusError error; + AddMatch(name_owner_changed_match_rule, error.get()); + if (error.is_set()) { + LOG(ERROR) << "Failed to add match rule for " << service_name + << ". Got " << error.name() << ": " << error.message(); + return; + } + + service_owner_changed_listener_map_[service_name].push_back(callback); + return; + } + + // Check if the callback has already been added. + std::vector<GetServiceOwnerCallback>& callbacks = it->second; + for (size_t i = 0; i < callbacks.size(); ++i) { + if (callbacks[i].Equals(callback)) + return; + } + callbacks.push_back(callback); +} + +void Bus::UnlistenForServiceOwnerChange( + const std::string& service_name, + const GetServiceOwnerCallback& callback) { + AssertOnOriginThread(); + DCHECK(!service_name.empty()); + DCHECK(!callback.is_null()); + + PostTaskToDBusThread(FROM_HERE, + base::Bind(&Bus::UnlistenForServiceOwnerChangeInternal, + this, service_name, callback)); +} + +void Bus::UnlistenForServiceOwnerChangeInternal( + const std::string& service_name, + const GetServiceOwnerCallback& callback) { + AssertOnDBusThread(); + DCHECK(!service_name.empty()); + DCHECK(!callback.is_null()); + + ServiceOwnerChangedListenerMap::iterator it = + service_owner_changed_listener_map_.find(service_name); + if (it == service_owner_changed_listener_map_.end()) + return; + + std::vector<GetServiceOwnerCallback>& callbacks = it->second; + for (size_t i = 0; i < callbacks.size(); ++i) { + if (callbacks[i].Equals(callback)) { + callbacks.erase(callbacks.begin() + i); + break; // There can be only one. + } + } + if (!callbacks.empty()) + return; + + // Last callback for |service_name| has been removed, remove match rule. + const std::string name_owner_changed_match_rule = + base::StringPrintf(kServiceNameOwnerChangeMatchRule, + service_name.c_str()); + ScopedDBusError error; + RemoveMatch(name_owner_changed_match_rule, error.get()); + // And remove |service_owner_changed_listener_map_| entry. + service_owner_changed_listener_map_.erase(it); + + if (service_owner_changed_listener_map_.empty()) { + bool filter_removed = + RemoveFilterFunction(Bus::OnServiceOwnerChangedFilter, this); + DCHECK(filter_removed); + } +} + dbus_bool_t Bus::OnAddWatch(DBusWatch* raw_watch) { AssertOnDBusThread(); @@ -1000,36 +1117,81 @@ void Bus::OnConnectionDisconnected(DBusConnection* connection) { ShutdownAndBlock(); } +void Bus::OnServiceOwnerChanged(DBusMessage* message) { + DCHECK(message); + AssertOnDBusThread(); + + // |message| will be unrefed on exit of the function. Increment the + // reference so we can use it in Signal::FromRawMessage() below. + dbus_message_ref(message); + scoped_ptr<Signal> signal(Signal::FromRawMessage(message)); + + // Confirm the validity of the NameOwnerChanged signal. + if (signal->GetMember() != kNameOwnerChangedSignal || + signal->GetInterface() != DBUS_INTERFACE_DBUS || + signal->GetSender() != DBUS_SERVICE_DBUS) { + return; + } + + MessageReader reader(signal.get()); + std::string service_name; + std::string old_owner; + std::string new_owner; + if (!reader.PopString(&service_name) || + !reader.PopString(&old_owner) || + !reader.PopString(&new_owner)) { + return; + } + + ServiceOwnerChangedListenerMap::const_iterator it = + service_owner_changed_listener_map_.find(service_name); + if (it == service_owner_changed_listener_map_.end()) + return; + + const std::vector<GetServiceOwnerCallback>& callbacks = it->second; + for (size_t i = 0; i < callbacks.size(); ++i) { + PostTaskToOriginThread(FROM_HERE, + base::Bind(callbacks[i], new_owner)); + } +} + +// static dbus_bool_t Bus::OnAddWatchThunk(DBusWatch* raw_watch, void* data) { Bus* self = static_cast<Bus*>(data); return self->OnAddWatch(raw_watch); } +// static void Bus::OnRemoveWatchThunk(DBusWatch* raw_watch, void* data) { Bus* self = static_cast<Bus*>(data); self->OnRemoveWatch(raw_watch); } +// static void Bus::OnToggleWatchThunk(DBusWatch* raw_watch, void* data) { Bus* self = static_cast<Bus*>(data); self->OnToggleWatch(raw_watch); } +// static dbus_bool_t Bus::OnAddTimeoutThunk(DBusTimeout* raw_timeout, void* data) { Bus* self = static_cast<Bus*>(data); return self->OnAddTimeout(raw_timeout); } +// static void Bus::OnRemoveTimeoutThunk(DBusTimeout* raw_timeout, void* data) { Bus* self = static_cast<Bus*>(data); self->OnRemoveTimeout(raw_timeout); } +// static void Bus::OnToggleTimeoutThunk(DBusTimeout* raw_timeout, void* data) { Bus* self = static_cast<Bus*>(data); self->OnToggleTimeout(raw_timeout); } +// static void Bus::OnDispatchStatusChangedThunk(DBusConnection* connection, DBusDispatchStatus status, void* data) { @@ -1037,6 +1199,7 @@ void Bus::OnDispatchStatusChangedThunk(DBusConnection* connection, self->OnDispatchStatusChanged(connection, status); } +// static DBusHandlerResult Bus::OnConnectionDisconnectedFilter( DBusConnection* connection, DBusMessage* message, @@ -1045,11 +1208,26 @@ DBusHandlerResult Bus::OnConnectionDisconnectedFilter( DBUS_INTERFACE_LOCAL, kDisconnectedSignal)) { Bus* self = static_cast<Bus*>(data); - self->AssertOnDBusThread(); self->OnConnectionDisconnected(connection); return DBUS_HANDLER_RESULT_HANDLED; } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } +// static +DBusHandlerResult Bus::OnServiceOwnerChangedFilter( + DBusConnection* connection, + DBusMessage* message, + void* data) { + if (dbus_message_is_signal(message, + DBUS_INTERFACE_DBUS, + kNameOwnerChangedSignal)) { + Bus* self = static_cast<Bus*>(data); + self->OnServiceOwnerChanged(message); + } + // Always return unhandled to let others, e.g. ObjectProxies, handle the same + // signal. + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + } // namespace dbus @@ -11,6 +11,7 @@ #include <set> #include <string> #include <utility> +#include <vector> #include "base/callback.h" #include "base/memory/ref_counted.h" @@ -22,7 +23,6 @@ namespace base { class SequencedTaskRunner; class SingleThreadTaskRunner; -class Thread; } namespace tracked_objects { @@ -563,6 +563,28 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> { virtual void GetServiceOwner(const std::string& service_name, const GetServiceOwnerCallback& callback); + // Whenever the owner for |service_name| changes, run |callback| with the + // name of the new owner. If the owner goes away, then |callback| receives + // an empty string. + // + // Any unique (service_name, callback) can be used. Duplicate are ignored. + // |service_name| must not be empty and |callback| must not be null. + // + // Must be called in the origin thread. + virtual void ListenForServiceOwnerChange( + const std::string& service_name, + const GetServiceOwnerCallback& callback); + + // Stop listening for |service_name| owner changes for |callback|. + // Any unique (service_name, callback) can be used. Non-registered callbacks + // for a given service name are ignored. + // |service_name| must not be empty and |callback| must not be null. + // + // Must be called in the origin thread. + virtual void UnlistenForServiceOwnerChange( + const std::string& service_name, + const GetServiceOwnerCallback& callback); + // Returns true if the bus is connected to D-Bus. bool is_connected() { return connection_ != NULL; } @@ -592,6 +614,16 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> { void GetServiceOwnerInternal(const std::string& service_name, const GetServiceOwnerCallback& callback); + // Helper function used for ListenForServiceOwnerChange(). + void ListenForServiceOwnerChangeInternal( + const std::string& service_name, + const GetServiceOwnerCallback& callback); + + // Helper function used for UnListenForServiceOwnerChange(). + void UnlistenForServiceOwnerChangeInternal( + const std::string& service_name, + const GetServiceOwnerCallback& callback); + // Processes the all incoming data to the connection, if any. // // BLOCKING CALL. @@ -625,6 +657,9 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> { // Called when the connection is diconnected. void OnConnectionDisconnected(DBusConnection* connection); + // Called when a service owner change occurs. + void OnServiceOwnerChanged(DBusMessage* message); + // Callback helper functions. Redirects to the corresponding member function. static dbus_bool_t OnAddWatchThunk(DBusWatch* raw_watch, void* data); static void OnRemoveWatchThunk(DBusWatch* raw_watch, void* data); @@ -636,12 +671,18 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> { DBusDispatchStatus status, void* data); - // Calls OnConnectionDisconnected if the Diconnected signal is received. + // Calls OnConnectionDisconnected if the Disconnected signal is received. static DBusHandlerResult OnConnectionDisconnectedFilter( DBusConnection* connection, DBusMessage* message, void* user_data); + // Calls OnServiceOwnerChanged for a NameOwnerChanged signal. + static DBusHandlerResult OnServiceOwnerChangedFilter( + DBusConnection* connection, + DBusMessage* message, + void* user_data); + const BusType bus_type_; const ConnectionType connection_type_; scoped_refptr<base::SequencedTaskRunner> dbus_task_runner_; @@ -684,6 +725,16 @@ class CHROME_DBUS_EXPORT Bus : public base::RefCountedThreadSafe<Bus> { scoped_refptr<dbus::ObjectManager> > ObjectManagerTable; ObjectManagerTable object_manager_table_; + // A map of NameOwnerChanged signals to listen for and the callbacks to run + // on the origin thread when the owner changes. + // Only accessed on the DBus thread. + // Key: Service name + // Value: Vector of callbacks. Unique and expected to be small. Not using + // std::set here because base::Callbacks don't have a '<' operator. + typedef std::map<std::string, std::vector<GetServiceOwnerCallback> > + ServiceOwnerChangedListenerMap; + ServiceOwnerChangedListenerMap service_owner_changed_listener_map_; + bool async_operations_set_up_; bool shutdown_completed_; diff --git a/dbus/bus_unittest.cc b/dbus/bus_unittest.cc index 82949d3..3c453f7 100644 --- a/dbus/bus_unittest.cc +++ b/dbus/bus_unittest.cc @@ -7,11 +7,13 @@ #include "base/bind.h" #include "base/message_loop.h" #include "base/memory/ref_counted.h" +#include "base/run_loop.h" #include "base/threading/thread.h" #include "dbus/exported_object.h" #include "dbus/object_path.h" #include "dbus/object_proxy.h" #include "dbus/scoped_dbus_error.h" +#include "dbus/test_service.h" #include "testing/gtest/include/gtest/gtest.h" @@ -24,6 +26,49 @@ DBusHandlerResult DummyHandler(DBusConnection* connection, return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } +// Test helper for BusTest.ListenForServiceOwnerChange that wraps a +// base::RunLoop. At Run() time, the caller pass in the expected number of +// quit calls, and at QuitIfConditionIsSatisified() time, only quit the RunLoop +// if the expected number of quit calls have been reached. +class RunLoopWithExpectedCount { + public: + RunLoopWithExpectedCount() : expected_quit_calls_(0), actual_quit_calls_(0) {} + ~RunLoopWithExpectedCount() {} + + void Run(int expected_quit_calls) { + DCHECK_EQ(0, expected_quit_calls_); + DCHECK_EQ(0, actual_quit_calls_); + expected_quit_calls_ = expected_quit_calls; + run_loop_.reset(new base::RunLoop()); + run_loop_->Run(); + } + + void QuitIfConditionIsSatisified() { + if (++actual_quit_calls_ != expected_quit_calls_) + return; + run_loop_->Quit(); + expected_quit_calls_ = 0; + actual_quit_calls_ = 0; + } + + private: + scoped_ptr<base::RunLoop> run_loop_; + int expected_quit_calls_; + int actual_quit_calls_; + + DISALLOW_COPY_AND_ASSIGN(RunLoopWithExpectedCount); +}; + +// Test helper for BusTest.ListenForServiceOwnerChange. +void OnServiceOwnerChanged(RunLoopWithExpectedCount* run_loop_state, + std::string* service_owner, + int* num_of_owner_changes, + const std::string& new_service_owner) { + *service_owner = new_service_owner; + ++(*num_of_owner_changes); + run_loop_state->QuitIfConditionIsSatisified(); +} + } // namespace TEST(BusTest, GetObjectProxy) { @@ -296,3 +341,80 @@ TEST(BusTest, DoubleAddAndRemoveMatch) { bus->ShutdownAndBlock(); } + +TEST(BusTest, ListenForServiceOwnerChange) { + // Setup the current thread's MessageLoop. Must be of TYPE_IO for the + // listeners to work. + base::MessageLoop message_loop(base::MessageLoop::TYPE_IO); + RunLoopWithExpectedCount run_loop_state; + + // Create the bus. + dbus::Bus::Options bus_options; + scoped_refptr<dbus::Bus> bus = new dbus::Bus(bus_options); + + // Add a listener. + std::string service_owner1; + int num_of_owner_changes1 = 0; + dbus::Bus::GetServiceOwnerCallback callback1 = + base::Bind(&OnServiceOwnerChanged, + &run_loop_state, + &service_owner1, + &num_of_owner_changes1); + bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1); + // This should be a no-op. + bus->ListenForServiceOwnerChange("org.chromium.TestService", callback1); + base::RunLoop().RunUntilIdle(); + + // Nothing has happened yet. Check initial state. + EXPECT_TRUE(service_owner1.empty()); + EXPECT_EQ(0, num_of_owner_changes1); + + // Make an ownership change. + ASSERT_TRUE(bus->RequestOwnershipAndBlock("org.chromium.TestService")); + run_loop_state.Run(1); + + { + // Get the current service owner and check to make sure the listener got + // the right value. + std::string current_service_owner = + bus->GetServiceOwnerAndBlock("org.chromium.TestService", + dbus::Bus::REPORT_ERRORS); + ASSERT_FALSE(current_service_owner.empty()); + + // Make sure the listener heard about the new owner. + EXPECT_EQ(current_service_owner, service_owner1); + + // Test the second ListenForServiceOwnerChange() above is indeed a no-op. + EXPECT_EQ(1, num_of_owner_changes1); + } + + // Add a second listener. + std::string service_owner2; + int num_of_owner_changes2 = 0; + dbus::Bus::GetServiceOwnerCallback callback2 = + base::Bind(&OnServiceOwnerChanged, + &run_loop_state, + &service_owner2, + &num_of_owner_changes2); + bus->ListenForServiceOwnerChange("org.chromium.TestService", callback2); + base::RunLoop().RunUntilIdle(); + + // Release the ownership and make sure the service owner listeners fire with + // the right values and the right number of times. + ASSERT_TRUE(bus->ReleaseOwnership("org.chromium.TestService")); + run_loop_state.Run(2); + + EXPECT_TRUE(service_owner1.empty()); + EXPECT_TRUE(service_owner2.empty()); + EXPECT_EQ(2, num_of_owner_changes1); + EXPECT_EQ(1, num_of_owner_changes2); + + // Unlisten so shutdown can proceed correctly. + bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback1); + bus->UnlistenForServiceOwnerChange("org.chromium.TestService", callback2); + base::RunLoop().RunUntilIdle(); + + // Shut down synchronously. + bus->ShutdownAndBlock(); + EXPECT_TRUE(bus->shutdown_completed()); +} diff --git a/device/media_transfer_protocol/media_transfer_protocol_manager.cc b/device/media_transfer_protocol/media_transfer_protocol_manager.cc index 869e924..9bcf5be 100644 --- a/device/media_transfer_protocol/media_transfer_protocol_manager.cc +++ b/device/media_transfer_protocol/media_transfer_protocol_manager.cc @@ -50,15 +50,29 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { session_bus_ = new dbus::Bus(options); #endif - dbus::Bus::GetServiceOwnerCallback reply_task = + // Listen for future mtpd service owner changes, in case it is not + // available right now. There is no guarantee on Linux or ChromeOS that + // mtpd is running already. + mtpd_owner_changed_callback_ = base::Bind(&MediaTransferProtocolManagerImpl::FinishSetupOnOriginThread, weak_ptr_factory_.GetWeakPtr()); - GetBus()->GetServiceOwner(mtpd::kMtpdServiceName, reply_task); + GetBus()->ListenForServiceOwnerChange(mtpd::kMtpdServiceName, + mtpd_owner_changed_callback_); + GetBus()->GetServiceOwner(mtpd::kMtpdServiceName, + mtpd_owner_changed_callback_); } virtual ~MediaTransferProtocolManagerImpl() { DCHECK(g_media_transfer_protocol_manager); g_media_transfer_protocol_manager = NULL; + GetBus()->UnlistenForServiceOwnerChange(mtpd::kMtpdServiceName, + mtpd_owner_changed_callback_); + +#if !defined(OS_CHROMEOS) + session_bus_->PostTaskToDBusThread( + FROM_HERE, base::Bind(&dbus::Bus::ShutdownAndBlock, session_bus_)); +#endif + VLOG(1) << "MediaTransferProtocolManager Shutdown completed"; } @@ -97,7 +111,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { const std::string& mode, const OpenStorageCallback& callback) OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); - if (!ContainsKey(storage_info_map_, storage_name)) { + if (!ContainsKey(storage_info_map_, storage_name) || !mtp_client_) { callback.Run(std::string(), true); return; } @@ -115,7 +129,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { virtual void CloseStorage(const std::string& storage_handle, const CloseStorageCallback& callback) OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); - if (!ContainsKey(handles_, storage_handle)) { + if (!ContainsKey(handles_, storage_handle) || !mtp_client_) { callback.Run(true); return; } @@ -134,7 +148,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { const std::string& path, const ReadDirectoryCallback& callback) OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); - if (!ContainsKey(handles_, storage_handle)) { + if (!ContainsKey(handles_, storage_handle) || !mtp_client_) { callback.Run(std::vector<MtpFileEntry>(), true); return; } @@ -154,7 +168,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { uint32 file_id, const ReadDirectoryCallback& callback) OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); - if (!ContainsKey(handles_, storage_handle)) { + if (!ContainsKey(handles_, storage_handle) || !mtp_client_) { callback.Run(std::vector<MtpFileEntry>(), true); return; } @@ -175,7 +189,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { uint32 count, const ReadFileCallback& callback) OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); - if (!ContainsKey(handles_, storage_handle)) { + if (!ContainsKey(handles_, storage_handle) || !mtp_client_) { callback.Run(std::string(), true); return; } @@ -195,7 +209,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { uint32 count, const ReadFileCallback& callback) OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); - if (!ContainsKey(handles_, storage_handle)) { + if (!ContainsKey(handles_, storage_handle) || !mtp_client_) { callback.Run(std::string(), true); return; } @@ -212,7 +226,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { const std::string& path, const GetFileInfoCallback& callback) OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); - if (!ContainsKey(handles_, storage_handle)) { + if (!ContainsKey(handles_, storage_handle) || !mtp_client_) { callback.Run(MtpFileEntry(), true); return; } @@ -230,7 +244,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { uint32 file_id, const GetFileInfoCallback& callback) OVERRIDE { DCHECK(thread_checker_.CalledOnValidThread()); - if (!ContainsKey(handles_, storage_handle)) { + if (!ContainsKey(handles_, storage_handle) || !mtp_client_) { callback.Run(MtpFileEntry(), true); return; } @@ -259,6 +273,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { void OnStorageChanged(bool is_attach, const std::string& storage_name) { DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(mtp_client_); if (is_attach) { mtp_client_->GetStorageInfo( storage_name, @@ -283,6 +298,7 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { void OnEnumerateStorages(const std::vector<std::string>& storage_names) { DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(mtp_client_); for (size_t i = 0; i < storage_names.size(); ++i) { mtp_client_->GetStorageInfo( storage_names[i], @@ -396,20 +412,22 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { } // Callback to finish initialization after figuring out if the mtp service - // has an owner. - // |service_owner| contains the name of the current owner, if any. - void FinishSetupOnOriginThread(const std::string& service_owner) { + // has an owner, or if the service owner has changed. + // |mtpd_service_owner| contains the name of the current owner, if any. + void FinishSetupOnOriginThread(const std::string& mtpd_service_owner) { DCHECK(thread_checker_.CalledOnValidThread()); - if (service_owner.empty()) { -#if !defined(OS_CHROMEOS) - // |session_bus_| will not get used. Manually shut it down. - session_bus_->PostTaskToDBusThread( - FROM_HERE, base::Bind(&dbus::Bus::ShutdownAndBlock, session_bus_)); -#endif + if (mtpd_service_owner == current_mtpd_owner_) + return; + + if (mtpd_service_owner.empty()) { + current_mtpd_owner_.clear(); + mtp_client_.reset(); return; } + current_mtpd_owner_ = mtpd_service_owner; + mtp_client_.reset( MediaTransferProtocolDaemonClient::Create(GetBus(), false /* not stub */)); @@ -445,6 +463,10 @@ class MediaTransferProtocolManagerImpl : public MediaTransferProtocolManager { // Set of open storage handles. std::set<std::string> handles_; + dbus::Bus::GetServiceOwnerCallback mtpd_owner_changed_callback_; + + std::string current_mtpd_owner_; + // Queued callbacks. OpenStorageCallbackQueue open_storage_callbacks_; CloseStorageCallbackQueue close_storage_callbacks_; |