[Bluetooth] BLEAdapter isScanning and BLEDevice new functions.
[platform/core/api/webapi-plugins.git] / src / bluetooth / bluetooth_le_adapter.cc
index b2f0be3..27fd38f 100644 (file)
 #include "bluetooth/bluetooth_le_device.h"
 #include "bluetooth/bluetooth_privilege.h"
 #include "bluetooth/bluetooth_util.h"
+using common::optional;
 
 namespace extension {
 namespace bluetooth {
 
 namespace {
 
+const std::string kData = "data";
+const std::string kUuid = "uuid";
+const std::string kUuids = "uuids";
+const std::string kSolicitationUuids = "solicitationuuids";
+const std::string kServiceData = "serviceData";
+
 class ParsedDataHolder {
  public:
   ParsedDataHolder() : valid_(false) {
@@ -84,7 +91,7 @@ class HexData {
 
 class BluetoothLEServiceData : public ParsedDataHolder {
  public:
-  const std::string& uuid() const {
+  const UUID& uuid() const {
     return uuid_;
   }
 
@@ -92,42 +99,34 @@ class BluetoothLEServiceData : public ParsedDataHolder {
     return data_;
   }
 
-  static bool Construct(const picojson::value& obj, BluetoothLEServiceData* out) {
-    if (!obj.is<picojson::object>() || !ParseUUID(obj, out) || !ParseData(obj, out)) {
-      return false;
+  static optional<BluetoothLEServiceData> Construct(const picojson::value& service_data_obj) {
+    ScopeLogger();
+
+    const auto& data = service_data_obj.get(kData);
+    const auto& uuid_str = service_data_obj.get(kUuid);
+    if (!data.is<std::string>() || !uuid_str.is<std::string>()) {
+      LoggerE("Invalid data type in service data");
+      return {};
     }
 
-    out->set_valid();
+    auto uuid = UUID::create(uuid_str.get<std::string>());
+    if (!uuid) {
+      return {};
+    }
 
-    return true;
+    return BluetoothLEServiceData{std::move(*uuid), data.get<std::string>()};
   }
 
  private:
-  static bool ParseUUID(const picojson::value& obj, BluetoothLEServiceData* out) {
-    ScopeLogger();
-    const auto& uuid = obj.get("uuid");
-    if (uuid.is<std::string>()) {
-      out->uuid_ = uuid.get<std::string>();
-    } else {
-      return false;
-    }
-
-    return true;
-  }
+  BluetoothLEServiceData(UUID&& uuid, const std::string& data_str) : uuid_{std::move(uuid)} {
+    ScopeLogger("UUID: %s, data_str: %s", uuid_.uuid_128_bit.c_str(), data_str.c_str());
 
-  static bool ParseData(const picojson::value& obj, BluetoothLEServiceData* out) {
-    ScopeLogger();
-    const auto& data = obj.get("data");
-    if (data.is<std::string>()) {
-      out->data_.Parse(data.get<std::string>());
-    } else {
-      return false;
-    }
+    data_.Parse(data_str);
 
-    return true;
+    set_valid();
   }
 
-  std::string uuid_;
+  const UUID uuid_;
   HexData data_;
 };
 
@@ -196,11 +195,11 @@ class BluetoothLEAdvertiseData : public ParsedDataHolder {
     return include_name_;
   }
 
-  const std::vector<std::string>& service_uuids() const {
+  const std::vector<UUID>& service_uuids() const {
     return service_uuids_;
   }
 
-  const std::vector<std::string>& solicitation_uuids() const {
+  const std::vector<UUID>& solicitation_uuids() const {
     return solicitation_uuids_;
   }
 
@@ -212,7 +211,7 @@ class BluetoothLEAdvertiseData : public ParsedDataHolder {
     return include_tx_power_level_;
   }
 
-  const BluetoothLEServiceData& service_data() const {
+  const std::vector<BluetoothLEServiceData>& service_data() const {
     return service_data_;
   }
 
@@ -249,16 +248,23 @@ class BluetoothLEAdvertiseData : public ParsedDataHolder {
 
   static bool ParseServiceUUIDs(const picojson::value& obj, BluetoothLEAdvertiseData* out) {
     ScopeLogger();
-    const auto& service_uuids = obj.get("uuids");
+    const auto& service_uuids = obj.get(kUuids);
     if (service_uuids.is<picojson::array>()) {
-      for (const auto& i : service_uuids.get<picojson::array>()) {
-        if (i.is<std::string>()) {
-          out->service_uuids_.push_back(i.get<std::string>());
+      for (const auto& uuid : service_uuids.get<picojson::array>()) {
+        if (!uuid.is<std::string>()) {
+          LoggerE("uuid is not a string");
+          return false;
+        }
+
+        auto uuid_obj = UUID::create(uuid.get<std::string>());
+        if (uuid_obj) {
+          out->service_uuids_.push_back(*uuid_obj);
         } else {
           return false;
         }
       }
     } else if (!service_uuids.is<picojson::null>()) {
+      LoggerE("Invalid service_uuids type");
       return false;
     }
 
@@ -267,11 +273,17 @@ class BluetoothLEAdvertiseData : public ParsedDataHolder {
 
   static bool ParseSolicitationUUIDs(const picojson::value& obj, BluetoothLEAdvertiseData* out) {
     ScopeLogger();
-    const auto& solicitation_uuids = obj.get("solicitationuuids");
+    const auto& solicitation_uuids = obj.get(kSolicitationUuids);
     if (solicitation_uuids.is<picojson::array>()) {
-      for (const auto& i : solicitation_uuids.get<picojson::array>()) {
-        if (i.is<std::string>()) {
-          out->solicitation_uuids_.push_back(i.get<std::string>());
+      for (const auto& uuid : solicitation_uuids.get<picojson::array>()) {
+        if (!uuid.is<std::string>()) {
+          LoggerE("uuid is not a string");
+          return false;
+        }
+
+        auto uuid_obj = UUID::create(uuid.get<std::string>());
+        if (uuid_obj) {
+          out->solicitation_uuids_.push_back(*uuid_obj);
         } else {
           return false;
         }
@@ -309,14 +321,27 @@ class BluetoothLEAdvertiseData : public ParsedDataHolder {
 
   static bool ParseServiceData(const picojson::value& obj, BluetoothLEAdvertiseData* out) {
     ScopeLogger();
-    const auto& service_data = obj.get("serviceData");
-    BluetoothLEServiceData data;
-    if (BluetoothLEServiceData::Construct(service_data, &data)) {
-      out->service_data_ = std::move(data);
-    } else if (!service_data.is<picojson::null>()) {
+
+    const auto& service_data_obj = obj.get(kServiceData);
+    if (service_data_obj.is<picojson::null>()) {
+      return true;
+    } else if (!service_data_obj.is<picojson::object>()) {
       return false;
     }
 
+    /*
+     * Currently, only advertising of a single service data object is supported
+     * by the Web API. In the future, support for advertising arrays of those
+     * may be added.
+     *
+     * TODO: if supported, parse here the whole array of service data instances.
+     */
+    auto service_data = BluetoothLEServiceData::Construct(service_data_obj);
+    if (!service_data) {
+      return false;
+    }
+
+    out->service_data_.emplace_back(std::move(*service_data));
     return true;
   }
 
@@ -334,11 +359,11 @@ class BluetoothLEAdvertiseData : public ParsedDataHolder {
   }
 
   bool include_name_;
-  std::vector<std::string> service_uuids_;
-  std::vector<std::string> solicitation_uuids_;
+  std::vector<UUID> service_uuids_;
+  std::vector<UUID> solicitation_uuids_;
   int appearance_;
   bool include_tx_power_level_;
-  BluetoothLEServiceData service_data_;
+  std::vector<BluetoothLEServiceData> service_data_;
   BluetoothLEManufacturerData manufacturer_data_;
 };
 
@@ -349,9 +374,7 @@ bool ToBool(bt_adapter_le_state_e state) {
 }
 
 // constants
-
 const std::string kAction = "action";
-const std::string kData = "data";
 // scan-related
 const std::string kOnScanSuccess = "onsuccess";
 const std::string kOnScanError = "onerror";
@@ -442,6 +465,22 @@ void BluetoothLEAdapter::StopScan(const picojson::value& data, picojson::object&
   }
 }
 
+void BluetoothLEAdapter::IsScanning(picojson::object& out) {
+  ScopeLogger();
+
+  bool is_scanning;
+  int ret = bt_adapter_le_is_discovering(&is_scanning);
+
+  if (BT_ERROR_NONE != ret) {
+    LogAndReportError(
+        PlatformResult(ErrorCode::UNKNOWN_ERR, "Failed to check for scanning in progress"), &out,
+        ("Failed to check for scanning in progress: %d (%s)", ret, get_error_message(ret)));
+  } else {
+    scanning_ = is_scanning;
+    ReportSuccess(picojson::value(is_scanning), out);
+  }
+}
+
 void BluetoothLEAdapter::StartAdvertise(const picojson::value& data, picojson::object& out) {
   ScopeLogger();
   CHECK_BACKWARD_COMPABILITY_PRIVILEGE_ACCESS(Privilege::kBluetooth, Privilege::kBluetoothAdmin,
@@ -529,8 +568,21 @@ void BluetoothLEAdapter::StartAdvertise(const picojson::value& data, picojson::o
     }
   }
 
-  for (const auto& i : advertise_data.service_uuids()) {
-    ret = bt_adapter_le_add_advertising_service_uuid(advertiser, packet_type, i.c_str());
+  for (const auto& uuid : advertise_data.service_uuids()) {
+    /*
+     * This native function accepts 16 or 128 bit UUIDs.
+     * To handle also 32 bit UUIDs, we pass all UUIDs in their canonical form.
+     *
+     * Note:
+     * The documentation of Native Bluetooth API says, this function advertises 128-bit UUIDs,
+     * that have 32-bit equivalents in the full 128-bit form.
+     * However, as of the day of writing this comment, packets advertised
+     * by the device contain these UUIDs in their 32-bit formats,
+     * i.e. AD fields with 0x04 («Incomplete List of 32-bit Service Class UUIDs») type.
+     * For example, "12345678-0000-1000-8000-00805F9B34FB" is advertised as "12345678".
+     */
+    ret = bt_adapter_le_add_advertising_service_uuid(advertiser, packet_type,
+                                                     uuid.uuid_128_bit.c_str());
     if (BT_ERROR_NONE != ret) {
       LogAndReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
                         ("bt_adapter_le_add_advertising_service_uuid() failed with: %d (%s)", ret,
@@ -539,9 +591,21 @@ void BluetoothLEAdapter::StartAdvertise(const picojson::value& data, picojson::o
     }
   }
 
-  for (const auto& i : advertise_data.solicitation_uuids()) {
-    ret =
-        bt_adapter_le_add_advertising_service_solicitation_uuid(advertiser, packet_type, i.c_str());
+  for (const auto& uuid : advertise_data.solicitation_uuids()) {
+    /*
+     * This native function accepts 16 or 128 bit UUIDs.
+     * To handle also 32 bit UUIDs, we pass all UUIDs in their canonical form.
+     *
+     * Note:
+     * The documentation of Native Bluetooth API says, it advertises 128-bit UUIDs,
+     * that have 32-bit equivalents in the full 128-bit form.
+     * However, as of the day of writing this comment, packets advertised
+     * by the device contain these UUIDs in their 32-bit formats,
+     * i.e. AD fields with 0x1F («List of 32-bit Service Solicitation UUIDs») type.
+     * For example, "12345678-0000-1000-8000-00805F9B34FB" is advertised as "12345678".
+     */
+    ret = bt_adapter_le_add_advertising_service_solicitation_uuid(advertiser, packet_type,
+                                                                  uuid.uuid_128_bit.c_str());
     if (BT_ERROR_NONE != ret) {
       LogAndReportError(
           util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
@@ -571,18 +635,26 @@ void BluetoothLEAdapter::StartAdvertise(const picojson::value& data, picojson::o
     }
   }
 
-  const auto& service_data = advertise_data.service_data();
-  if (service_data.uuid().empty() && nullptr == service_data.data().pointer()) {
+  if (advertise_data.service_data().empty()) {
     LoggerD("service data is empty");
   } else {
-    ret = bt_adapter_le_add_advertising_service_data(
-        advertiser, packet_type, service_data.uuid().c_str(), service_data.data().pointer(),
-        service_data.data().length());
-    if (BT_ERROR_NONE != ret) {
-      LogAndReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
-                        ("bt_adapter_le_add_advertising_service_data() failed with: %d (%s)", ret,
-                         get_error_message(ret)));
-      return;
+    for (const auto& service_data_obj : advertise_data.service_data()) {
+      ret = bt_adapter_le_add_advertising_service_data(
+          advertiser, packet_type, service_data_obj.uuid().ShortestPossibleFormat().c_str(),
+          service_data_obj.data().pointer(), service_data_obj.data().length());
+      if (BT_ERROR_NONE != ret) {
+        std::string error_message = "Failed to create advertiser";
+        if (BT_ERROR_QUOTA_EXCEEDED == ret && !service_data_obj.uuid().To16Bit()) {
+          error_message =
+              "Failed to start advertising: only 16 bit values of BluetoothLEServiceData::uuid are "
+              "supported";
+        }
+
+        LogAndReportError(util::GetBluetoothError(ret, error_message), &out,
+                          ("bt_adapter_le_add_advertising_service_data() failed with: %d (%s)", ret,
+                           get_error_message(ret)));
+        return;
+      }
     }
   }
 
@@ -701,12 +773,12 @@ void BluetoothLEAdapter::OnScanResult(int result, bt_adapter_le_device_scan_resu
       // device found
       LoggerD("Device found");
       picojson::value data{picojson::object{}};
-      const auto& r = BluetoothLEDevice::ToJson(info, &data.get<picojson::object>());
-      if (r) {
+      const auto& ret = BluetoothLEDevice::ToJson(info, &data.get<picojson::object>());
+      if (ret) {
         data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanSuccess)));
         data_obj->insert(std::make_pair(kData, data));
       } else {
-        LogAndReportError(r, data_obj, ("Failed to parse Bluetooth LE device"));
+        LogAndReportError(ret, data_obj, ("Failed to parse Bluetooth LE device"));
         data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanError)));
       }
     }