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 <stdint.h>
8 
9 #include <algorithm>
10 #include <string>
11 #include <vector>
12 
13 #include "base/bind.h"
14 #include "base/callback.h"
15 #include "base/command_line.h"
16 #include "base/location.h"
17 #include "base/logging.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/stl_util.h"
20 #include "base/strings/string_util.h"
21 #include "base/threading/thread_task_runner_handle.h"
22 #include "chromeos/dbus/constants/dbus_switches.h"
23 #include "chromeos/dbus/util/version_loader.h"
24 #include "dbus/bus.h"
25 #include "dbus/message.h"
26 #include "dbus/object_path.h"
27 #include "dbus/object_proxy.h"
28 #include "third_party/cros_system_api/dbus/service_constants.h"
29 
30 namespace chromeos {
31 
32 namespace {
33 
34 const char kReleaseChannelCanary[] = "canary-channel";
35 const char kReleaseChannelDev[] = "dev-channel";
36 const char kReleaseChannelBeta[] = "beta-channel";
37 const char kReleaseChannelStable[] = "stable-channel";
38 
39 // List of release channels ordered by stability.
40 const char* kReleaseChannelsList[] = {kReleaseChannelCanary, kReleaseChannelDev,
41                                       kReleaseChannelBeta,
42                                       kReleaseChannelStable};
43 
44 // Delay between successive state transitions during AU.
45 const int kStateTransitionDefaultDelayMs = 3000;
46 
47 // Delay between successive notifications about downloading progress
48 // during fake AU.
49 const int kStateTransitionDownloadingDelayMs = 250;
50 
51 // Size of parts of a "new" image which are downloaded each
52 // |kStateTransitionDownloadingDelayMs| during fake AU.
53 const int64_t kDownloadSizeDelta = 1 << 19;
54 
55 // Version number of the image being installed during fake AU.
56 const char kStubVersion[] = "1234.0.0.0";
57 
IsValidChannel(const std::string & channel)58 bool IsValidChannel(const std::string& channel) {
59   return channel == kReleaseChannelDev || channel == kReleaseChannelBeta ||
60          channel == kReleaseChannelStable;
61 }
62 
63 }  // namespace
64 
65 // The UpdateEngineClient implementation used in production.
66 class UpdateEngineClientImpl : public UpdateEngineClient {
67  public:
UpdateEngineClientImpl()68   UpdateEngineClientImpl() : update_engine_proxy_(nullptr), last_status_() {}
69 
70   ~UpdateEngineClientImpl() override = default;
71 
72   // UpdateEngineClient implementation:
AddObserver(Observer * observer)73   void AddObserver(Observer* observer) override {
74     observers_.AddObserver(observer);
75   }
76 
RemoveObserver(Observer * observer)77   void RemoveObserver(Observer* observer) override {
78     observers_.RemoveObserver(observer);
79   }
80 
HasObserver(const Observer * observer) const81   bool HasObserver(const Observer* observer) const override {
82     return observers_.HasObserver(observer);
83   }
84 
RequestUpdateCheck(UpdateCheckCallback callback)85   void RequestUpdateCheck(UpdateCheckCallback callback) override {
86     if (!service_available_) {
87       // TODO(alemate): we probably need to remember callbacks only.
88       // When service becomes available, we can do a single request,
89       // and trigger all callbacks with the same return value.
90       pending_tasks_.push_back(
91           base::BindOnce(&UpdateEngineClientImpl::RequestUpdateCheck,
92                          weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
93       return;
94     }
95     // TODO(crbug.com/982438): Use newer version of kAttemptUpdate instead once
96     // it was enhanced with protobuf arguments.
97     dbus::MethodCall method_call(update_engine::kUpdateEngineInterface,
98                                  update_engine::kAttemptUpdateWithFlags);
99     dbus::MessageWriter writer(&method_call);
100     writer.AppendString("");  // app_version
101     writer.AppendString("");  // omaha_url
102     writer.AppendInt32(0);    // flags, default is 0 (interactive). See
103                               // org.chromium.UpdateEngineInterface.dbus-xml.
104 
105     VLOG(1) << "Requesting an update check";
106     update_engine_proxy_->CallMethod(
107         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
108         base::BindOnce(&UpdateEngineClientImpl::OnRequestUpdateCheck,
109                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
110   }
111 
RebootAfterUpdate()112   void RebootAfterUpdate() override {
113     dbus::MethodCall method_call(
114         update_engine::kUpdateEngineInterface,
115         update_engine::kRebootIfNeeded);
116 
117     VLOG(1) << "Requesting a reboot";
118     update_engine_proxy_->CallMethod(
119         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
120         base::BindOnce(&UpdateEngineClientImpl::OnRebootAfterUpdate,
121                        weak_ptr_factory_.GetWeakPtr()));
122   }
123 
Rollback()124   void Rollback() override {
125     VLOG(1) << "Requesting a rollback";
126      dbus::MethodCall method_call(
127         update_engine::kUpdateEngineInterface,
128         update_engine::kAttemptRollback);
129     dbus::MessageWriter writer(&method_call);
130     writer.AppendBool(true /* powerwash */);
131 
132     update_engine_proxy_->CallMethod(
133         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
134         base::BindOnce(&UpdateEngineClientImpl::OnRollback,
135                        weak_ptr_factory_.GetWeakPtr()));
136   }
137 
CanRollbackCheck(RollbackCheckCallback callback)138   void CanRollbackCheck(RollbackCheckCallback callback) override {
139     dbus::MethodCall method_call(
140         update_engine::kUpdateEngineInterface,
141         update_engine::kCanRollback);
142 
143     VLOG(1) << "Requesting to get rollback availability status";
144     update_engine_proxy_->CallMethod(
145         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
146         base::BindOnce(&UpdateEngineClientImpl::OnCanRollbackCheck,
147                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
148   }
149 
GetLastStatus()150   update_engine::StatusResult GetLastStatus() override { return last_status_; }
151 
SetChannel(const std::string & target_channel,bool is_powerwash_allowed)152   void SetChannel(const std::string& target_channel,
153                   bool is_powerwash_allowed) override {
154     if (!IsValidChannel(target_channel)) {
155       LOG(ERROR) << "Invalid channel name: " << target_channel;
156       return;
157     }
158 
159     dbus::MethodCall method_call(
160         update_engine::kUpdateEngineInterface,
161         update_engine::kSetChannel);
162     dbus::MessageWriter writer(&method_call);
163     writer.AppendString(target_channel);
164     writer.AppendBool(is_powerwash_allowed);
165 
166     VLOG(1) << "Requesting to set channel: "
167             << "target_channel=" << target_channel << ", "
168             << "is_powerwash_allowed=" << is_powerwash_allowed;
169     update_engine_proxy_->CallMethod(
170         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
171         base::BindOnce(&UpdateEngineClientImpl::OnSetChannel,
172                        weak_ptr_factory_.GetWeakPtr()));
173   }
174 
GetChannel(bool get_current_channel,GetChannelCallback callback)175   void GetChannel(bool get_current_channel,
176                   GetChannelCallback callback) override {
177     dbus::MethodCall method_call(
178         update_engine::kUpdateEngineInterface,
179         update_engine::kGetChannel);
180     dbus::MessageWriter writer(&method_call);
181     writer.AppendBool(get_current_channel);
182 
183     VLOG(1) << "Requesting to get channel, get_current_channel="
184             << get_current_channel;
185     update_engine_proxy_->CallMethod(
186         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
187         base::BindOnce(&UpdateEngineClientImpl::OnGetChannel,
188                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
189   }
190 
GetEolInfo(GetEolInfoCallback callback)191   void GetEolInfo(GetEolInfoCallback callback) override {
192     dbus::MethodCall method_call(update_engine::kUpdateEngineInterface,
193                                  update_engine::kGetStatusAdvanced);
194 
195     VLOG(1) << "Requesting to get end of life status";
196     update_engine_proxy_->CallMethod(
197         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
198         base::BindOnce(&UpdateEngineClientImpl::OnGetEolInfo,
199                        weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
200   }
201 
SetUpdateOverCellularPermission(bool allowed,base::OnceClosure callback)202   void SetUpdateOverCellularPermission(bool allowed,
203                                        base::OnceClosure callback) override {
204     dbus::MethodCall method_call(
205         update_engine::kUpdateEngineInterface,
206         update_engine::kSetUpdateOverCellularPermission);
207     dbus::MessageWriter writer(&method_call);
208     writer.AppendBool(allowed);
209 
210     VLOG(1) << "Requesting UpdateEngine to " << (allowed ? "allow" : "prohibit")
211             << " updates over cellular.";
212 
213     return update_engine_proxy_->CallMethod(
214         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
215         base::BindOnce(
216             &UpdateEngineClientImpl::OnSetUpdateOverCellularPermission,
217             weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
218   }
219 
SetUpdateOverCellularOneTimePermission(const std::string & update_version,int64_t update_size,UpdateOverCellularOneTimePermissionCallback callback)220   void SetUpdateOverCellularOneTimePermission(
221       const std::string& update_version,
222       int64_t update_size,
223       UpdateOverCellularOneTimePermissionCallback callback) override {
224     // TODO(https://crbug.com/927439): Change 'kSetUpdateOverCellularTarget' to
225     // 'kSetUpdateOverCellularOneTimePermission'
226     dbus::MethodCall method_call(update_engine::kUpdateEngineInterface,
227                                  update_engine::kSetUpdateOverCellularTarget);
228     dbus::MessageWriter writer(&method_call);
229     writer.AppendString(update_version);
230     writer.AppendInt64(update_size);
231 
232     VLOG(1) << "Requesting UpdateEngine to allow updates over cellular "
233             << "to target version: \"" << update_version << "\" "
234             << "target_size: " << update_size;
235 
236     update_engine_proxy_->CallMethod(
237         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
238         base::BindOnce(
239             &UpdateEngineClientImpl::OnSetUpdateOverCellularOneTimePermission,
240             weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
241   }
242 
243  protected:
Init(dbus::Bus * bus)244   void Init(dbus::Bus* bus) override {
245     update_engine_proxy_ = bus->GetObjectProxy(
246         update_engine::kUpdateEngineServiceName,
247         dbus::ObjectPath(update_engine::kUpdateEngineServicePath));
248     update_engine_proxy_->ConnectToSignal(
249         update_engine::kUpdateEngineInterface,
250         update_engine::kStatusUpdateAdvanced,
251         base::BindRepeating(&UpdateEngineClientImpl::StatusUpdateReceived,
252                             weak_ptr_factory_.GetWeakPtr()),
253         base::BindOnce(&UpdateEngineClientImpl::StatusUpdateConnected,
254                        weak_ptr_factory_.GetWeakPtr()));
255     update_engine_proxy_->WaitForServiceToBeAvailable(
256         base::BindOnce(&UpdateEngineClientImpl::OnServiceInitiallyAvailable,
257                        weak_ptr_factory_.GetWeakPtr()));
258   }
259 
260  private:
OnServiceInitiallyAvailable(bool service_is_available)261   void OnServiceInitiallyAvailable(bool service_is_available) {
262     if (service_is_available) {
263       service_available_ = true;
264       std::vector<base::OnceClosure> callbacks;
265       callbacks.swap(pending_tasks_);
266       for (auto& callback : callbacks) {
267         std::move(callback).Run();
268       }
269 
270       // Get update engine status for the initial status. Update engine won't
271       // send StatusUpdate signal unless there is a status change. If chrome
272       // crashes after UPDATED_NEED_REBOOT status is set, restarted chrome would
273       // not get this status. See crbug.com/154104.
274       GetUpdateEngineStatus();
275     } else {
276       LOG(ERROR) << "Failed to wait for D-Bus service to become available";
277       pending_tasks_.clear();
278     }
279   }
280 
GetUpdateEngineStatus()281   void GetUpdateEngineStatus() {
282     // TODO(crbug.com/977320): Rename the method call back to GetStatus() after
283     // the interface changed.
284     dbus::MethodCall method_call(update_engine::kUpdateEngineInterface,
285                                  update_engine::kGetStatusAdvanced);
286     update_engine_proxy_->CallMethodWithErrorCallback(
287         &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
288         base::BindOnce(&UpdateEngineClientImpl::OnGetStatus,
289                        weak_ptr_factory_.GetWeakPtr()),
290         base::BindOnce(&UpdateEngineClientImpl::OnGetStatusError,
291                        weak_ptr_factory_.GetWeakPtr()));
292   }
293 
294   // Called when a response for RequestUpdateCheck() is received.
OnRequestUpdateCheck(UpdateCheckCallback callback,dbus::Response * response)295   void OnRequestUpdateCheck(UpdateCheckCallback callback,
296                             dbus::Response* response) {
297     if (!response) {
298       LOG(ERROR) << "Failed to request update check";
299       std::move(callback).Run(UPDATE_RESULT_FAILED);
300       return;
301     }
302     std::move(callback).Run(UPDATE_RESULT_SUCCESS);
303   }
304 
305   // Called when a response for RebootAfterUpdate() is received.
OnRebootAfterUpdate(dbus::Response * response)306   void OnRebootAfterUpdate(dbus::Response* response) {
307     if (!response) {
308       LOG(ERROR) << "Failed to request rebooting after update";
309       return;
310     }
311   }
312 
313   // Called when a response for Rollback() is received.
OnRollback(dbus::Response * response)314   void OnRollback(dbus::Response* response) {
315     if (!response) {
316       LOG(ERROR) << "Failed to rollback";
317       return;
318     }
319   }
320 
321   // Called when a response for CanRollbackCheck() is received.
OnCanRollbackCheck(RollbackCheckCallback callback,dbus::Response * response)322   void OnCanRollbackCheck(RollbackCheckCallback callback,
323                           dbus::Response* response) {
324     if (!response) {
325       LOG(ERROR) << "Failed to request rollback availability status";
326       std::move(callback).Run(false);
327       return;
328     }
329     dbus::MessageReader reader(response);
330     bool can_rollback;
331     if (!reader.PopBool(&can_rollback)) {
332       LOG(ERROR) << "Incorrect response: " << response->ToString();
333       std::move(callback).Run(false);
334       return;
335     }
336     VLOG(1) << "Rollback availability status received: " << can_rollback;
337     std::move(callback).Run(can_rollback);
338   }
339 
340   // Called when a response for GetStatus is received.
OnGetStatus(dbus::Response * response)341   void OnGetStatus(dbus::Response* response) {
342     if (!response) {
343       LOG(ERROR) << "Failed to get response for GetStatus request.";
344       return;
345     }
346 
347     dbus::MessageReader reader(response);
348     update_engine::StatusResult status;
349     if (!reader.PopArrayOfBytesAsProto(&status)) {
350       LOG(ERROR) << "Failed to parse proto from DBus Response.";
351       return;
352     }
353 
354     last_status_ = status;
355     for (auto& observer : observers_)
356       observer.UpdateStatusChanged(status);
357   }
358 
359   // Called when GetStatus call failed.
OnGetStatusError(dbus::ErrorResponse * error)360   void OnGetStatusError(dbus::ErrorResponse* error) {
361     LOG(ERROR) << "GetStatus request failed with error: "
362                << (error ? error->ToString() : "");
363   }
364 
365   // Called when a response for SetReleaseChannel() is received.
OnSetChannel(dbus::Response * response)366   void OnSetChannel(dbus::Response* response) {
367     if (!response) {
368       LOG(ERROR) << "Failed to request setting channel";
369       return;
370     }
371     VLOG(1) << "Succeeded to set channel";
372   }
373 
374   // Called when a response for GetChannel() is received.
OnGetChannel(GetChannelCallback callback,dbus::Response * response)375   void OnGetChannel(GetChannelCallback callback, dbus::Response* response) {
376     if (!response) {
377       LOG(ERROR) << "Failed to request getting channel";
378       std::move(callback).Run("");
379       return;
380     }
381     dbus::MessageReader reader(response);
382     std::string channel;
383     if (!reader.PopString(&channel)) {
384       LOG(ERROR) << "Incorrect response: " << response->ToString();
385       std::move(callback).Run("");
386       return;
387     }
388     VLOG(1) << "The channel received: " << channel;
389     std::move(callback).Run(channel);
390   }
391 
392   // Called when a response for GetStatusAdvanced() is
393   // received.
OnGetEolInfo(GetEolInfoCallback callback,dbus::Response * response)394   void OnGetEolInfo(GetEolInfoCallback callback, dbus::Response* response) {
395     if (!response) {
396       LOG(ERROR) << "Failed to request getting eol info.";
397       std::move(callback).Run(EolInfo());
398       return;
399     }
400 
401     dbus::MessageReader reader(response);
402     update_engine::StatusResult status;
403     if (!reader.PopArrayOfBytesAsProto(&status)) {
404       LOG(ERROR) << "Failed to parse proto from DBus Response.";
405       std::move(callback).Run(EolInfo());
406       return;
407     }
408 
409     VLOG(1) << "Eol date received: " << status.eol_date();
410 
411     EolInfo eol_info;
412     if (status.eol_date() > 0) {
413       eol_info.eol_date = base::Time::UnixEpoch() +
414                           base::TimeDelta::FromDays(status.eol_date());
415     }
416     std::move(callback).Run(eol_info);
417   }
418 
419   // Called when a response for SetUpdateOverCellularPermission() is received.
OnSetUpdateOverCellularPermission(base::OnceClosure callback,dbus::Response * response)420   void OnSetUpdateOverCellularPermission(base::OnceClosure callback,
421                                          dbus::Response* response) {
422     if (!response) {
423       LOG(ERROR) << update_engine::kSetUpdateOverCellularPermission
424                  << " call failed";
425     }
426 
427     // Callback should run anyway, regardless of whether DBus call to enable
428     // update over cellular succeeded or failed.
429     std::move(callback).Run();
430   }
431 
432   // Called when a response for SetUpdateOverCellularOneTimePermission() is
433   // received.
OnSetUpdateOverCellularOneTimePermission(UpdateOverCellularOneTimePermissionCallback callback,dbus::Response * response)434   void OnSetUpdateOverCellularOneTimePermission(
435       UpdateOverCellularOneTimePermissionCallback callback,
436       dbus::Response* response) {
437     bool success = true;
438     if (!response) {
439       success = false;
440       LOG(ERROR) << update_engine::kSetUpdateOverCellularTarget
441                  << " call failed";
442     }
443 
444     if (success) {
445       for (auto& observer : observers_) {
446         observer.OnUpdateOverCellularOneTimePermissionGranted();
447       }
448     }
449 
450     std::move(callback).Run(success);
451   }
452 
453   // Called when a status update signal is received.
StatusUpdateReceived(dbus::Signal * signal)454   void StatusUpdateReceived(dbus::Signal* signal) {
455     VLOG(1) << "Status update signal received: " << signal->ToString();
456     dbus::MessageReader reader(signal);
457     update_engine::StatusResult status;
458     if (!reader.PopArrayOfBytesAsProto(&status)) {
459       LOG(ERROR) << "Failed to parse proto from DBus Response.";
460       return;
461     }
462 
463     last_status_ = status;
464     for (auto& observer : observers_)
465       observer.UpdateStatusChanged(status);
466   }
467 
468   // Called when the status update signal is initially connected.
StatusUpdateConnected(const std::string & interface_name,const std::string & signal_name,bool success)469   void StatusUpdateConnected(const std::string& interface_name,
470                              const std::string& signal_name,
471                              bool success) {
472     LOG_IF(WARNING, !success)
473         << "Failed to connect to status updated signal.";
474   }
475 
476   dbus::ObjectProxy* update_engine_proxy_;
477   base::ObserverList<Observer>::Unchecked observers_;
478   update_engine::StatusResult last_status_;
479 
480   // True after update_engine's D-Bus service has become available.
481   bool service_available_ = false;
482 
483   // This is a list of postponed calls to update engine to be called
484   // after it becomes available.
485   std::vector<base::OnceClosure> pending_tasks_;
486 
487   // Note: This should remain the last member so it'll be destroyed and
488   // invalidate its weak pointers before any other members are destroyed.
489   base::WeakPtrFactory<UpdateEngineClientImpl> weak_ptr_factory_{this};
490 
491   DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientImpl);
492 };
493 
494 // The UpdateEngineClient implementation used on Linux desktop,
495 // which does nothing.
496 class UpdateEngineClientStubImpl : public UpdateEngineClient {
497  public:
UpdateEngineClientStubImpl()498   UpdateEngineClientStubImpl()
499       : current_channel_(kReleaseChannelBeta),
500         target_channel_(kReleaseChannelBeta) {}
501 
502   // UpdateEngineClient implementation:
Init(dbus::Bus * bus)503   void Init(dbus::Bus* bus) override {}
504 
AddObserver(Observer * observer)505   void AddObserver(Observer* observer) override {
506     observers_.AddObserver(observer);
507   }
508 
RemoveObserver(Observer * observer)509   void RemoveObserver(Observer* observer) override {
510     observers_.RemoveObserver(observer);
511   }
512 
HasObserver(const Observer * observer) const513   bool HasObserver(const Observer* observer) const override {
514     return observers_.HasObserver(observer);
515   }
516 
RequestUpdateCheck(UpdateCheckCallback callback)517   void RequestUpdateCheck(UpdateCheckCallback callback) override {
518     if (last_status_.current_operation() != update_engine::Operation::IDLE) {
519       std::move(callback).Run(UPDATE_RESULT_FAILED);
520       return;
521     }
522     std::move(callback).Run(UPDATE_RESULT_SUCCESS);
523     last_status_.set_current_operation(
524         update_engine::Operation::CHECKING_FOR_UPDATE);
525     last_status_.set_progress(0.0);
526     last_status_.set_last_checked_time(0);
527     last_status_.set_new_version("0.0.0.0");
528     last_status_.set_new_size(0);
529     last_status_.set_is_enterprise_rollback(false);
530     base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
531         FROM_HERE,
532         base::BindOnce(&UpdateEngineClientStubImpl::StateTransition,
533                        weak_factory_.GetWeakPtr()),
534         base::TimeDelta::FromMilliseconds(kStateTransitionDefaultDelayMs));
535   }
536 
RebootAfterUpdate()537   void RebootAfterUpdate() override {}
538 
Rollback()539   void Rollback() override {}
540 
CanRollbackCheck(RollbackCheckCallback callback)541   void CanRollbackCheck(RollbackCheckCallback callback) override {
542     std::move(callback).Run(true);
543   }
544 
GetLastStatus()545   update_engine::StatusResult GetLastStatus() override { return last_status_; }
546 
SetChannel(const std::string & target_channel,bool is_powerwash_allowed)547   void SetChannel(const std::string& target_channel,
548                   bool is_powerwash_allowed) override {
549     VLOG(1) << "Requesting to set channel: "
550             << "target_channel=" << target_channel << ", "
551             << "is_powerwash_allowed=" << is_powerwash_allowed;
552     target_channel_ = target_channel;
553   }
GetChannel(bool get_current_channel,GetChannelCallback callback)554   void GetChannel(bool get_current_channel,
555                   GetChannelCallback callback) override {
556     VLOG(1) << "Requesting to get channel, get_current_channel="
557             << get_current_channel;
558     if (get_current_channel)
559       std::move(callback).Run(current_channel_);
560     else
561       std::move(callback).Run(target_channel_);
562   }
563 
GetEolInfo(GetEolInfoCallback callback)564   void GetEolInfo(GetEolInfoCallback callback) override {
565     std::move(callback).Run(EolInfo());
566   }
567 
SetUpdateOverCellularPermission(bool allowed,base::OnceClosure callback)568   void SetUpdateOverCellularPermission(bool allowed,
569                                        base::OnceClosure callback) override {
570     std::move(callback).Run();
571   }
572 
SetUpdateOverCellularOneTimePermission(const std::string & update_version,int64_t update_size,UpdateOverCellularOneTimePermissionCallback callback)573   void SetUpdateOverCellularOneTimePermission(
574       const std::string& update_version,
575       int64_t update_size,
576       UpdateOverCellularOneTimePermissionCallback callback) override {}
577 
578  private:
StateTransition()579   void StateTransition() {
580     update_engine::Operation next_operation = update_engine::Operation::ERROR;
581     int delay_ms = kStateTransitionDefaultDelayMs;
582     switch (last_status_.current_operation()) {
583       case update_engine::Operation::ERROR:
584       case update_engine::Operation::IDLE:
585       case update_engine::Operation::UPDATED_NEED_REBOOT:
586       case update_engine::Operation::REPORTING_ERROR_EVENT:
587       case update_engine::Operation::ATTEMPTING_ROLLBACK:
588       case update_engine::Operation::NEED_PERMISSION_TO_UPDATE:
589       case update_engine::Operation::DISABLED:
590         return;
591       case update_engine::Operation::CHECKING_FOR_UPDATE:
592         next_operation = update_engine::Operation::UPDATE_AVAILABLE;
593         break;
594       case update_engine::Operation::UPDATE_AVAILABLE:
595         next_operation = update_engine::Operation::DOWNLOADING;
596         break;
597       case update_engine::Operation::DOWNLOADING:
598         if (last_status_.progress() >= 1.0) {
599           next_operation = update_engine::Operation::VERIFYING;
600         } else {
601           next_operation = update_engine::Operation::DOWNLOADING;
602           last_status_.set_progress(last_status_.progress() + 0.01);
603           last_status_.set_new_version(kStubVersion);
604           last_status_.set_new_size(kDownloadSizeDelta);
605           delay_ms = kStateTransitionDownloadingDelayMs;
606         }
607         break;
608       case update_engine::Operation::VERIFYING:
609         next_operation = update_engine::Operation::FINALIZING;
610         break;
611       case update_engine::Operation::FINALIZING:
612         next_operation = update_engine::Operation::UPDATED_NEED_REBOOT;
613         break;
614       default:
615         NOTREACHED();
616     }
617     last_status_.set_current_operation(next_operation);
618     for (auto& observer : observers_)
619       observer.UpdateStatusChanged(last_status_);
620     if (last_status_.current_operation() != update_engine::Operation::IDLE) {
621       base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
622           FROM_HERE,
623           base::BindOnce(&UpdateEngineClientStubImpl::StateTransition,
624                          weak_factory_.GetWeakPtr()),
625           base::TimeDelta::FromMilliseconds(delay_ms));
626     }
627   }
628 
629   base::ObserverList<Observer>::Unchecked observers_;
630 
631   std::string current_channel_;
632   std::string target_channel_;
633 
634   update_engine::StatusResult last_status_;
635 
636   base::WeakPtrFactory<UpdateEngineClientStubImpl> weak_factory_{this};
637 
638   DISALLOW_COPY_AND_ASSIGN(UpdateEngineClientStubImpl);
639 };
640 
641 UpdateEngineClient::UpdateEngineClient() = default;
642 
643 UpdateEngineClient::~UpdateEngineClient() = default;
644 
645 // static
Create(DBusClientImplementationType type)646 UpdateEngineClient* UpdateEngineClient::Create(
647     DBusClientImplementationType type) {
648   if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
649     return new UpdateEngineClientImpl();
650   DCHECK_EQ(FAKE_DBUS_CLIENT_IMPLEMENTATION, type);
651   return new UpdateEngineClientStubImpl();
652 }
653 
654 // static
IsTargetChannelMoreStable(const std::string & current_channel,const std::string & target_channel)655 bool UpdateEngineClient::IsTargetChannelMoreStable(
656     const std::string& current_channel,
657     const std::string& target_channel) {
658   const char** cix = std::find(
659       kReleaseChannelsList,
660       kReleaseChannelsList + base::size(kReleaseChannelsList), current_channel);
661   const char** tix = std::find(
662       kReleaseChannelsList,
663       kReleaseChannelsList + base::size(kReleaseChannelsList), target_channel);
664   return tix > cix;
665 }
666 
667 }  // namespace chromeos
668