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#include <time.h>
17
18#include <base/message_loop/message_loop.h>
19
20#include "proxy_dbus_client.h"
21
22const char ProxyDbusClient::kCommonLogScopes[] =
23  "connection+dbus+device+link+manager+portal+service";
24const int ProxyDbusClient::kLogLevel = -4;
25const char ProxyDbusClient::kDbusErrorObjectUnknown[] =
26  "org.freedesktop.DBus.Error.UnknownObject";
27
28namespace {
29template<typename Proxy> bool GetPropertyValueFromProxy(
30    Proxy* proxy,
31    const std::string& property_name,
32    brillo::Any* property_value) {
33  CHECK(property_value);
34  brillo::VariantDictionary proxy_properties;
35  brillo::ErrorPtr error;
36  CHECK(proxy->GetProperties(&proxy_properties, &error));
37  if (proxy_properties.find(property_name) == proxy_properties.end()) {
38    return false;
39  }
40  *property_value = proxy_properties[property_name];
41  return true;
42}
43
44template<typename Proxy> void IsProxyPropertyValueIn(
45    Proxy* proxy,
46    const std::string& property_name,
47    const std::vector<brillo::Any>& expected_values,
48    base::Time wait_start_time,
49    bool* is_success,
50    brillo::Any* final_value,
51    long* elapsed_time_milliseconds) {
52  brillo::Any property_value;
53  *is_success = false;
54  if ((GetPropertyValueFromProxy<Proxy>(proxy, property_name, &property_value)) &&
55      (std::find(expected_values.begin(), expected_values.end(),
56                 property_value) != expected_values.end())) {
57    *is_success = true;
58  }
59  if (final_value) {
60    *final_value = property_value;
61  }
62  if (elapsed_time_milliseconds) {
63    *elapsed_time_milliseconds =
64        (base::Time::Now() - wait_start_time).InMilliseconds();
65  }
66}
67
68// This is invoked when the dbus detects a change in one of
69// the properties of the proxy. We need to check if the property
70// we're interested in has reached one of the expected values.
71void PropertyChangedSignalCallback(
72    const std::string& watched_property_name,
73    const std::vector<brillo::Any>& expected_values,
74    const std::string& changed_property_name,
75    const brillo::Any& new_property_value) {
76  if ((watched_property_name == changed_property_name) &&
77      (std::find(expected_values.begin(), expected_values.end(),
78                 new_property_value) != expected_values.end())) {
79    // Unblock the waiting function by stopping the message loop.
80    base::MessageLoop::current()->QuitNow();
81  }
82}
83
84// This is invoked to indicate whether dbus successfully connected our
85// signal callback or not.
86void PropertyChangedOnConnectedCallback(
87    const std::string& /* watched_property_name */,
88    const std::string& /* interface */,
89    const std::string& /* signal_name */,
90    bool success) {
91  CHECK(success);
92}
93
94template<typename Proxy>
95void HelpRegisterPropertyChangedSignalHandler(
96    Proxy* proxy,
97    dbus::ObjectProxy::OnConnectedCallback on_connected_callback,
98    const DbusPropertyChangeCallback& signal_callback) {
99  // Re-order |on_connected_callback| and |signal_callback|, to meet
100  // the requirements of RegisterPropertyChangedSignalHandler().
101  proxy->RegisterPropertyChangedSignalHandler(
102      signal_callback, on_connected_callback);
103}
104
105template<typename OutValueType, typename ConditionChangeCallbackType>
106void WaitForCondition(
107    base::Callback<void(base::Time, bool*, OutValueType*, long*)>
108        condition_termination_checker,
109    base::Callback<ConditionChangeCallbackType> condition_change_callback,
110    base::Callback<void(const base::Callback<ConditionChangeCallbackType>&)>
111        condition_change_callback_registrar,
112    long timeout_milliseconds,
113    bool* is_success,
114    OutValueType* out_value,
115    long* elapsed_time_milliseconds) {
116  CHECK(is_success);
117  const base::Time wait_start_time(base::Time::Now());
118  const base::TimeDelta timeout(
119      base::TimeDelta::FromMilliseconds(timeout_milliseconds));
120  base::CancelableClosure wait_timeout_callback;
121  base::CancelableCallback<ConditionChangeCallbackType> change_callback;
122
123  condition_termination_checker.Run(
124      wait_start_time, is_success, out_value, elapsed_time_milliseconds);
125  if (*is_success) {
126    return;
127  }
128
129  wait_timeout_callback.Reset(base::MessageLoop::QuitWhenIdleClosure());
130  change_callback.Reset(condition_change_callback);
131
132  condition_change_callback_registrar.Run(change_callback.callback());
133
134  // Add timeout, in case we never hit the expected condition.
135  base::MessageLoop::current()->PostDelayedTask(
136      FROM_HERE,
137      wait_timeout_callback.callback(),
138      timeout);
139
140  // Wait for the condition to occur within |timeout_milliseconds|.
141  base::MessageLoop::current()->Run();
142
143  wait_timeout_callback.Cancel();
144  change_callback.Cancel();
145
146  // We could have reached here either because we timed out or
147  // because we reached the condition.
148  condition_termination_checker.Run(
149      wait_start_time, is_success, out_value, elapsed_time_milliseconds);
150}
151} // namespace
152
153ProxyDbusClient::ProxyDbusClient(scoped_refptr<dbus::Bus> bus)
154  : dbus_bus_(bus),
155    shill_manager_proxy_(dbus_bus_),
156    weak_ptr_factory_(this) {
157}
158
159void ProxyDbusClient::SetLogging(Technology tech) {
160  std::string log_scopes(kCommonLogScopes);
161  switch (tech) {
162    case TECHNOLOGY_CELLULAR:
163      log_scopes += "+cellular";
164      break;
165    case TECHNOLOGY_ETHERNET:
166      log_scopes += "+ethernet";
167      break;
168    case TECHNOLOGY_VPN:
169      log_scopes += "+vpn";
170      break;
171    case TECHNOLOGY_WIFI:
172      log_scopes += "+wifi";
173      break;
174    case TECHNOLOGY_WIMAX:
175      log_scopes += "+wimax";
176      break;
177  }
178  SetLoggingInternal(kLogLevel, log_scopes);
179}
180
181std::vector<std::unique_ptr<DeviceProxy>> ProxyDbusClient::GetDeviceProxies() {
182  return GetProxies<DeviceProxy>(shill::kDevicesProperty);
183}
184
185std::vector<std::unique_ptr<ServiceProxy>> ProxyDbusClient::GetServiceProxies() {
186  return GetProxies<ServiceProxy>(shill::kServicesProperty);
187}
188
189std::vector<std::unique_ptr<ProfileProxy>> ProxyDbusClient::GetProfileProxies() {
190  return GetProxies<ProfileProxy>(shill::kProfilesProperty);
191}
192
193std::unique_ptr<DeviceProxy> ProxyDbusClient::GetMatchingDeviceProxy(
194    const brillo::VariantDictionary& expected_properties) {
195  return GetMatchingProxy<DeviceProxy>(shill::kDevicesProperty, expected_properties);
196}
197
198std::unique_ptr<ServiceProxy> ProxyDbusClient::GetMatchingServiceProxy(
199    const brillo::VariantDictionary& expected_properties) {
200  return GetMatchingProxy<ServiceProxy>(shill::kServicesProperty, expected_properties);
201}
202
203std::unique_ptr<ProfileProxy> ProxyDbusClient::GetMatchingProfileProxy(
204    const brillo::VariantDictionary& expected_properties) {
205  return GetMatchingProxy<ProfileProxy>(shill::kProfilesProperty, expected_properties);
206}
207
208bool ProxyDbusClient::GetPropertyValueFromDeviceProxy(
209    DeviceProxy* proxy,
210    const std::string& property_name,
211    brillo::Any* property_value) {
212  return GetPropertyValueFromProxy<DeviceProxy>(
213      proxy, property_name, property_value);
214}
215
216bool ProxyDbusClient::GetPropertyValueFromServiceProxy(
217    ServiceProxy* proxy,
218    const std::string& property_name,
219    brillo::Any* property_value) {
220  return GetPropertyValueFromProxy<ServiceProxy>(
221      proxy, property_name, property_value);
222}
223
224bool ProxyDbusClient::GetPropertyValueFromProfileProxy(
225    ProfileProxy* proxy,
226    const std::string& property_name,
227    brillo::Any* property_value) {
228  return GetPropertyValueFromProxy<ProfileProxy>(
229      proxy, property_name, property_value);
230}
231
232bool ProxyDbusClient::WaitForDeviceProxyPropertyValueIn(
233    const dbus::ObjectPath& object_path,
234    const std::string& property_name,
235    const std::vector<brillo::Any>& expected_values,
236    long timeout_milliseconds,
237    brillo::Any* final_value,
238    long* elapsed_time_milliseconds) {
239  return WaitForProxyPropertyValueIn<DeviceProxy>(
240      object_path, property_name, expected_values, timeout_milliseconds,
241      final_value, elapsed_time_milliseconds);
242}
243
244bool ProxyDbusClient::WaitForServiceProxyPropertyValueIn(
245    const dbus::ObjectPath& object_path,
246    const std::string& property_name,
247    const std::vector<brillo::Any>& expected_values,
248    long timeout_milliseconds,
249    brillo::Any* final_value,
250    long* elapsed_time_milliseconds) {
251  return WaitForProxyPropertyValueIn<ServiceProxy>(
252      object_path, property_name, expected_values, timeout_milliseconds,
253      final_value, elapsed_time_milliseconds);
254}
255
256bool ProxyDbusClient::WaitForProfileProxyPropertyValueIn(
257    const dbus::ObjectPath& object_path,
258    const std::string& property_name,
259    const std::vector<brillo::Any>& expected_values,
260    long timeout_milliseconds,
261    brillo::Any* final_value,
262    long* elapsed_time_milliseconds) {
263  return WaitForProxyPropertyValueIn<ProfileProxy>(
264      object_path, property_name, expected_values, timeout_milliseconds,
265      final_value, elapsed_time_milliseconds);
266}
267
268std::unique_ptr<ServiceProxy> ProxyDbusClient::GetServiceProxy(
269    const brillo::VariantDictionary& expected_properties) {
270  dbus::ObjectPath service_path;
271  brillo::ErrorPtr error;
272  if (!shill_manager_proxy_.GetService(
273          expected_properties, &service_path, &error)) {
274    return nullptr;
275  }
276  return std::unique_ptr<ServiceProxy>(
277      new ServiceProxy(dbus_bus_, service_path));
278}
279
280std::unique_ptr<ProfileProxy> ProxyDbusClient::GetActiveProfileProxy() {
281  return GetProxyForObjectPath<ProfileProxy>(GetObjectPathForActiveProfile());
282}
283
284std::unique_ptr<ServiceProxy> ProxyDbusClient::WaitForMatchingServiceProxy(
285    const brillo::VariantDictionary& service_properties,
286    const std::string& service_type,
287    long timeout_milliseconds,
288    int rescan_interval_milliseconds,
289    long* elapsed_time_milliseconds) {
290  auto condition_termination_checker =
291      base::Bind(&ProxyDbusClient::IsMatchingServicePresent,
292                 weak_ptr_factory_.GetWeakPtr(),
293                 service_properties);
294  auto condition_change_callback =
295      base::Bind(&ProxyDbusClient::FindServiceOrRestartScan,
296                 weak_ptr_factory_.GetWeakPtr(),
297                 service_properties,
298                 service_type);
299  auto condition_change_callback_registrar =
300      base::Bind(&ProxyDbusClient::InitiateScanForService,
301                 weak_ptr_factory_.GetWeakPtr(),
302                 base::TimeDelta::FromMilliseconds(rescan_interval_milliseconds),
303                 service_type);
304
305  std::unique_ptr<ServiceProxy> service_proxy;
306  bool is_success;
307  WaitForCondition(
308      condition_termination_checker, condition_change_callback,
309      condition_change_callback_registrar,
310      timeout_milliseconds, &is_success, &service_proxy, elapsed_time_milliseconds);
311  return service_proxy;
312}
313
314bool ProxyDbusClient::ConfigureService(
315    const brillo::VariantDictionary& config_params) {
316  dbus::ObjectPath service_path;
317  brillo::ErrorPtr error;
318  return shill_manager_proxy_.ConfigureService(
319      config_params, &service_path, &error);
320}
321
322bool ProxyDbusClient::ConfigureServiceByGuid(
323    const std::string& guid,
324    const brillo::VariantDictionary& config_params) {
325  dbus::ObjectPath service_path;
326  brillo::ErrorPtr error;
327  brillo::VariantDictionary guid_config_params(config_params);
328  guid_config_params[shill::kGuidProperty] = guid;
329  return shill_manager_proxy_.ConfigureService(
330      guid_config_params, &service_path, &error);
331}
332
333bool ProxyDbusClient::ConnectService(
334    const dbus::ObjectPath& object_path,
335    long timeout_milliseconds) {
336  auto proxy = GetProxyForObjectPath<ServiceProxy>(object_path);
337  brillo::ErrorPtr error;
338  if (!proxy->Connect(&error)) {
339    return false;
340  }
341  const std::vector<brillo::Any> expected_values = {
342    brillo::Any(std::string(shill::kStatePortal)),
343    brillo::Any(std::string(shill::kStateOnline)) };
344  return WaitForProxyPropertyValueIn<ServiceProxy>(
345      object_path, shill::kStateProperty, expected_values,
346      timeout_milliseconds, nullptr, nullptr);
347}
348
349bool ProxyDbusClient::DisconnectService(
350    const dbus::ObjectPath& object_path,
351    long timeout_milliseconds) {
352  auto proxy = GetProxyForObjectPath<ServiceProxy>(object_path);
353  brillo::ErrorPtr error;
354  if (!proxy->Disconnect(&error)) {
355    return false;
356  }
357  const std::vector<brillo::Any> expected_values = {
358    brillo::Any(std::string(shill::kStateIdle)) };
359  return WaitForProxyPropertyValueIn<ServiceProxy>(
360      object_path, shill::kStateProperty, expected_values,
361      timeout_milliseconds, nullptr, nullptr);
362}
363
364bool ProxyDbusClient::CreateProfile(const std::string& profile_name) {
365  dbus::ObjectPath profile_path;
366  brillo::ErrorPtr error;
367  return shill_manager_proxy_.CreateProfile(
368      profile_name, &profile_path, &error);
369}
370
371bool ProxyDbusClient::RemoveProfile(const std::string& profile_name) {
372  brillo::ErrorPtr error;
373  return shill_manager_proxy_.RemoveProfile(profile_name, &error);
374}
375
376bool ProxyDbusClient::PushProfile(const std::string& profile_name) {
377  dbus::ObjectPath profile_path;
378  brillo::ErrorPtr error;
379  return shill_manager_proxy_.PushProfile(
380      profile_name, &profile_path, &error);
381}
382
383bool ProxyDbusClient::PopProfile(const std::string& profile_name) {
384  brillo::ErrorPtr error;
385  return shill_manager_proxy_.PopProfile(profile_name, &error);
386}
387
388bool ProxyDbusClient::PopAnyProfile() {
389  brillo::ErrorPtr error;
390  return shill_manager_proxy_.PopAnyProfile(&error);
391}
392
393bool ProxyDbusClient::RequestServiceScan(const std::string& service_type) {
394  brillo::ErrorPtr error;
395  return shill_manager_proxy_.RequestScan(service_type, &error);
396}
397
398bool ProxyDbusClient::GetServiceOrder(std::string* order) {
399  brillo::ErrorPtr error;
400  return shill_manager_proxy_.GetServiceOrder(order, &error);
401}
402
403bool ProxyDbusClient::SetServiceOrder(const std::string& order) {
404  brillo::ErrorPtr error;
405  return shill_manager_proxy_.SetServiceOrder(order, &error);
406}
407
408bool ProxyDbusClient::SetSchedScan(bool enable) {
409  brillo::ErrorPtr error;
410  return shill_manager_proxy_.SetSchedScan(enable, &error);
411}
412
413bool ProxyDbusClient::GetPropertyValueFromManager(
414    const std::string& property_name,
415    brillo::Any* property_value) {
416  return GetPropertyValueFromProxy(
417      &shill_manager_proxy_, property_name, property_value);
418}
419
420dbus::ObjectPath ProxyDbusClient::GetObjectPathForActiveProfile() {
421  brillo::Any property_value;
422  if (!GetPropertyValueFromManager(
423        shill::kActiveProfileProperty, &property_value)) {
424    return dbus::ObjectPath();
425  }
426  return dbus::ObjectPath(property_value.Get<std::string>());
427}
428
429bool ProxyDbusClient::SetLoggingInternal(int level, const std::string& tags) {
430  bool is_success = true;
431  brillo::ErrorPtr error;
432  is_success &= shill_manager_proxy_.SetDebugLevel(level, &error);
433  is_success &= shill_manager_proxy_.SetDebugTags(tags, &error);
434  return is_success;
435}
436
437template<typename Proxy>
438std::unique_ptr<Proxy> ProxyDbusClient::GetProxyForObjectPath(
439    const dbus::ObjectPath& object_path) {
440  return std::unique_ptr<Proxy>(new Proxy(dbus_bus_, object_path));
441}
442
443// Templated functions to return the object path property_name based on
444template<typename Proxy>
445std::vector<std::unique_ptr<Proxy>> ProxyDbusClient::GetProxies(
446    const std::string& object_paths_property_name) {
447  brillo::Any object_paths;
448  if (!GetPropertyValueFromManager(object_paths_property_name, &object_paths)) {
449    return std::vector<std::unique_ptr<Proxy>>();
450  }
451  std::vector<std::unique_ptr<Proxy>> proxies;
452  for (const auto& object_path :
453       object_paths.Get<std::vector<dbus::ObjectPath>>()) {
454    proxies.emplace_back(GetProxyForObjectPath<Proxy>(object_path));
455  }
456  return proxies;
457}
458
459template<typename Proxy>
460std::unique_ptr<Proxy> ProxyDbusClient::GetMatchingProxy(
461    const std::string& object_paths_property_name,
462    const brillo::VariantDictionary& expected_properties) {
463  for (auto& proxy : GetProxies<Proxy>(object_paths_property_name)) {
464    brillo::VariantDictionary proxy_properties;
465    brillo::ErrorPtr error;
466    if (!proxy->GetProperties(&proxy_properties, &error)) {
467      // Ignore unknown object path errors since we might be using some proxies
468      // for objects which may have been destroyed since.
469      CHECK(error->GetCode() == kDbusErrorObjectUnknown);
470      continue;
471    }
472    bool all_expected_properties_matched = true;
473    for (const auto& expected_property : expected_properties) {
474      if (proxy_properties[expected_property.first] != expected_property.second) {
475        all_expected_properties_matched = false;
476        break;
477      }
478    }
479    if (all_expected_properties_matched) {
480      return std::move(proxy);
481    }
482  }
483  return nullptr;
484}
485
486template<typename Proxy>
487bool ProxyDbusClient::WaitForProxyPropertyValueIn(
488    const dbus::ObjectPath& object_path,
489    const std::string& property_name,
490    const std::vector<brillo::Any>& expected_values,
491    long timeout_milliseconds,
492    brillo::Any* final_value,
493    long* elapsed_time_milliseconds) {
494  // Creates a local proxy using |object_path| instead of accepting the proxy
495  // from the caller since we cannot deregister the signal property change
496  // callback associated.
497  auto proxy = GetProxyForObjectPath<Proxy>(object_path);
498  auto condition_termination_checker =
499      base::Bind(&IsProxyPropertyValueIn<Proxy>,
500                 proxy.get(),
501                 property_name,
502                 expected_values);
503  auto condition_change_callback =
504      base::Bind(&PropertyChangedSignalCallback,
505                 property_name,
506                 expected_values);
507  auto condition_change_callback_registrar =
508      base::Bind(&HelpRegisterPropertyChangedSignalHandler<Proxy>,
509                 base::Unretained(proxy.get()),
510                 base::Bind(&PropertyChangedOnConnectedCallback,
511                            property_name));
512  bool is_success;
513  WaitForCondition(
514      condition_termination_checker, condition_change_callback,
515      condition_change_callback_registrar,
516      timeout_milliseconds, &is_success, final_value, elapsed_time_milliseconds);
517  return is_success;
518}
519
520void ProxyDbusClient::IsMatchingServicePresent(
521    const brillo::VariantDictionary& service_properties,
522    base::Time wait_start_time,
523    bool* is_success,
524    std::unique_ptr<ServiceProxy>* service_proxy_out,
525    long* elapsed_time_milliseconds) {
526  auto service_proxy = GetMatchingServiceProxy(service_properties);
527  *is_success = false;
528  if (service_proxy) {
529    *is_success = true;
530  }
531  if (service_proxy_out) {
532    *service_proxy_out = std::move(service_proxy);
533  }
534  if (elapsed_time_milliseconds) {
535    *elapsed_time_milliseconds =
536        (base::Time::Now() - wait_start_time).InMilliseconds();
537  }
538}
539
540void ProxyDbusClient::FindServiceOrRestartScan(
541    const brillo::VariantDictionary& service_properties,
542    const std::string& service_type) {
543  if (GetMatchingServiceProxy(service_properties)) {
544    base::MessageLoop::current()->QuitNow();
545  } else {
546    RestartScanForService(service_type);
547  }
548}
549
550void ProxyDbusClient::InitiateScanForService(
551    base::TimeDelta rescan_interval,
552    const std::string& service_type,
553    const base::Closure& timer_callback) {
554  // Create a new timer instance for repeatedly calling the provided
555  // |timer_callback|. |WaitForCondition| will cancel |timer_callback|'s
556  // enclosing CancelableCallback when it exits and hence we need to
557  // use the same reference when we repeatedly schedule |timer_callback|.
558  wait_for_service_timer_.reset(
559      new base::Timer(FROM_HERE, rescan_interval, timer_callback, false));
560  RestartScanForService(service_type);
561}
562
563void ProxyDbusClient::RestartScanForService(
564    const std::string& service_type) {
565  RequestServiceScan(service_type);
566  wait_for_service_timer_->Reset();
567}
568