/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_bluetooth_bluedroid_bluetoothdaemonhelpers_h__ #define mozilla_dom_bluetooth_bluedroid_bluetoothdaemonhelpers_h__ #include "BluetoothCommon.h" #include "mozilla/ArrayUtils.h" #include "mozilla/dom/bluetooth/BluetoothTypes.h" #include "mozilla/ipc/BluetoothDaemonConnection.h" #include "nsThreadUtils.h" #if MOZ_IS_GCC && MOZ_GCC_VERSION_AT_LEAST(4, 7, 0) /* use designated array initializers if supported */ #define INIT_ARRAY_AT(in_, out_) \ [in_] = out_ #else /* otherwise init array element by position */ #define INIT_ARRAY_AT(in_, out_) \ out_ #endif #define CONVERT(in_, out_) \ INIT_ARRAY_AT(in_, out_) using namespace mozilla::ipc; BEGIN_BLUETOOTH_NAMESPACE // // Helper structures // enum BluetoothAclState { ACL_STATE_CONNECTED, ACL_STATE_DISCONNECTED }; enum BluetoothSspPairingVariant { SSP_VARIANT_PASSKEY_CONFIRMATION, SSP_VARIANT_PASSKEY_ENTRY, SSP_VARIANT_CONSENT, SSP_VARIANT_PASSKEY_NOTIFICATION }; struct BluetoothAddress { uint8_t mAddr[6]; }; struct BluetoothConfigurationParameter { uint8_t mType; uint16_t mLength; nsAutoArrayPtr mValue; }; struct BluetoothDaemonPDUHeader { BluetoothDaemonPDUHeader() : mService(0x00) , mOpcode(0x00) , mLength(0x00) { } BluetoothDaemonPDUHeader(uint8_t aService, uint8_t aOpcode, uint8_t aLength) : mService(aService) , mOpcode(aOpcode) , mLength(aLength) { } uint8_t mService; uint8_t mOpcode; uint16_t mLength; }; struct BluetoothPinCode { uint8_t mPinCode[16]; uint8_t mLength; }; struct BluetoothRemoteName { uint8_t mName[249]; }; struct BluetoothServiceName { uint8_t mName[256]; }; // // Conversion // // PDUs can only store primitive data types, such as intergers or // strings. Gecko often uses more complex data types, such as // enumators or stuctures. Conversion functions convert between // primitive data and internal Gecko's data types during a PDU's // packing and unpacking. // nsresult Convert(bool aIn, uint8_t& aOut); nsresult Convert(bool aIn, BluetoothScanMode& aOut); nsresult Convert(int aIn, uint8_t& aOut); nsresult Convert(int aIn, int16_t& aOut); nsresult Convert(uint8_t aIn, bool& aOut); nsresult Convert(uint8_t aIn, char& aOut); nsresult Convert(uint8_t aIn, int& aOut); nsresult Convert(uint8_t aIn, BluetoothA2dpAudioState& aOut); nsresult Convert(uint8_t aIn, BluetoothA2dpConnectionState& aOut); nsresult Convert(uint8_t aIn, BluetoothAclState& aOut); nsresult Convert(uint8_t aIn, BluetoothHandsfreeAudioState& aOut); nsresult Convert(uint8_t aIn, BluetoothHandsfreeCallHoldType& aOut); nsresult Convert(uint8_t aIn, BluetoothHandsfreeConnectionState& aOut); nsresult Convert(uint8_t aIn, BluetoothHandsfreeNRECState& aOut); nsresult Convert(uint8_t aIn, BluetoothHandsfreeVoiceRecognitionState& aOut); nsresult Convert(uint8_t aIn, BluetoothHandsfreeVolumeType& aOut); nsresult Convert(uint8_t aIn, BluetoothBondState& aOut); nsresult Convert(uint8_t aIn, BluetoothDeviceType& aOut); nsresult Convert(uint8_t aIn, BluetoothPropertyType& aOut); nsresult Convert(uint8_t aIn, BluetoothScanMode& aOut); nsresult Convert(uint8_t aIn, BluetoothSspPairingVariant& aOut); nsresult Convert(uint8_t aIn, BluetoothStatus& aOut); nsresult Convert(uint32_t aIn, int& aOut); nsresult Convert(size_t aIn, uint16_t& aOut); nsresult Convert(const nsAString& aIn, BluetoothAddress& aOut); nsresult Convert(const nsAString& aIn, BluetoothPinCode& aOut); nsresult Convert(const nsAString& aIn, BluetoothPropertyType& aOut); nsresult Convert(const nsAString& aIn, BluetoothServiceName& aOut); nsresult Convert(const nsAString& aIn, BluetoothSspPairingVariant& aOut); nsresult Convert(BluetoothAclState aIn, bool& aOut); nsresult Convert(const BluetoothAddress& aIn, nsAString& aOut); nsresult Convert(BluetoothHandsfreeAtResponse aIn, uint8_t& aOut); nsresult Convert(BluetoothHandsfreeCallAddressType aIn, uint8_t& aOut); nsresult Convert(BluetoothHandsfreeCallDirection aIn, uint8_t& aOut); nsresult Convert(BluetoothHandsfreeCallState aIn, uint8_t& aOut); nsresult Convert(BluetoothHandsfreeCallMode aIn, uint8_t& aOut); nsresult Convert(BluetoothHandsfreeCallMptyType aIn, uint8_t& aOut); nsresult Convert(BluetoothHandsfreeNetworkState aIn, uint8_t& aOut); nsresult Convert(BluetoothHandsfreeServiceType aIn, uint8_t& aOut); nsresult Convert(BluetoothHandsfreeVolumeType aIn, uint8_t& aOut); nsresult Convert(BluetoothPropertyType aIn, uint8_t& aOut); nsresult Convert(const BluetoothRemoteName& aIn, nsAString& aOut); nsresult Convert(BluetoothScanMode aIn, uint8_t& aOut); nsresult Convert(BluetoothSocketType aIn, uint8_t& aOut); nsresult Convert(BluetoothSspPairingVariant aIn, uint8_t& aOut); nsresult Convert(BluetoothSspPairingVariant aIn, nsAString& aOut); // // Packing // nsresult PackPDU(bool aIn, BluetoothDaemonPDU& aPDU); inline nsresult PackPDU(uint8_t aIn, BluetoothDaemonPDU& aPDU) { return aPDU.Write(aIn); } inline nsresult PackPDU(uint16_t aIn, BluetoothDaemonPDU& aPDU) { return aPDU.Write(aIn); } inline nsresult PackPDU(int32_t aIn, BluetoothDaemonPDU& aPDU) { return aPDU.Write(aIn); } inline nsresult PackPDU(uint32_t aIn, BluetoothDaemonPDU& aPDU) { return aPDU.Write(aIn); } nsresult PackPDU(const BluetoothAddress& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothConfigurationParameter& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothDaemonPDUHeader& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothHandsfreeAtResponse& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothHandsfreeCallAddressType& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothHandsfreeCallDirection& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothHandsfreeCallMode& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothHandsfreeCallMptyType& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothHandsfreeCallState& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothHandsfreeNetworkState& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothHandsfreeServiceType& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothHandsfreeVolumeType& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothNamedValue& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothPinCode& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(BluetoothPropertyType aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(const BluetoothServiceName& aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(BluetoothSocketType aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(BluetoothSspPairingVariant aIn, BluetoothDaemonPDU& aPDU); nsresult PackPDU(BluetoothScanMode aIn, BluetoothDaemonPDU& aPDU); /* |PackConversion| is a helper for packing converted values. Pass * an instance of this structure to |PackPDU| to convert a value from * the input type to the output type and and write it to the PDU. */ template struct PackConversion { PackConversion(const Tin& aIn) : mIn(aIn) { } const Tin& mIn; }; template inline nsresult PackPDU(const PackConversion& aIn, BluetoothDaemonPDU& aPDU) { Tout out; nsresult rv = Convert(aIn.mIn, out); if (NS_FAILED(rv)) { return rv; } return PackPDU(out, aPDU); } /* |PackArray| is a helper for packing arrays. Pass an instance * of this structure as the first argument to |PackPDU| to pack * an array. The array's maximum default length is 255 elements. */ template struct PackArray { PackArray(const T* aData, size_t aLength) : mData(aData) , mLength(aLength) { } const T* mData; size_t mLength; }; /* This implementation of |PackPDU| packs the length of an array * and the elements of the array one-by-one. */ template inline nsresult PackPDU(const PackArray& aIn, BluetoothDaemonPDU& aPDU) { for (size_t i = 0; i < aIn.mLength; ++i) { nsresult rv = PackPDU(aIn.mData[i], aPDU); if (NS_FAILED(rv)) { return rv; } } return NS_OK; } template<> inline nsresult PackPDU(const PackArray& aIn, BluetoothDaemonPDU& aPDU) { /* Write raw bytes in one pass */ return aPDU.Write(aIn.mData, aIn.mLength); } /* |PackCString0| is a helper for packing 0-terminated C string, * including the \0 character. Pass an instance of this structure * as the first argument to |PackPDU| to pack a string. */ struct PackCString0 { PackCString0(const nsCString& aString) : mString(aString) { } const nsCString& mString; }; /* This implementation of |PackPDU| packs a 0-terminated C string. */ inline nsresult PackPDU(const PackCString0& aIn, BluetoothDaemonPDU& aPDU) { return PackPDU( PackArray(reinterpret_cast(aIn.mString.get()), aIn.mString.Length() + 1), aPDU); } template inline nsresult PackPDU(const T1& aIn1, const T2& aIn2, BluetoothDaemonPDU& aPDU) { nsresult rv = PackPDU(aIn1, aPDU); if (NS_FAILED(rv)) { return rv; } return PackPDU(aIn2, aPDU); } template inline nsresult PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, BluetoothDaemonPDU& aPDU) { nsresult rv = PackPDU(aIn1, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn2, aPDU); if (NS_FAILED(rv)) { return rv; } return PackPDU(aIn3, aPDU); } template inline nsresult PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, const T4& aIn4, BluetoothDaemonPDU& aPDU) { nsresult rv = PackPDU(aIn1, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn2, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn3, aPDU); if (NS_FAILED(rv)) { return rv; } return PackPDU(aIn4, aPDU); } template inline nsresult PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, const T4& aIn4, const T5& aIn5, BluetoothDaemonPDU& aPDU) { nsresult rv = PackPDU(aIn1, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn2, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn3, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn4, aPDU); if (NS_FAILED(rv)) { return rv; } return PackPDU(aIn5, aPDU); } template inline nsresult PackPDU(const T1& aIn1, const T2& aIn2, const T3& aIn3, const T4& aIn4, const T5& aIn5, const T6& aIn6, const T7& aIn7, BluetoothDaemonPDU& aPDU) { nsresult rv = PackPDU(aIn1, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn2, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn3, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn4, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn5, aPDU); if (NS_FAILED(rv)) { return rv; } rv = PackPDU(aIn6, aPDU); if (NS_FAILED(rv)) { return rv; } return PackPDU(aIn7, aPDU); } // // Unpacking // inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, int8_t& aOut) { return aPDU.Read(aOut); } inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, uint8_t& aOut) { return aPDU.Read(aOut); } inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, uint16_t& aOut) { return aPDU.Read(aOut); } inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, int32_t& aOut) { return aPDU.Read(aOut); } inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, uint32_t& aOut) { return aPDU.Read(aOut); } nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, bool& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, char& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpAudioState& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpConnectionState& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAclState& aOut); inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAddress& aOut) { return aPDU.Read(aOut.mAddr, sizeof(aOut.mAddr)); } nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothBondState& aOut); inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothDaemonPDUHeader& aOut) { nsresult rv = UnpackPDU(aPDU, aOut.mService); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(aPDU, aOut.mOpcode); if (NS_FAILED(rv)) { return rv; } return UnpackPDU(aPDU, aOut.mLength); } nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothDeviceType& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeAudioState& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeCallHoldType& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeConnectionState& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeNRECState& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeVoiceRecognitionState& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeVolumeType& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothRemoteInfo& aOut); inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothRemoteName& aOut) { return aPDU.Read(aOut.mName, sizeof(aOut.mName)); } nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothProperty& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothPropertyType& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothScanMode& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothServiceRecord& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothSspPairingVariant& aOut); nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothStatus& aOut); inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothUuid& aOut) { return aPDU.Read(aOut.mUuid, sizeof(aOut.mUuid)); } nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, nsDependentCString& aOut); /* |UnpackConversion| is a helper for convering unpacked values. Pass * an instance of this structure to |UnpackPDU| to read a value from * the PDU in the input type and convert it to the output type. */ template struct UnpackConversion { UnpackConversion(Tout& aOut) : mOut(aOut) { } Tout& mOut; }; template inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackConversion& aOut) { Tin in; nsresult rv = UnpackPDU(aPDU, in); if (NS_FAILED(rv)) { return rv; } return Convert(in, aOut.mOut); } /* |UnpackArray| is a helper for unpacking arrays. Pass an instance * of this structure as the second argument to |UnpackPDU| to unpack * an array. */ template struct UnpackArray { UnpackArray(T* aData, size_t aLength) : mData(aData) , mLength(aLength) { } UnpackArray(nsAutoArrayPtr& aData, size_t aLength) : mData(nullptr) , mLength(aLength) { aData = new T[mLength]; mData = aData.get(); } UnpackArray(nsAutoArrayPtr& aData, size_t aSize, size_t aElemSize) : mData(nullptr) , mLength(aSize / aElemSize) { aData = new T[mLength]; mData = aData.get(); } T* mData; size_t mLength; }; template inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackArray& aOut) { for (size_t i = 0; i < aOut.mLength; ++i) { nsresult rv = UnpackPDU(aPDU, aOut.mData[i]); if (NS_FAILED(rv)) { return rv; } } return NS_OK; } template<> inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackArray& aOut) { /* Read raw bytes in one pass */ return aPDU.Read(aOut.mData, aOut.mLength); } template inline nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, nsTArray& aOut) { for (typename nsTArray::size_type i = 0; i < aOut.Length(); ++i) { nsresult rv = UnpackPDU(aPDU, aOut[i]); if (NS_FAILED(rv)) { return rv; } } return NS_OK; } /* |UnpackCString0| is a helper for unpacking 0-terminated C string, * including the \0 character. Pass an instance of this structure * as the first argument to |UnpackPDU| to unpack a string. */ struct UnpackCString0 { UnpackCString0(nsCString& aString) : mString(&aString) { } nsCString* mString; // non-null by construction }; /* This implementation of |UnpackPDU| unpacks a 0-terminated C string. */ nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackCString0& aOut); /* |UnpackString0| is a helper for unpacking 0-terminated C string, * including the \0 character. Pass an instance of this structure * as the first argument to |UnpackPDU| to unpack a C string and convert * it to wide-character encoding. */ struct UnpackString0 { UnpackString0(nsString& aString) : mString(&aString) { } nsString* mString; // non-null by construction }; /* This implementation of |UnpackPDU| unpacks a 0-terminated C string * and converts it to wide-character encoding. */ nsresult UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackString0& aOut); // // Init operators // // |PDUInitOP| provides functionality for init operators that unpack PDUs. class PDUInitOp { protected: PDUInitOp(BluetoothDaemonPDU& aPDU) : mPDU(&aPDU) { } BluetoothDaemonPDU& GetPDU() const { return *mPDU; // cannot be nullptr } void WarnAboutTrailingData() const { size_t size = mPDU->GetSize(); if (MOZ_LIKELY(!size)) { return; } uint8_t service, opcode; uint16_t payloadSize; mPDU->GetHeader(service, opcode, payloadSize); BT_LOGR("Unpacked PDU of type (%x,%x) still contains %zu Bytes of data.", service, opcode, size); } private: BluetoothDaemonPDU* mPDU; // Hold pointer to allow for constant instances }; // |UnpackPDUInitOp| is a general-purpose init operator for all variants // of |BluetoothResultRunnable| and |BluetoothNotificationRunnable|. The // call operators of |UnpackPDUInitOp| unpack a PDU into the supplied // arguments. class UnpackPDUInitOp MOZ_FINAL : private PDUInitOp { public: UnpackPDUInitOp(BluetoothDaemonPDU& aPDU) : PDUInitOp(aPDU) { } nsresult operator () () const { WarnAboutTrailingData(); return NS_OK; } template nsresult operator () (T1& aArg1) const { nsresult rv = UnpackPDU(GetPDU(), aArg1); if (NS_FAILED(rv)) { return rv; } WarnAboutTrailingData(); return NS_OK; } template nsresult operator () (T1& aArg1, T2& aArg2) const { BluetoothDaemonPDU& pdu = GetPDU(); nsresult rv = UnpackPDU(pdu, aArg1); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg2); if (NS_FAILED(rv)) { return rv; } WarnAboutTrailingData(); return NS_OK; } template nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3) const { BluetoothDaemonPDU& pdu = GetPDU(); nsresult rv = UnpackPDU(pdu, aArg1); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg2); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg3); if (NS_FAILED(rv)) { return rv; } WarnAboutTrailingData(); return NS_OK; } template nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3, T4& aArg4) const { BluetoothDaemonPDU& pdu = GetPDU(); nsresult rv = UnpackPDU(pdu, aArg1); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg2); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg3); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg4); if (NS_FAILED(rv)) { return rv; } WarnAboutTrailingData(); return NS_OK; } template nsresult operator () (T1& aArg1, T2& aArg2, T3& aArg3, T4& aArg4, T5& aArg5) const { BluetoothDaemonPDU& pdu = GetPDU(); nsresult rv = UnpackPDU(pdu, aArg1); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg2); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg3); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg4); if (NS_FAILED(rv)) { return rv; } rv = UnpackPDU(pdu, aArg5); if (NS_FAILED(rv)) { return rv; } WarnAboutTrailingData(); return NS_OK; } }; END_BLUETOOTH_NAMESPACE #endif