1 // Copyright (c) 2012 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 "chromeos/dbus/update_engine_client.h" 6 7 #include "base/bind.h" 8 #include "base/callback.h" 9 #include "base/strings/string_util.h" 10 #include "dbus/bus.h" 11 #include "dbus/message.h" 12 #include "dbus/object_path.h" 13 #include "dbus/object_proxy.h" 14 #include "third_party/cros_system_api/dbus/service_constants.h" 15 16 namespace chromeos { 17 18 namespace { 19 20 const char kReleaseChannelDev[] = "dev-channel"; 21 const char kReleaseChannelBeta[] = "beta-channel"; 22 const char kReleaseChannelStable[] = "stable-channel"; 23 24 // Returns UPDATE_STATUS_ERROR on error. 25 UpdateEngineClient::UpdateStatusOperation UpdateStatusFromString( 26 const std::string& str) { 27 if (str == "UPDATE_STATUS_IDLE") 28 return UpdateEngineClient::UPDATE_STATUS_IDLE; 29 if (str == "UPDATE_STATUS_CHECKING_FOR_UPDATE") 30 return UpdateEngineClient::UPDATE_STATUS_CHECKING_FOR_UPDATE; 31 if (str == "UPDATE_STATUS_UPDATE_AVAILABLE") 32 return UpdateEngineClient::UPDATE_STATUS_UPDATE_AVAILABLE; 33 if (str == "UPDATE_STATUS_DOWNLOADING") 34 return UpdateEngineClient::UPDATE_STATUS_DOWNLOADING; 35 if (str == "UPDATE_STATUS_VERIFYING") 36 return UpdateEngineClient::UPDATE_STATUS_VERIFYING; 37 if (str == "UPDATE_STATUS_FINALIZING") 38 return UpdateEngineClient::UPDATE_STATUS_FINALIZING; 39 if (str == "UPDATE_STATUS_UPDATED_NEED_REBOOT") 40 return UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT; 41 if (str == "UPDATE_STATUS_REPORTING_ERROR_EVENT") 42 return UpdateEngineClient::UPDATE_STATUS_REPORTING_ERROR_EVENT; 43 return UpdateEngineClient::UPDATE_STATUS_ERROR; 44 } 45 46 // Used in UpdateEngineClient::EmptyUpdateCheckCallback(). 47 void EmptyUpdateCheckCallbackBody( 48 UpdateEngineClient::UpdateCheckResult unused_result) { 49 } 50 51 bool IsValidChannel(const std::string& channel) { 52 return channel == kReleaseChannelDev || 53 channel == kReleaseChannelBeta || 54 channel == kReleaseChannelStable; 55 } 56 57 } // namespace 58 59 // The UpdateEngineClient implementation used in production. 60 class UpdateEngineClientImpl : public UpdateEngineClient { 61 public: 62 explicit UpdateEngineClientImpl(dbus::Bus* bus) 63 : update_engine_proxy_(NULL), 64 last_status_(), 65 weak_ptr_factory_(this) { 66 update_engine_proxy_ = bus->GetObjectProxy( 67 update_engine::kUpdateEngineServiceName, 68 dbus::ObjectPath(update_engine::kUpdateEngineServicePath)); 69 70 // Monitor the D-Bus signal for brightness changes. Only the power 71 // manager knows the actual brightness level. We don't cache the 72 // brightness level in Chrome as it will make things less reliable. 73 update_engine_proxy_->ConnectToSignal( 74 update_engine::kUpdateEngineInterface, 75 update_engine::kStatusUpdate, 76 base::Bind(&UpdateEngineClientImpl::StatusUpdateReceived, 77 weak_ptr_factory_.GetWeakPtr()), 78 base::Bind(&UpdateEngineClientImpl::StatusUpdateConnected, 79 weak_ptr_factory_.GetWeakPtr())); 80 81 // Get update engine status for the initial status. Update engine won't 82 // send StatusUpdate signal unless there is a status change. If chrome 83 // crashes after UPDATE_STATUS_UPDATED_NEED_REBOOT status is set, 84 // restarted chrome would not get this status. See crbug.com/154104. 85 GetUpdateEngineStatus(); 86 } 87 88 virtual ~UpdateEngineClientImpl() { 89 } 90 91 // UpdateEngineClient implementation: 92 virtual void AddObserver(Observer* observer) OVERRIDE { 93 observers_.AddObserver(observer); 94 } 95 96 virtual void RemoveObserver(Observer* observer) OVERRIDE { 97 observers_.RemoveObserver(observer); 98 } 99 100 virtual bool HasObserver(Observer* observer) OVERRIDE { 101 return observers_.HasObserver(observer); 102 } 103 104 virtual void RequestUpdateCheck( 105 const UpdateCheckCallback& callback) OVERRIDE { 106 dbus::MethodCall method_call( 107 update_engine::kUpdateEngineInterface, 108 update_engine::kAttemptUpdate); 109 dbus::MessageWriter writer(&method_call); 110 writer.AppendString(""); // Unused. 111 writer.AppendString(""); // Unused. 112 113 VLOG(1) << "Requesting an update check"; 114 update_engine_proxy_->CallMethod( 115 &method_call, 116 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 117 base::Bind(&UpdateEngineClientImpl::OnRequestUpdateCheck, 118 weak_ptr_factory_.GetWeakPtr(), 119 callback)); 120 } 121 122 virtual void RebootAfterUpdate() OVERRIDE { 123 dbus::MethodCall method_call( 124 update_engine::kUpdateEngineInterface, 125 update_engine::kRebootIfNeeded); 126 127 VLOG(1) << "Requesting a reboot"; 128 update_engine_proxy_->CallMethod( 129 &method_call, 130 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 131 base::Bind(&UpdateEngineClientImpl::OnRebootAfterUpdate, 132 weak_ptr_factory_.GetWeakPtr())); 133 } 134 135 virtual Status GetLastStatus() OVERRIDE { 136 return last_status_; 137 } 138 139 virtual void SetChannel(const std::string& target_channel, 140 bool is_powerwash_allowed) OVERRIDE { 141 if (!IsValidChannel(target_channel)) { 142 LOG(ERROR) << "Invalid channel name: " << target_channel; 143 return; 144 } 145 146 dbus::MethodCall method_call( 147 update_engine::kUpdateEngineInterface, 148 update_engine::kSetChannel); 149 dbus::MessageWriter writer(&method_call); 150 writer.AppendString(target_channel); 151 writer.AppendBool(is_powerwash_allowed); 152 153 VLOG(1) << "Requesting to set channel: " 154 << "target_channel=" << target_channel << ", " 155 << "is_powerwash_allowed=" << is_powerwash_allowed; 156 update_engine_proxy_->CallMethod( 157 &method_call, 158 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 159 base::Bind(&UpdateEngineClientImpl::OnSetChannel, 160 weak_ptr_factory_.GetWeakPtr())); 161 } 162 163 virtual void GetChannel(bool get_current_channel, 164 const GetChannelCallback& callback) OVERRIDE { 165 dbus::MethodCall method_call( 166 update_engine::kUpdateEngineInterface, 167 update_engine::kGetChannel); 168 dbus::MessageWriter writer(&method_call); 169 writer.AppendBool(get_current_channel); 170 171 VLOG(1) << "Requesting to get channel, get_current_channel=" 172 << get_current_channel; 173 update_engine_proxy_->CallMethod( 174 &method_call, 175 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 176 base::Bind(&UpdateEngineClientImpl::OnGetChannel, 177 weak_ptr_factory_.GetWeakPtr(), 178 callback)); 179 } 180 181 private: 182 void GetUpdateEngineStatus() { 183 dbus::MethodCall method_call( 184 update_engine::kUpdateEngineInterface, 185 update_engine::kGetStatus); 186 update_engine_proxy_->CallMethodWithErrorCallback( 187 &method_call, 188 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, 189 base::Bind(&UpdateEngineClientImpl::OnGetStatus, 190 weak_ptr_factory_.GetWeakPtr()), 191 base::Bind(&UpdateEngineClientImpl::OnGetStatusError, 192 weak_ptr_factory_.GetWeakPtr())); 193 } 194 195 // Called when a response for RequestUpdateCheck() is received. 196 void OnRequestUpdateCheck(const UpdateCheckCallback& callback, 197 dbus::Response* response) { 198 if (!response) { 199 LOG(ERROR) << "Failed to request update check"; 200 callback.Run(UPDATE_RESULT_FAILED); 201 return; 202 } 203 callback.Run(UPDATE_RESULT_SUCCESS); 204 } 205 206 // Called when a response for RebootAfterUpdate() is received. 207 void OnRebootAfterUpdate(dbus::Response* response) { 208 if (!response) { 209 LOG(ERROR) << "Failed to request rebooting after update"; 210 return; 211 } 212 } 213 214 // Called when a response for GetStatus is received. 215 void OnGetStatus(dbus::Response* response) { 216 if (!response) { 217 LOG(ERROR) << "Failed to get response for GetStatus request."; 218 return; 219 } 220 221 dbus::MessageReader reader(response); 222 std::string current_operation; 223 Status status; 224 if (!(reader.PopInt64(&status.last_checked_time) && 225 reader.PopDouble(&status.download_progress) && 226 reader.PopString(¤t_operation) && 227 reader.PopString(&status.new_version) && 228 reader.PopInt64(&status.new_size))) { 229 LOG(ERROR) << "GetStatus had incorrect response: " 230 << response->ToString(); 231 return; 232 } 233 status.status = UpdateStatusFromString(current_operation); 234 last_status_ = status; 235 FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status)); 236 } 237 238 // Called when GetStatus call failed. 239 void OnGetStatusError(dbus::ErrorResponse* error) { 240 LOG(ERROR) << "GetStatus request failed with error: " << error->ToString(); 241 } 242 243 // Called when a response for SetReleaseChannel() is received. 244 void OnSetChannel(dbus::Response* response) { 245 if (!response) { 246 LOG(ERROR) << "Failed to request setting channel"; 247 return; 248 } 249 VLOG(1) << "Succeeded to set channel"; 250 } 251 252 // Called when a response for GetChannel() is received. 253 void OnGetChannel(const GetChannelCallback& callback, 254 dbus::Response* response) { 255 if (!response) { 256 LOG(ERROR) << "Failed to request getting channel"; 257 callback.Run(""); 258 return; 259 } 260 dbus::MessageReader reader(response); 261 std::string channel; 262 if (!reader.PopString(&channel)) { 263 LOG(ERROR) << "Incorrect response: " << response->ToString(); 264 callback.Run(""); 265 return; 266 } 267 VLOG(1) << "The channel received: " << channel; 268 callback.Run(channel); 269 } 270 271 // Called when a status update signal is received. 272 void StatusUpdateReceived(dbus::Signal* signal) { 273 VLOG(1) << "Status update signal received: " << signal->ToString(); 274 dbus::MessageReader reader(signal); 275 int64 last_checked_time = 0; 276 double progress = 0.0; 277 std::string current_operation; 278 std::string new_version; 279 int64_t new_size = 0; 280 if (!(reader.PopInt64(&last_checked_time) && 281 reader.PopDouble(&progress) && 282 reader.PopString(¤t_operation) && 283 reader.PopString(&new_version) && 284 reader.PopInt64(&new_size))) { 285 LOG(ERROR) << "Status changed signal had incorrect parameters: " 286 << signal->ToString(); 287 return; 288 } 289 Status status; 290 status.last_checked_time = last_checked_time; 291 status.download_progress = progress; 292 status.status = UpdateStatusFromString(current_operation); 293 status.new_version = new_version; 294 status.new_size = new_size; 295 296 last_status_ = status; 297 FOR_EACH_OBSERVER(Observer, observers_, UpdateStatusChanged(status)); 298 } 299 300 // Called when the status update signal is initially connected. 301 void StatusUpdateConnected(const std::string& interface_name, 302 const std::string& signal_name, 303 bool success) { 304 LOG_IF(WARNING, !success) 305 << "Failed to connect to status updated signal."; 306 } 307 308 dbus::ObjectProxy* update_engine_proxy_; 309 ObserverList<Observer> observers_; 310 Status last_status_; 311 312 // Note: This should remain the last member so it'll be destroyed and 313 // invalidate its weak pointers before any other members are destroyed. 314 base::WeakPtrFactory<UpdateEngineClientImpl> weak_ptr_factory_; 315 316 DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl); 317 }; 318 319 // The UpdateEngineClient implementation used on Linux desktop, 320 // which does nothing. 321 class UpdateEngineClientStubImpl : public UpdateEngineClient { 322 // UpdateEngineClient implementation: 323 virtual void AddObserver(Observer* observer) OVERRIDE {} 324 virtual void RemoveObserver(Observer* observer) OVERRIDE {} 325 virtual bool HasObserver(Observer* observer) OVERRIDE { return false; } 326 327 virtual void RequestUpdateCheck( 328 const UpdateCheckCallback& callback) OVERRIDE { 329 callback.Run(UPDATE_RESULT_NOTIMPLEMENTED); 330 } 331 virtual void RebootAfterUpdate() OVERRIDE {} 332 virtual Status GetLastStatus() OVERRIDE { return Status(); } 333 virtual void SetChannel(const std::string& target_channel, 334 bool is_powerwash_allowed) OVERRIDE { 335 LOG(INFO) << "Requesting to set channel: " 336 << "target_channel=" << target_channel << ", " 337 << "is_powerwash_allowed=" << is_powerwash_allowed; 338 } 339 virtual void GetChannel(bool get_current_channel, 340 const GetChannelCallback& callback) OVERRIDE { 341 LOG(INFO) << "Requesting to get channel, get_current_channel=" 342 << get_current_channel; 343 callback.Run("beta-channel"); 344 } 345 }; 346 347 UpdateEngineClient::UpdateEngineClient() { 348 } 349 350 UpdateEngineClient::~UpdateEngineClient() { 351 } 352 353 // static 354 UpdateEngineClient::UpdateCheckCallback 355 UpdateEngineClient::EmptyUpdateCheckCallback() { 356 return base::Bind(&EmptyUpdateCheckCallbackBody); 357 } 358 359 // static 360 UpdateEngineClient* UpdateEngineClient::Create( 361 DBusClientImplementationType type, 362 dbus::Bus* bus) { 363 if (type == REAL_DBUS_CLIENT_IMPLEMENTATION) 364 return new UpdateEngineClientImpl(bus); 365 DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type); 366 return new UpdateEngineClientStubImpl(); 367 } 368 369 } // namespace chromeos 370