1 //
2 // Copyright (C) 2015 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 #include "update_engine/client_library/client_dbus.h"
18 
19 #include <base/message_loop/message_loop.h>
20 
21 #include <dbus/bus.h>
22 #include <update_engine/dbus-constants.h>
23 #include <update_engine/proto_bindings/update_engine.pb.h>
24 
25 #include "update_engine/update_status_utils.h"
26 
27 using chromeos_update_engine::StringToUpdateStatus;
28 using dbus::Bus;
29 using org::chromium::UpdateEngineInterfaceProxy;
30 using std::string;
31 using std::vector;
32 
33 namespace update_engine {
34 namespace internal {
35 
36 bool DBusUpdateEngineClient::Init() {
37   Bus::Options options;
38   options.bus_type = Bus::SYSTEM;
39   scoped_refptr<Bus> bus{new Bus{options}};
40 
41   if (!bus->Connect())
42     return false;
43 
44   proxy_.reset(new UpdateEngineInterfaceProxy{bus});
45   return true;
46 }
47 
48 bool DBusUpdateEngineClient::AttemptUpdate(const string& in_app_version,
49                                            const string& in_omaha_url,
50                                            bool at_user_request) {
51   return proxy_->AttemptUpdateWithFlags(
52       in_app_version,
53       in_omaha_url,
54       (at_user_request)
55           ? 0
56           : update_engine::UpdateAttemptFlags::kFlagNonInteractive,
57       nullptr);
58 }
59 
60 bool DBusUpdateEngineClient::AttemptInstall(
61     const string& omaha_url, const vector<string>& dlc_module_ids) {
62   // Convert parameters into protobuf.
63   chromeos_update_engine::DlcParameters dlc_parameters;
64   dlc_parameters.set_omaha_url(omaha_url);
65   for (const auto& dlc_module_id : dlc_module_ids) {
66     chromeos_update_engine::DlcInfo* dlc_info = dlc_parameters.add_dlc_infos();
67     dlc_info->set_dlc_id(dlc_module_id);
68   }
69   string dlc_request;
70   if (dlc_parameters.SerializeToString(&dlc_request)) {
71     return proxy_->AttemptInstall(dlc_request, nullptr /* brillo::ErrorPtr* */);
72   } else {
73     LOG(ERROR) << "Fail to serialize a protobuf to a string.";
74     return false;
75   }
76 }
77 
78 bool DBusUpdateEngineClient::GetStatus(int64_t* out_last_checked_time,
79                                        double* out_progress,
80                                        UpdateStatus* out_update_status,
81                                        string* out_new_version,
82                                        int64_t* out_new_size) const {
83   string status_as_string;
84   const bool success = proxy_->GetStatus(out_last_checked_time,
85                                          out_progress,
86                                          &status_as_string,
87                                          out_new_version,
88                                          out_new_size,
89                                          nullptr);
90   if (!success) {
91     return false;
92   }
93 
94   return StringToUpdateStatus(status_as_string, out_update_status);
95 }
96 
97 bool DBusUpdateEngineClient::SetCohortHint(const string& cohort_hint) {
98   return proxy_->SetCohortHint(cohort_hint, nullptr);
99 }
100 
101 bool DBusUpdateEngineClient::GetCohortHint(string* cohort_hint) const {
102   return proxy_->GetCohortHint(cohort_hint, nullptr);
103 }
104 
105 bool DBusUpdateEngineClient::SetUpdateOverCellularPermission(bool allowed) {
106   return proxy_->SetUpdateOverCellularPermission(allowed, nullptr);
107 }
108 
109 bool DBusUpdateEngineClient::GetUpdateOverCellularPermission(
110     bool* allowed) const {
111   return proxy_->GetUpdateOverCellularPermission(allowed, nullptr);
112 }
113 
114 bool DBusUpdateEngineClient::SetP2PUpdatePermission(bool enabled) {
115   return proxy_->SetP2PUpdatePermission(enabled, nullptr);
116 }
117 
118 bool DBusUpdateEngineClient::GetP2PUpdatePermission(bool* enabled) const {
119   return proxy_->GetP2PUpdatePermission(enabled, nullptr);
120 }
121 
122 bool DBusUpdateEngineClient::Rollback(bool powerwash) {
123   return proxy_->AttemptRollback(powerwash, nullptr);
124 }
125 
126 bool DBusUpdateEngineClient::GetRollbackPartition(
127     string* rollback_partition) const {
128   return proxy_->GetRollbackPartition(rollback_partition, nullptr);
129 }
130 
131 bool DBusUpdateEngineClient::GetPrevVersion(string* prev_version) const {
132   return proxy_->GetPrevVersion(prev_version, nullptr);
133 }
134 
135 void DBusUpdateEngineClient::RebootIfNeeded() {
136   bool ret = proxy_->RebootIfNeeded(nullptr);
137   if (!ret) {
138     // Reboot error code doesn't necessarily mean that a reboot
139     // failed. For example, D-Bus may be shutdown before we receive the
140     // result.
141     LOG(INFO) << "RebootIfNeeded() failure ignored.";
142   }
143 }
144 
145 bool DBusUpdateEngineClient::ResetStatus() {
146   return proxy_->ResetStatus(nullptr);
147 }
148 
149 void DBusUpdateEngineClient::DBusStatusHandlersRegistered(
150     const string& interface, const string& signal_name, bool success) const {
151   if (!success) {
152     for (auto handler : handlers_) {
153       handler->IPCError("Could not connect to" + signal_name + " on " +
154                         interface);
155     }
156   } else {
157     StatusUpdateHandlersRegistered(nullptr);
158   }
159 }
160 
161 void DBusUpdateEngineClient::StatusUpdateHandlersRegistered(
162     StatusUpdateHandler* handler) const {
163   int64_t last_checked_time;
164   double progress;
165   UpdateStatus update_status;
166   string new_version;
167   int64_t new_size;
168 
169   if (!GetStatus(&last_checked_time,
170                  &progress,
171                  &update_status,
172                  &new_version,
173                  &new_size)) {
174     handler->IPCError("Could not query current status");
175     return;
176   }
177 
178   std::vector<update_engine::StatusUpdateHandler*> just_handler = {handler};
179   for (auto h : handler ? just_handler : handlers_) {
180     h->HandleStatusUpdate(
181         last_checked_time, progress, update_status, new_version, new_size);
182   }
183 }
184 
185 void DBusUpdateEngineClient::RunStatusUpdateHandlers(
186     int64_t last_checked_time,
187     double progress,
188     const string& current_operation,
189     const string& new_version,
190     int64_t new_size) {
191   UpdateStatus status;
192   StringToUpdateStatus(current_operation, &status);
193 
194   for (auto handler : handlers_) {
195     handler->HandleStatusUpdate(
196         last_checked_time, progress, status, new_version, new_size);
197   }
198 }
199 
200 bool DBusUpdateEngineClient::UnregisterStatusUpdateHandler(
201     StatusUpdateHandler* handler) {
202   auto it = std::find(handlers_.begin(), handlers_.end(), handler);
203   if (it != handlers_.end()) {
204     handlers_.erase(it);
205     return true;
206   }
207 
208   return false;
209 }
210 
211 bool DBusUpdateEngineClient::RegisterStatusUpdateHandler(
212     StatusUpdateHandler* handler) {
213   if (!base::MessageLoopForIO::current()) {
214     LOG(FATAL) << "Cannot get UpdateEngineClient outside of message loop.";
215     return false;
216   }
217 
218   handlers_.push_back(handler);
219 
220   if (dbus_handler_registered_) {
221     StatusUpdateHandlersRegistered(handler);
222     return true;
223   }
224 
225   proxy_->RegisterStatusUpdateSignalHandler(
226       base::Bind(&DBusUpdateEngineClient::RunStatusUpdateHandlers,
227                  base::Unretained(this)),
228       base::Bind(&DBusUpdateEngineClient::DBusStatusHandlersRegistered,
229                  base::Unretained(this)));
230 
231   dbus_handler_registered_ = true;
232 
233   return true;
234 }
235 
236 bool DBusUpdateEngineClient::SetTargetChannel(const string& in_target_channel,
237                                               bool allow_powerwash) {
238   return proxy_->SetChannel(in_target_channel, allow_powerwash, nullptr);
239 }
240 
241 bool DBusUpdateEngineClient::GetTargetChannel(string* out_channel) const {
242   return proxy_->GetChannel(false,  // Get the target channel.
243                             out_channel,
244                             nullptr);
245 }
246 
247 bool DBusUpdateEngineClient::GetChannel(string* out_channel) const {
248   return proxy_->GetChannel(true,  // Get the current channel.
249                             out_channel,
250                             nullptr);
251 }
252 
253 bool DBusUpdateEngineClient::GetLastAttemptError(
254     int32_t* last_attempt_error) const {
255   return proxy_->GetLastAttemptError(last_attempt_error, nullptr);
256 }
257 
258 bool DBusUpdateEngineClient::GetEolStatus(int32_t* eol_status) const {
259   return proxy_->GetEolStatus(eol_status, nullptr);
260 }
261 
262 }  // namespace internal
263 }  // namespace update_engine
264