/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=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/. */

#include "BluetoothDaemonHelpers.h"
#include <limits>

#define MAX_UUID_SIZE 16

BEGIN_BLUETOOTH_NAMESPACE

using mozilla::ipc::DaemonSocketPDUHelpers::Convert;
using mozilla::ipc::DaemonSocketPDUHelpers::PackPDU;

//
// Conversion
//

nsresult
Convert(bool aIn, BluetoothScanMode& aOut)
{
  static const BluetoothScanMode sScanMode[] = {
    [false] = SCAN_MODE_CONNECTABLE,
    [true] = SCAN_MODE_CONNECTABLE_DISCOVERABLE
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sScanMode), bool, BluetoothScanMode)) {
    aOut = SCAN_MODE_NONE; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sScanMode[aIn];
  return NS_OK;
}

nsresult
Convert(int32_t aIn, BluetoothTypeOfDevice& aOut)
{
  static const BluetoothTypeOfDevice sTypeOfDevice[] = {
    [0x00] = static_cast<BluetoothTypeOfDevice>(0), // invalid, required by gcc
    [0x01] = TYPE_OF_DEVICE_BREDR,
    [0x02] = TYPE_OF_DEVICE_BLE,
    [0x03] = TYPE_OF_DEVICE_DUAL
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        !aIn, int32_t, BluetoothTypeOfDevice) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        static_cast<size_t>(aIn) >= MOZ_ARRAY_LENGTH(sTypeOfDevice), int32_t,
        BluetoothTypeOfDevice)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sTypeOfDevice[aIn];
  return NS_OK;
}

nsresult
Convert(int32_t aIn, BluetoothScanMode& aOut)
{
  static const BluetoothScanMode sScanMode[] = {
    [0x00] = SCAN_MODE_NONE,
    [0x01] = SCAN_MODE_CONNECTABLE,
    [0x02] = SCAN_MODE_CONNECTABLE_DISCOVERABLE
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn < 0, int32_t, BluetoothScanMode) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        static_cast<size_t>(aIn) >= MOZ_ARRAY_LENGTH(sScanMode), int32_t,
        BluetoothScanMode)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sScanMode[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothA2dpAudioState& aOut)
{
  static const BluetoothA2dpAudioState sAudioState[] = {
    [0x00] = A2DP_AUDIO_STATE_REMOTE_SUSPEND,
    [0x01] = A2DP_AUDIO_STATE_STOPPED,
    [0x02] = A2DP_AUDIO_STATE_STARTED
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sAudioState), uint8_t,
        BluetoothA2dpAudioState)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sAudioState[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothA2dpConnectionState& aOut)
{
  static const BluetoothA2dpConnectionState sConnectionState[] = {
    [0x00] = A2DP_CONNECTION_STATE_DISCONNECTED,
    [0x01] = A2DP_CONNECTION_STATE_CONNECTING,
    [0x02] = A2DP_CONNECTION_STATE_CONNECTED,
    [0x03] = A2DP_CONNECTION_STATE_DISCONNECTING
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sConnectionState), uint8_t,
        BluetoothA2dpConnectionState)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sConnectionState[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothAclState& aOut)
{
  static const BluetoothAclState sAclState[] = {
    [0x00] = ACL_STATE_CONNECTED,
    [0x01] = ACL_STATE_DISCONNECTED
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sAclState), uint8_t, BluetoothAclState)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sAclState[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothAvrcpEvent& aOut)
{
  static const BluetoothAvrcpEvent sAvrcpEvent[] = {
    [0x00] = static_cast<BluetoothAvrcpEvent>(0),
    [0x01] = AVRCP_EVENT_PLAY_STATUS_CHANGED,
    [0x02] = AVRCP_EVENT_TRACK_CHANGE,
    [0x03] = AVRCP_EVENT_TRACK_REACHED_END,
    [0x04] = AVRCP_EVENT_TRACK_REACHED_START,
    [0x05] = AVRCP_EVENT_PLAY_POS_CHANGED,
    [0x06] = static_cast<BluetoothAvrcpEvent>(0),
    [0x07] = static_cast<BluetoothAvrcpEvent>(0),
    [0x08] = AVRCP_EVENT_APP_SETTINGS_CHANGED
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        !aIn, uint8_t, BluetoothAvrcpEvent) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn == 0x06, uint8_t, BluetoothAvrcpEvent) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn == 0x07, uint8_t, BluetoothAvrcpEvent) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sAvrcpEvent), uint8_t, BluetoothAvrcpEvent)) {
    aOut = static_cast<BluetoothAvrcpEvent>(0); // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sAvrcpEvent[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothAvrcpMediaAttribute& aOut)
{
  static const BluetoothAvrcpMediaAttribute sAvrcpMediaAttribute[] = {
    [0x00] = static_cast<BluetoothAvrcpMediaAttribute>(0),
    [0x01] = AVRCP_MEDIA_ATTRIBUTE_TITLE,
    [0x02] = AVRCP_MEDIA_ATTRIBUTE_ARTIST,
    [0x03] = AVRCP_MEDIA_ATTRIBUTE_ALBUM,
    [0x04] = AVRCP_MEDIA_ATTRIBUTE_TRACK_NUM,
    [0x05] = AVRCP_MEDIA_ATTRIBUTE_NUM_TRACKS,
    [0x06] = AVRCP_MEDIA_ATTRIBUTE_GENRE,
    [0x07] = AVRCP_MEDIA_ATTRIBUTE_PLAYING_TIME
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        !aIn, uint8_t, BluetoothAvrcpMediaAttrbiute) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sAvrcpMediaAttribute), uint8_t,
        BluetoothAvrcpMediaAttribute)) {
    // silences compiler warning
    aOut = static_cast<BluetoothAvrcpMediaAttribute>(0);
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sAvrcpMediaAttribute[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothAvrcpPlayerAttribute& aOut)
{
  static const BluetoothAvrcpPlayerAttribute sAvrcpPlayerAttribute[] = {
    [0x00] = static_cast<BluetoothAvrcpPlayerAttribute>(0),
    [0x01] = AVRCP_PLAYER_ATTRIBUTE_EQUALIZER,
    [0x02] = AVRCP_PLAYER_ATTRIBUTE_REPEAT,
    [0x03] = AVRCP_PLAYER_ATTRIBUTE_SHUFFLE,
    [0x04] = AVRCP_PLAYER_ATTRIBUTE_SCAN
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        !aIn, uint8_t, BluetoothAvrcpPlayerAttrbiute) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sAvrcpPlayerAttribute), uint8_t,
        BluetoothAvrcpPlayerAttribute)) {
    // silences compiler warning
    aOut = static_cast<BluetoothAvrcpPlayerAttribute>(0);
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sAvrcpPlayerAttribute[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothAvrcpRemoteFeatureBits& aOut)
{
  static const uint8_t sAvrcpRemoteFeatureBits[] = {
    [0] = AVRCP_REMOTE_FEATURE_METADATA,
    [1] = AVRCP_REMOTE_FEATURE_ABSOLUTE_VOLUME,
    [2] = AVRCP_REMOTE_FEATURE_BROWSE
  };
  uint8_t bits = 0;
  size_t i;
  for (i = 0; i < MOZ_ARRAY_LENGTH(sAvrcpRemoteFeatureBits); ++i, aIn >>= 1) {
    if (aIn & 0x01) {
      bits |= sAvrcpRemoteFeatureBits[i];
    }
  }
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn << i, 'uint8_t', BluetoothAvrcpRemoteFeatureBits)) {
    aOut = AVRCP_REMOTE_FEATURE_NONE; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = static_cast<BluetoothAvrcpRemoteFeatureBits>(bits);
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothBondState& aOut)
{
  static const BluetoothBondState sBondState[] = {
    [0x00] = BOND_STATE_NONE,
    [0x01] = BOND_STATE_BONDING,
    [0x02] = BOND_STATE_BONDED
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sBondState), uint8_t, BluetoothBondState)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sBondState[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothHandsfreeAudioState& aOut)
{
  static const BluetoothHandsfreeAudioState sAudioState[] = {
    [0x00] = HFP_AUDIO_STATE_DISCONNECTED,
    [0x01] = HFP_AUDIO_STATE_CONNECTING,
    [0x02] = HFP_AUDIO_STATE_CONNECTED,
    [0x03] = HFP_AUDIO_STATE_DISCONNECTING
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sAudioState), uint8_t,
        BluetoothHandsfreeAudioState)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sAudioState[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothHandsfreeCallHoldType& aOut)
{
  static const BluetoothHandsfreeCallHoldType sCallHoldType[] = {
    [0x00] = HFP_CALL_HOLD_RELEASEHELD,
    [0x01] = HFP_CALL_HOLD_RELEASEACTIVE_ACCEPTHELD,
    [0x02] = HFP_CALL_HOLD_HOLDACTIVE_ACCEPTHELD,
    [0x03] = HFP_CALL_HOLD_ADDHELDTOCONF
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sCallHoldType), uint8_t,
        BluetoothHandsfreeCallHoldType)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sCallHoldType[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothHandsfreeConnectionState& aOut)
{
  static const BluetoothHandsfreeConnectionState sConnectionState[] = {
    [0x00] = HFP_CONNECTION_STATE_DISCONNECTED,
    [0x01] = HFP_CONNECTION_STATE_CONNECTING,
    [0x02] = HFP_CONNECTION_STATE_CONNECTED,
    [0x03] = HFP_CONNECTION_STATE_SLC_CONNECTED,
    [0x04] = HFP_CONNECTION_STATE_DISCONNECTING
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sConnectionState), uint8_t,
        BluetoothHandsfreeConnectionState)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sConnectionState[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothHandsfreeNRECState& aOut)
{
  static const BluetoothHandsfreeNRECState sNRECState[] = {
    [0x00] = HFP_NREC_STOPPED,
    [0x01] = HFP_NREC_STARTED
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sNRECState), uint8_t,
        BluetoothHandsfreeNRECState)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sNRECState[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothHandsfreeVoiceRecognitionState& aOut)
{
  static const BluetoothHandsfreeVoiceRecognitionState sState[] = {
    [0x00] = HFP_VOICE_RECOGNITION_STOPPED,
    [0x01] = HFP_VOICE_RECOGNITION_STARTED
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sState), uint8_t,
        BluetoothHandsfreeVoiceRecognitionState)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sState[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothHandsfreeVolumeType& aOut)
{
  static const BluetoothHandsfreeVolumeType sVolumeType[] = {
    [0x00] = HFP_VOLUME_TYPE_SPEAKER,
    [0x01] = HFP_VOLUME_TYPE_MICROPHONE
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sVolumeType), uint8_t,
        BluetoothHandsfreeVolumeType)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sVolumeType[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothHandsfreeWbsConfig& aOut)
{
  static const BluetoothHandsfreeWbsConfig sWbsConfig[] = {
    [0x00] = HFP_WBS_NONE,
    [0x01] = HFP_WBS_NO,
    [0x02] = HFP_WBS_YES
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sWbsConfig), uint8_t,
        BluetoothHandsfreeWbsConfig)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sWbsConfig[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothTypeOfDevice& aOut)
{
  return Convert((int32_t)aIn, aOut);
}

nsresult
Convert(uint8_t aIn, BluetoothPropertyType& aOut)
{
  static const BluetoothPropertyType sPropertyType[] = {
    [0x00] = static_cast<BluetoothPropertyType>(0), // invalid, required by gcc
    [0x01] = PROPERTY_BDNAME,
    [0x02] = PROPERTY_BDADDR,
    [0x03] = PROPERTY_UUIDS,
    [0x04] = PROPERTY_CLASS_OF_DEVICE,
    [0x05] = PROPERTY_TYPE_OF_DEVICE,
    [0x06] = PROPERTY_SERVICE_RECORD,
    [0x07] = PROPERTY_ADAPTER_SCAN_MODE,
    [0x08] = PROPERTY_ADAPTER_BONDED_DEVICES,
    [0x09] = PROPERTY_ADAPTER_DISCOVERY_TIMEOUT,
    [0x0a] = PROPERTY_REMOTE_FRIENDLY_NAME,
    [0x0b] = PROPERTY_REMOTE_RSSI,
    [0x0c] = PROPERTY_REMOTE_VERSION_INFO
  };
  if (aIn == 0xff) {
    /* This case is handled separately to not populate
     * |sPropertyType| with empty entries. */
    aOut = PROPERTY_REMOTE_DEVICE_TIMESTAMP;
    return NS_OK;
  }
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        !aIn, uint8_t, BluetoothPropertyType) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sPropertyType), uint8_t,
        BluetoothPropertyType)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sPropertyType[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothSocketType aIn, uint8_t& aOut)
{
  static const uint8_t sSocketType[] = {
    [0] = 0, // silences compiler warning
    [BluetoothSocketType::RFCOMM] = 0x01,
    [BluetoothSocketType::SCO] = 0x02,
    [BluetoothSocketType::L2CAP] = 0x03
    // EL2CAP not supported
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn == BluetoothSocketType::EL2CAP, BluetoothSocketType, uint8_t) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sSocketType), BluetoothSocketType, uint8_t) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        !sSocketType[aIn], BluetoothSocketType, uint8_t)) {
    aOut = 0; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sSocketType[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothSspVariant& aOut)
{
  static const BluetoothSspVariant sSspVariant[] = {
    [0x00] = SSP_VARIANT_PASSKEY_CONFIRMATION,
    [0x01] = SSP_VARIANT_PASSKEY_ENTRY,
    [0x02] = SSP_VARIANT_CONSENT,
    [0x03] = SSP_VARIANT_PASSKEY_NOTIFICATION
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sSspVariant), uint8_t, BluetoothSspVariant)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sSspVariant[aIn];
  return NS_OK;
}

nsresult
Convert(uint8_t aIn, BluetoothStatus& aOut)
{
  static const BluetoothStatus sStatus[] = {
    [0x00] = STATUS_SUCCESS,
    [0x01] = STATUS_FAIL,
    [0x02] = STATUS_NOT_READY,
    [0x03] = STATUS_NOMEM,
    [0x04] = STATUS_BUSY,
    [0x05] = STATUS_DONE,
    [0x06] = STATUS_UNSUPPORTED,
    [0x07] = STATUS_PARM_INVALID,
    [0x08] = STATUS_UNHANDLED,
    [0x09] = STATUS_AUTH_FAILURE,
    [0x0a] = STATUS_RMT_DEV_DOWN,
    [0x0b] = STATUS_AUTH_REJECTED
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sStatus), uint8_t, BluetoothStatus)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sStatus[aIn];
  return NS_OK;
}

nsresult
Convert(int32_t aIn, BluetoothAttributeHandle& aOut)
{
  if (NS_WARN_IF(aIn < 0x0000) || NS_WARN_IF(aIn > 0xFFFF)) {
    aOut.mHandle = 0x0000; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut.mHandle = static_cast<uint16_t>(aIn);
  return NS_OK;
}

nsresult
Convert(int32_t aIn, BluetoothGattStatus& aOut)
{
  /* Reference: $B2G/external/bluetooth/bluedroid/stack/include/gatt_api.h */
  static const BluetoothGattStatus sGattStatus[] = {
    [0x0000] = GATT_STATUS_SUCCESS,
    [0x0001] = GATT_STATUS_INVALID_HANDLE,
    [0x0002] = GATT_STATUS_READ_NOT_PERMITTED,
    [0x0003] = GATT_STATUS_WRITE_NOT_PERMITTED,
    [0x0004] = GATT_STATUS_INVALID_PDU,
    [0x0005] = GATT_STATUS_INSUFFICIENT_AUTHENTICATION,
    [0x0006] = GATT_STATUS_REQUEST_NOT_SUPPORTED,
    [0x0007] = GATT_STATUS_INVALID_OFFSET,
    [0x0008] = GATT_STATUS_INSUFFICIENT_AUTHORIZATION,
    [0x0009] = GATT_STATUS_PREPARE_QUEUE_FULL,
    [0x000a] = GATT_STATUS_ATTRIBUTE_NOT_FOUND,
    [0x000b] = GATT_STATUS_ATTRIBUTE_NOT_LONG,
    [0x000c] = GATT_STATUS_INSUFFICIENT_ENCRYPTION_KEY_SIZE,
    [0x000d] = GATT_STATUS_INVALID_ATTRIBUTE_LENGTH,
    [0x000e] = GATT_STATUS_UNLIKELY_ERROR,
    [0x000f] = GATT_STATUS_INSUFFICIENT_ENCRYPTION,
    [0x0010] = GATT_STATUS_UNSUPPORTED_GROUP_TYPE,
    [0x0011] = GATT_STATUS_INSUFFICIENT_RESOURCES
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn < 0, int32_t, BluetoothGattStatus) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= static_cast<ssize_t>(MOZ_ARRAY_LENGTH(sGattStatus)), int32_t,
        BluetoothGattStatus)) {
    aOut = GATT_STATUS_UNKNOWN_ERROR;
  } else {
    aOut = sGattStatus[aIn];
  }
  return NS_OK;
}

nsresult
Convert(nsresult aIn, BluetoothStatus& aOut)
{
  if (NS_SUCCEEDED(aIn)) {
    aOut = STATUS_SUCCESS;
  } else if (aIn == NS_ERROR_OUT_OF_MEMORY) {
    aOut = STATUS_NOMEM;
  } else {
    aOut = STATUS_FAIL;
  }
  return NS_OK;
}

nsresult
Convert(const BluetoothAttributeHandle& aIn, int32_t& aOut)
{
  aOut = static_cast<int32_t>(aIn.mHandle);
  return NS_OK;
}

nsresult
Convert(const BluetoothAttributeHandle& aIn, uint16_t& aOut)
{
  aOut = aIn.mHandle;
  return NS_OK;
}


nsresult
Convert(BluetoothAvrcpEvent aIn, uint8_t& aOut)
{
  static const uint8_t sValue[] = {
    [AVRCP_EVENT_PLAY_STATUS_CHANGED] = 0x01,
    [AVRCP_EVENT_TRACK_CHANGE] = 0x02,
    [AVRCP_EVENT_TRACK_REACHED_END] = 0x03,
    [AVRCP_EVENT_TRACK_REACHED_START] = 0x04,
    [AVRCP_EVENT_PLAY_POS_CHANGED] = 0x05,
    [AVRCP_EVENT_APP_SETTINGS_CHANGED] = 0x08
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sValue), BluetoothAvrcpEvent, uint8_t)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sValue[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothAvrcpNotification aIn, uint8_t& aOut)
{
  static const bool sValue[] = {
    [AVRCP_NTF_INTERIM] = 0x00,
    [AVRCP_NTF_CHANGED] = 0x01
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sValue), BluetoothAvrcpNotification,
        uint8_t)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sValue[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothAvrcpPlayerAttribute aIn, uint8_t& aOut)
{
  static const uint8_t sValue[] = {
    [AVRCP_PLAYER_ATTRIBUTE_EQUALIZER] = 0x01,
    [AVRCP_PLAYER_ATTRIBUTE_REPEAT] = 0x02,
    [AVRCP_PLAYER_ATTRIBUTE_SHUFFLE] = 0x03,
    [AVRCP_PLAYER_ATTRIBUTE_SCAN] = 0x04
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sValue), BluetoothAvrcpPlayerAttribute, uint8_t)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sValue[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothAvrcpRemoteFeatureBits aIn, unsigned long& aOut)
{
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn < std::numeric_limits<unsigned long>::min(),
        BluetoothAvrcpRemoteFeature, unsigned long) ||
      MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn > std::numeric_limits<unsigned long>::max(),
        BluetoothAvrcpRemoteFeature, unsigned long)) {
    aOut = 0; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = static_cast<unsigned long>(aIn);
  return NS_OK;
}

nsresult
Convert(BluetoothAvrcpStatus aIn, uint8_t& aOut)
{
  static const uint8_t sValue[] = {
    [AVRCP_STATUS_BAD_COMMAND] = 0x00,
    [AVRCP_STATUS_BAD_PARAMETER] = 0x01,
    [AVRCP_STATUS_NOT_FOUND] = 0x02,
    [AVRCP_STATUS_INTERNAL_ERROR] = 0x03,
    [AVRCP_STATUS_SUCCESS] = 0x04
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sValue), BluetoothAvrcpStatus, uint8_t)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sValue[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeAtResponse aIn, uint8_t& aOut)
{
  static const uint8_t sAtResponse[] = {
    [HFP_AT_RESPONSE_ERROR] = 0x00,
    [HFP_AT_RESPONSE_OK] = 0x01
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sAtResponse), BluetoothHandsfreeAtResponse,
        uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sAtResponse[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeCallAddressType aIn, uint8_t& aOut)
{
  static const uint8_t sCallAddressType[] = {
    [HFP_CALL_ADDRESS_TYPE_UNKNOWN] = 0x81,
    [HFP_CALL_ADDRESS_TYPE_INTERNATIONAL] = 0x91
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sCallAddressType),
        BluetoothHandsfreeCallAddressType, uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sCallAddressType[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeCallDirection aIn, uint8_t& aOut)
{
  static const uint8_t sCallDirection[] = {
    [HFP_CALL_DIRECTION_OUTGOING] = 0x00,
    [HFP_CALL_DIRECTION_INCOMING] = 0x01
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sCallDirection),
        BluetoothHandsfreeCallDirection, uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sCallDirection[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeCallState aIn, uint8_t& aOut)
{
  static const uint8_t sCallState[] = {
    [HFP_CALL_STATE_ACTIVE] = 0x00,
    [HFP_CALL_STATE_HELD] = 0x01,
    [HFP_CALL_STATE_DIALING] = 0x02,
    [HFP_CALL_STATE_ALERTING] = 0x03,
    [HFP_CALL_STATE_INCOMING] = 0x04,
    [HFP_CALL_STATE_WAITING] = 0x05,
    [HFP_CALL_STATE_IDLE] = 0x06
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sCallState), BluetoothHandsfreeCallState,
        uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sCallState[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeCallMode aIn, uint8_t& aOut)
{
  static const uint8_t sCallMode[] = {
    [HFP_CALL_MODE_VOICE] = 0x00,
    [HFP_CALL_MODE_DATA] = 0x01,
    [HFP_CALL_MODE_FAX] = 0x02
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sCallMode), BluetoothHandsfreeCallMode, uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sCallMode[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeCallMptyType aIn, uint8_t& aOut)
{
  static const uint8_t sCallMptyType[] = {
    [HFP_CALL_MPTY_TYPE_SINGLE] = 0x00,
    [HFP_CALL_MPTY_TYPE_MULTI] = 0x01
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sCallMptyType),
        BluetoothHandsfreeCallMptyType, uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sCallMptyType[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeNetworkState aIn, uint8_t& aOut)
{
  static const uint8_t sNetworkState[] = {
    [HFP_NETWORK_STATE_NOT_AVAILABLE] = 0x00,
    [HFP_NETWORK_STATE_AVAILABLE] = 0x01
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sNetworkState), BluetoothHandsfreeNetworkState,
        uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sNetworkState[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeServiceType aIn, uint8_t& aOut)
{
  static const uint8_t sServiceType[] = {
    [HFP_SERVICE_TYPE_HOME] = 0x00,
    [HFP_SERVICE_TYPE_ROAMING] = 0x01
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sServiceType), BluetoothHandsfreeServiceType,
        uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sServiceType[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeVolumeType aIn, uint8_t& aOut)
{
  static const uint8_t sVolumeType[] = {
    [HFP_VOLUME_TYPE_SPEAKER] = 0x00,
    [HFP_VOLUME_TYPE_MICROPHONE] = 0x01
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sVolumeType), BluetoothHandsfreeVolumeType,
        uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sVolumeType[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothHandsfreeWbsConfig aIn, uint8_t& aOut)
{
  static const uint8_t sWbsConfig[] = {
    [HFP_WBS_NONE] = 0x00,
    [HFP_WBS_NO] = 0x01,
    [HFP_WBS_YES] = 0x02
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sWbsConfig), BluetoothHandsfreeWbsConfig,
        uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sWbsConfig[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothPropertyType aIn, uint8_t& aOut)
{
  static const uint8_t sPropertyType[] = {
    [PROPERTY_UNKNOWN] = 0x00,
    [PROPERTY_BDNAME] = 0x01,
    [PROPERTY_BDADDR] = 0x02,
    [PROPERTY_UUIDS] = 0x03,
    [PROPERTY_CLASS_OF_DEVICE] = 0x04,
    [PROPERTY_TYPE_OF_DEVICE] = 0x05,
    [PROPERTY_SERVICE_RECORD] = 0x06,
    [PROPERTY_ADAPTER_SCAN_MODE] = 0x07,
    [PROPERTY_ADAPTER_BONDED_DEVICES] = 0x08,
    [PROPERTY_ADAPTER_DISCOVERY_TIMEOUT] = 0x09,
    [PROPERTY_REMOTE_FRIENDLY_NAME] = 0x0a,
    [PROPERTY_REMOTE_RSSI] = 0x0b,
    [PROPERTY_REMOTE_VERSION_INFO] = 0x0c,
    [PROPERTY_REMOTE_DEVICE_TIMESTAMP] = 0xff
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sPropertyType), BluetoothPropertyType,
        uint8_t)) {
    aOut = 0x00; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sPropertyType[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothScanMode aIn, int32_t& aOut)
{
  static const int32_t sScanMode[] = {
    [SCAN_MODE_NONE] = 0x00,
    [SCAN_MODE_CONNECTABLE] = 0x01,
    [SCAN_MODE_CONNECTABLE_DISCOVERABLE] = 0x02
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sScanMode), BluetoothScanMode, int32_t)) {
    aOut = 0; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sScanMode[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothSetupServiceId aIn, uint8_t& aOut)
{
  static const uint8_t sServiceId[] = {
    [SETUP_SERVICE_ID_SETUP] = 0x00,
    [SETUP_SERVICE_ID_CORE] = 0x01,
    [SETUP_SERVICE_ID_SOCKET] = 0x02,
    [SETUP_SERVICE_ID_HID] = 0x03,
    [SETUP_SERVICE_ID_PAN] = 0x04,
    [SETUP_SERVICE_ID_HANDSFREE] = 0x05,
    [SETUP_SERVICE_ID_A2DP] = 0x06,
    [SETUP_SERVICE_ID_HEALTH] = 0x07,
    [SETUP_SERVICE_ID_AVRCP] = 0x08,
    [SETUP_SERVICE_ID_GATT] = 0x09,
    [SETUP_SERVICE_ID_HANDSFREE_CLIENT] = 0x0a,
    [SETUP_SERVICE_ID_MAP_CLIENT] = 0x0b,
    [SETUP_SERVICE_ID_AVRCP_CONTROLLER] = 0x0c,
    [SETUP_SERVICE_ID_A2DP_SINK] = 0x0d
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sServiceId),
        BluetoothServiceSetupId, uint8_t)) {
    aOut = 0; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sServiceId[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothSspVariant aIn, uint8_t& aOut)
{
  static const uint8_t sValue[] = {
    [SSP_VARIANT_PASSKEY_CONFIRMATION] = 0x00,
    [SSP_VARIANT_PASSKEY_ENTRY] = 0x01,
    [SSP_VARIANT_CONSENT] = 0x02,
    [SSP_VARIANT_PASSKEY_NOTIFICATION] = 0x03
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sValue), BluetoothSspVariant, uint8_t)) {
    aOut = 0; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sValue[aIn];
  return NS_OK;
}

nsresult
Convert(ControlPlayStatus aIn, uint8_t& aOut)
{
  static const uint8_t sValue[] = {
    [PLAYSTATUS_STOPPED] = 0x00,
    [PLAYSTATUS_PLAYING] = 0x01,
    [PLAYSTATUS_PAUSED] = 0x02,
    [PLAYSTATUS_FWD_SEEK] = 0x03,
    [PLAYSTATUS_REV_SEEK] = 0x04
  };
  if (aIn == PLAYSTATUS_ERROR) {
    /* This case is handled separately to not populate
     * |sValue| with empty entries. */
    aOut = 0xff;
    return NS_OK;
  }
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sValue), ControlPlayStatus, uint8_t)) {
    aOut = 0; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sValue[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothGattAuthReq aIn, int32_t& aOut)
{
  static const int32_t sGattAuthReq[] = {
    [GATT_AUTH_REQ_NONE] = 0x00,
    [GATT_AUTH_REQ_NO_MITM] = 0x01,
    [GATT_AUTH_REQ_MITM] = 0x02,
    [GATT_AUTH_REQ_SIGNED_NO_MITM] = 0x03,
    [GATT_AUTH_REQ_SIGNED_MITM] = 0x04
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sGattAuthReq), BluetoothGattAuthReq,
        int32_t)) {
    aOut = GATT_AUTH_REQ_NONE; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sGattAuthReq[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothGattAuthReq aIn, uint8_t& aOut)
{
  static const uint8_t sGattAuthReq[] = {
    [GATT_AUTH_REQ_NONE] = 0x00,
    [GATT_AUTH_REQ_NO_MITM] = 0x01,
    [GATT_AUTH_REQ_MITM] = 0x02,
    [GATT_AUTH_REQ_SIGNED_NO_MITM] = 0x03,
    [GATT_AUTH_REQ_SIGNED_MITM] = 0x04
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sGattAuthReq), BluetoothGattAuthReq,
        uint8_t)) {
    aOut = GATT_AUTH_REQ_NONE; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sGattAuthReq[aIn];
  return NS_OK;
}

nsresult
Convert(BluetoothGattWriteType aIn, int32_t& aOut)
{
  static const int32_t sGattWriteType[] = {
    [GATT_WRITE_TYPE_NO_RESPONSE] = 0x01,
    [GATT_WRITE_TYPE_NORMAL] = 0x02,
    [GATT_WRITE_TYPE_PREPARE] = 0x03,
    [GATT_WRITE_TYPE_SIGNED] = 0x04
  };
  if (MOZ_HAL_IPC_CONVERT_WARN_IF(
        aIn >= MOZ_ARRAY_LENGTH(sGattWriteType), BluetoothGattWriteType,
        int32_t)) {
    aOut = GATT_WRITE_TYPE_NORMAL; // silences compiler warning
    return NS_ERROR_ILLEGAL_VALUE;
  }
  aOut = sGattWriteType[aIn];
  return NS_OK;
}

/* |ConvertArray| is a helper for converting arrays. Pass an
 * instance of this structure as the first argument to |Convert|
 * to convert an array. The output type has to support the array
 * subscript operator.
 */
template <typename T>
struct ConvertArray
{
  ConvertArray(const T* aData, unsigned long aLength)
  : mData(aData)
  , mLength(aLength)
  { }

  const T* mData;
  unsigned long mLength;
};

/* This implementation of |Convert| converts the elements of an
 * array one-by-one. The result data structures must have enough
 * memory allocated.
 */
template<typename Tin, typename Tout>
inline nsresult
Convert(const ConvertArray<Tin>& aIn, Tout& aOut)
{
  for (unsigned long i = 0; i < aIn.mLength; ++i) {
    nsresult rv = Convert(aIn.mData[i], aOut[i]);
    if (NS_FAILED(rv)) {
      return rv;
    }
  }
  return NS_OK;
}

//
// Packing
//

nsresult
PackPDU(const BluetoothAddress& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackArray<uint8_t>(aIn.mAddr, sizeof(aIn.mAddr)), aPDU);
}

nsresult
PackPDU(const BluetoothAttributeHandle& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothAttributeHandle, int32_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothAvrcpAttributeTextPairs& aIn,
        DaemonSocketPDU& aPDU)
{
  size_t i;

  for (i = 0; i < aIn.mLength; ++i) {
    nsresult rv = PackPDU(aIn.mAttr[i], aPDU);
    if (NS_FAILED(rv)) {
      return rv;
    }

    uint8_t len;
    const uint8_t* str;

    if (aIn.mText[i]) {
      str = reinterpret_cast<const uint8_t*>(aIn.mText[i]);
      len = strlen(aIn.mText[i]) + 1;
    } else {
      /* write \0 character for NULL strings */
      str = reinterpret_cast<const uint8_t*>("\0");
      len = 1;
    }

    rv = PackPDU(len, aPDU);
    if (NS_FAILED(rv)) {
      return rv;
    }
    rv = PackPDU(PackArray<uint8_t>(str, len), aPDU);
    if (NS_FAILED(rv)) {
      return rv;
    }
  }
  return NS_OK;
}

nsresult
PackPDU(const BluetoothAvrcpAttributeValuePairs& aIn,
        DaemonSocketPDU& aPDU)
{
  size_t i;

  for (i = 0; i < aIn.mLength; ++i) {
    nsresult rv = PackPDU(aIn.mAttr[i], aPDU);
    if (NS_FAILED(rv)) {
      return rv;
    }
    rv = PackPDU(aIn.mValue[i], aPDU);
    if (NS_FAILED(rv)) {
      return rv;
    }
  }
  return NS_OK;
}

nsresult
PackPDU(const BluetoothAvrcpElementAttribute& aIn, DaemonSocketPDU& aPDU)
{
  nsresult rv = PackPDU(PackConversion<uint32_t, uint8_t>(aIn.mId), aPDU);
  if (NS_FAILED(rv)) {
    return rv;
  }

  const NS_ConvertUTF16toUTF8 cstr(aIn.mValue);

  if (MOZ_HAL_IPC_PACK_WARN_IF(
        cstr.Length() == PR_UINT32_MAX, BluetoothAvrcpElementAttribute)) {
    return NS_ERROR_ILLEGAL_VALUE; /* integer overflow detected */
  }

  uint32_t clen = cstr.Length() + 1; /* include \0 character */

  rv = PackPDU(PackConversion<uint32_t, uint8_t>(clen), aPDU);
  if (NS_FAILED(rv)) {
    return rv;
  }

  return PackPDU(
    PackArray<uint8_t>(reinterpret_cast<const uint8_t*>(cstr.get()), clen),
    aPDU);
}

nsresult
PackPDU(BluetoothAvrcpEvent aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothAvrcpEvent, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothAvrcpEventParamPair& aIn, DaemonSocketPDU& aPDU)
{
  nsresult rv;

  switch (aIn.mEvent) {
    case AVRCP_EVENT_PLAY_STATUS_CHANGED:
      rv = PackPDU(aIn.mParam.mPlayStatus, aPDU);
      break;
    case AVRCP_EVENT_TRACK_CHANGE:
      rv = PackPDU(PackArray<uint8_t>(aIn.mParam.mTrack,
                                      MOZ_ARRAY_LENGTH(aIn.mParam.mTrack)),
                   aPDU);
      break;
    case AVRCP_EVENT_TRACK_REACHED_END:
      /* fall through */
    case AVRCP_EVENT_TRACK_REACHED_START:
      /* no data to pack */
      rv = NS_OK;
      break;
    case AVRCP_EVENT_PLAY_POS_CHANGED:
      rv = PackPDU(aIn.mParam.mSongPos, aPDU);
      break;
    case AVRCP_EVENT_APP_SETTINGS_CHANGED:
      /* pack number of attribute-value pairs */
      rv = PackPDU(aIn.mParam.mNumAttr, aPDU);
      if (NS_FAILED(rv)) {
        return rv;
      }
      /* pack attribute-value pairs */
      rv = PackPDU(BluetoothAvrcpAttributeValuePairs(aIn.mParam.mIds,
                                                     aIn.mParam.mValues,
                                                     aIn.mParam.mNumAttr),
                   aPDU);
      break;
    default:
      rv = NS_ERROR_ILLEGAL_VALUE;
      break;
  }
  return rv;
}

nsresult
PackPDU(BluetoothAvrcpNotification aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothAvrcpNotification, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(BluetoothAvrcpPlayerAttribute aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothAvrcpPlayerAttribute, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(BluetoothAvrcpStatus aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothAvrcpStatus, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothConfigurationParameter& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(aIn.mType, aIn.mLength,
                 PackArray<uint8_t>(aIn.mValue.get(), aIn.mLength), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeAtResponse& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeAtResponse, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeCallAddressType& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeCallAddressType, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeCallDirection& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeCallDirection, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeCallMode& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeCallMode, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeCallMptyType& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeCallMptyType, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeCallState& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeCallState, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeNetworkState& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeNetworkState, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeServiceType& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeServiceType, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeVolumeType& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeVolumeType, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothHandsfreeWbsConfig& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackConversion<BluetoothHandsfreeWbsConfig, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothProperty& aIn, DaemonSocketPDU& aPDU)
{
  nsresult rv = PackPDU(aIn.mType, aPDU);
  if (NS_FAILED(rv)) {
    return rv;
  }

  switch (aIn.mType) {
    case PROPERTY_BDNAME:
      /* fall through */
    case PROPERTY_REMOTE_FRIENDLY_NAME: {
        NS_ConvertUTF16toUTF8 stringUTF8(aIn.mString);

        rv = PackPDU(PackConversion<size_t, uint16_t>(stringUTF8.Length()),
                     PackArray<uint8_t>(
                       reinterpret_cast<const uint8_t*>(stringUTF8.get()),
                       stringUTF8.Length()),
                     aPDU);
      }
      break;
    case PROPERTY_CLASS_OF_DEVICE:
      /* fall through */
    case PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
      rv = PackPDU(PackConversion<size_t, uint16_t>(sizeof(aIn.mUint32)),
                   aIn.mUint32,
                   aPDU);
      break;
    case PROPERTY_ADAPTER_SCAN_MODE:
      /* |mScanMode| is sent as signed int of 4 bytes */
      rv = PackPDU(PackConversion<size_t, uint16_t>(sizeof(int32_t)),
                   aIn.mScanMode,
                   aPDU);
      break;
    default:
      NS_NOTREACHED("Invalid property for packing");
      return NS_ERROR_ILLEGAL_VALUE;
  }
  return rv;
}

nsresult
PackPDU(const BluetoothPinCode& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(aIn.mLength,
                 PackArray<uint8_t>(aIn.mPinCode, sizeof(aIn.mPinCode)),
                 aPDU);
}

nsresult
PackPDU(BluetoothPropertyType aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothPropertyType, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(BluetoothSspVariant aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothSspVariant, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(BluetoothScanMode aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothScanMode, int32_t>(aIn), aPDU);
}

nsresult
PackPDU(BluetoothSetupServiceId aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothSetupServiceId, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothServiceName& aIn, DaemonSocketPDU& aPDU)
{
  static const uint8_t sTerminator = '\0';

  nsresult rv =
    PackPDU(PackArray<uint8_t>(aIn.mName, sizeof(aIn.mName)), aPDU);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* The PDU requries one additional byte for \0 termination */
  return aPDU.Write(sTerminator);
}

nsresult
PackPDU(BluetoothSocketType aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothSocketType, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(ControlPlayStatus aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<ControlPlayStatus, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(BluetoothTransport aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothTransport, uint8_t>(aIn), aPDU);
}

nsresult
PackPDU(const BluetoothUuid& aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(
    PackArray<uint8_t>(aIn.mUuid, sizeof(aIn.mUuid)), aPDU);
}

nsresult
PackPDU(const BluetoothGattId& aIn, DaemonSocketPDU& aPDU)
{
  nsresult rv = PackPDU(PackReversed<BluetoothUuid>(aIn.mUuid), aPDU);
  if (NS_FAILED(rv)) {
    return rv;
  }
  return PackPDU(aIn.mInstanceId, aPDU);
}

nsresult
PackPDU(const BluetoothGattServiceId& aIn, DaemonSocketPDU& aPDU)
{
  nsresult rv = PackPDU(aIn.mId, aPDU);
  if (NS_FAILED(rv)) {
    return rv;
  }
  return PackPDU(aIn.mIsPrimary, aPDU);
}

nsresult
PackPDU(BluetoothGattAuthReq aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothGattAuthReq, int32_t>(aIn), aPDU);
}

nsresult
PackPDU(BluetoothGattWriteType aIn, DaemonSocketPDU& aPDU)
{
  return PackPDU(PackConversion<BluetoothGattWriteType, int32_t>(aIn), aPDU);
}

//
// Unpacking
//

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothA2dpAudioState& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothA2dpAudioState>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothA2dpConnectionState& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothA2dpConnectionState>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothAclState& aOut)
{
  return UnpackPDU(aPDU, UnpackConversion<uint8_t, BluetoothAclState>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothAttributeHandle& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<int32_t, BluetoothAttributeHandle>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothAvrcpEvent& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothAvrcpEvent>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothAvrcpMediaAttribute& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothAvrcpMediaAttribute>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothAvrcpPlayerAttribute& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothAvrcpPlayerAttribute>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothAvrcpPlayerSettings& aOut)
{
  /* Read number of attribute-value pairs */
  nsresult rv = UnpackPDU(aPDU, aOut.mNumAttr);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* Read attribute-value pairs */
  for (uint8_t i = 0; i < aOut.mNumAttr; ++i) {
    nsresult rv = UnpackPDU(aPDU, aOut.mIds[i]);
    if (NS_FAILED(rv)) {
      return rv;
    }
    rv = UnpackPDU(aPDU, aOut.mValues[i]);
    if (NS_FAILED(rv)) {
      return rv;
    }
  }
  return NS_OK;
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothAvrcpRemoteFeatureBits& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothAvrcpRemoteFeatureBits>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothBondState& aOut)
{
  return UnpackPDU(aPDU, UnpackConversion<uint8_t, BluetoothBondState>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothTypeOfDevice& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<int32_t, BluetoothTypeOfDevice>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHandsfreeAudioState& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeAudioState>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHandsfreeCallHoldType& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeCallHoldType>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHandsfreeConnectionState& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeConnectionState>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHandsfreeNRECState& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeNRECState>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHandsfreeWbsConfig& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeWbsConfig>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU,
          BluetoothHandsfreeVoiceRecognitionState& aOut)
{
  return UnpackPDU(
    aPDU,
    UnpackConversion<uint8_t, BluetoothHandsfreeVoiceRecognitionState>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothHandsfreeVolumeType& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeVolumeType>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothProperty& aOut)
{
  nsresult rv = UnpackPDU(aPDU, aOut.mType);
  if (NS_FAILED(rv)) {
    return rv;
  }
  uint16_t len;
  rv = UnpackPDU(aPDU, len);
  if (NS_FAILED(rv)) {
    return rv;
  }

  switch (aOut.mType) {
    case PROPERTY_BDNAME:
      /* fall through */
    case PROPERTY_REMOTE_FRIENDLY_NAME: {
        const uint8_t* data = aPDU.Consume(len);
        if (MOZ_HAL_IPC_UNPACK_WARN_IF(!data, BluetoothProperty)) {
          return NS_ERROR_ILLEGAL_VALUE;
        }
        // We construct an nsCString here because the string
        // returned from the PDU is not 0-terminated.
        aOut.mString = NS_ConvertUTF8toUTF16(
          nsCString(reinterpret_cast<const char*>(data), len));
      }
      break;
    case PROPERTY_BDADDR:
      rv = UnpackPDU(aPDU, aOut.mBdAddress);
      break;
    case PROPERTY_UUIDS: {
        size_t numUuids = len / MAX_UUID_SIZE;
        aOut.mUuidArray.SetLength(numUuids);
        rv = UnpackPDU(aPDU, aOut.mUuidArray);
      }
      break;
    case PROPERTY_CLASS_OF_DEVICE:
      /* fall through */
    case PROPERTY_ADAPTER_DISCOVERY_TIMEOUT:
      rv = UnpackPDU(aPDU, aOut.mUint32);
      break;
    case PROPERTY_TYPE_OF_DEVICE:
      rv = UnpackPDU(aPDU, aOut.mTypeOfDevice);
      break;
    case PROPERTY_SERVICE_RECORD:
      rv = UnpackPDU(aPDU, aOut.mServiceRecord);
      break;
    case PROPERTY_ADAPTER_SCAN_MODE:
      rv = UnpackPDU(aPDU, aOut.mScanMode);
      break;
    case PROPERTY_ADAPTER_BONDED_DEVICES: {
        /* unpack addresses */
        size_t numAddresses = len / BLUETOOTH_ADDRESS_BYTES;
        aOut.mBdAddressArray.SetLength(numAddresses);
        rv = UnpackPDU(aPDU, aOut.mBdAddressArray);
      }
      break;
    case PROPERTY_REMOTE_RSSI: {
        int8_t rssi;
        rv = UnpackPDU(aPDU, rssi);
        aOut.mInt32 = rssi;
      }
      break;
    case PROPERTY_REMOTE_VERSION_INFO:
      rv = UnpackPDU(aPDU, aOut.mRemoteInfo);
      break;
    case PROPERTY_REMOTE_DEVICE_TIMESTAMP:
      /* nothing to do */
      break;
    default:
      break;
  }
  return rv;
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothPropertyType& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothPropertyType>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothRemoteInfo& aOut)
{
  nsresult rv = UnpackPDU(aPDU,
                          UnpackConversion<uint32_t, int>(aOut.mVerMajor));
  if (NS_FAILED(rv)) {
    return rv;
  }
  rv = UnpackPDU(aPDU, UnpackConversion<uint32_t, int>(aOut.mVerMinor));
  if (NS_FAILED(rv)) {
    return rv;
  }
  return UnpackPDU(aPDU, UnpackConversion<uint32_t, int>(aOut.mManufacturer));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothScanMode& aOut)
{
  return UnpackPDU(aPDU, UnpackConversion<int32_t, BluetoothScanMode>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothServiceRecord& aOut)
{
  /* unpack UUID */
  nsresult rv = UnpackPDU(aPDU, aOut.mUuid);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack channel */
  rv = UnpackPDU(aPDU, aOut.mChannel);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack name */
  return aPDU.Read(aOut.mName, sizeof(aOut.mName));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothSspVariant& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothSspVariant>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothStatus& aOut)
{
  return UnpackPDU(aPDU, UnpackConversion<uint8_t, BluetoothStatus>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattStatus& aOut)
{
  return UnpackPDU(aPDU, UnpackConversion<int32_t, BluetoothGattStatus>(aOut));
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattId& aOut)
{
  /* unpack UUID */
  nsresult rv = UnpackPDU(aPDU, UnpackReversed<BluetoothUuid>(aOut.mUuid));
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack instance id */
  return UnpackPDU(aPDU, aOut.mInstanceId);
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattServiceId& aOut)
{
  /* unpack id */
  nsresult rv = UnpackPDU(aPDU, aOut.mId);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack isPrimary */
  return UnpackPDU(aPDU, aOut.mIsPrimary);
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattReadParam& aOut)
{
  /* unpack service id */
  nsresult rv = UnpackPDU(aPDU, aOut.mServiceId);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack characteristic id */
  rv = UnpackPDU(aPDU, aOut.mCharId);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack descriptor id */
  rv = UnpackPDU(aPDU, aOut.mDescriptorId);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack status */
  rv = UnpackPDU(aPDU, aOut.mStatus);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack value type */
  rv = UnpackPDU(aPDU, aOut.mValueType);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack length */
  rv = UnpackPDU(aPDU, aOut.mValueLength);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack value */
  return aPDU.Read(aOut.mValue, aOut.mValueLength);
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattWriteParam& aOut)
{
  /* unpack service id */
  nsresult rv = UnpackPDU(aPDU, aOut.mServiceId);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack characteristic id */
  rv = UnpackPDU(aPDU, aOut.mCharId);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack descriptor id */
  rv = UnpackPDU(aPDU, aOut.mDescriptorId);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack status */
  return UnpackPDU(aPDU, aOut.mStatus);
}

nsresult
UnpackPDU(DaemonSocketPDU& aPDU, BluetoothGattNotifyParam& aOut)
{
  /* unpack address */
  nsresult rv = UnpackPDU(aPDU, aOut.mBdAddr);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack service id */
  rv = UnpackPDU(aPDU, aOut.mServiceId);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack characteristic id */
  rv = UnpackPDU(aPDU, aOut.mCharId);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack isNotify */
  rv = UnpackPDU(aPDU, aOut.mIsNotify);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack length */
  rv = UnpackPDU(aPDU, aOut.mLength);
  if (NS_FAILED(rv)) {
    return rv;
  }
  /* unpack value */
  return aPDU.Read(aOut.mValue, aOut.mLength);
}

END_BLUETOOTH_NAMESPACE
