nsresult
  operator () (uint8_t& aArg1, uint8_t& aArg2,
               nsAutoArrayPtr<uint8_t>& aArg3) const
  {
    BluetoothDaemonPDU& pdu = GetPDU();

    /* Read attribute */
    nsresult rv = UnpackPDU(pdu, aArg1);
    if (NS_FAILED(rv)) {
      return rv;
    }

    /* Read number of values */
    rv = UnpackPDU(pdu, aArg2);
    if (NS_FAILED(rv)) {
      return rv;
    }

    /* Read values */
    rv = UnpackPDU(pdu, UnpackArray<uint8_t>(aArg3, aArg2));
    if (NS_FAILED(rv)) {
      return rv;
    }
    WarnAboutTrailingData();
    return NS_OK;
  }
  nsresult
  operator () (nsString& aArg1, uint32_t aArg2, uint8_t aArg3) const
  {
    BluetoothDaemonPDU& pdu = GetPDU();

    /* Read address */
    nsresult rv = UnpackPDU(
      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg1));
    if (NS_FAILED(rv)) {
      return rv;
    }

    /* Read sample rate */
    rv = UnpackPDU(pdu, aArg2);
    if (NS_FAILED(rv)) {
      return rv;
    }

    /* Read channel count */
    rv = UnpackPDU(pdu, aArg3);
    if (NS_FAILED(rv)) {
      return rv;
    }
    WarnAboutTrailingData();
    return NS_OK;
  }
nsresult
UnpackPDU(BluetoothDaemonPDU& 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(BluetoothDaemonPDU& 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
  operator () (int& aArg1, int& aArg2) const
  {
    BluetoothDaemonPDU& pdu = GetPDU();

    nsresult rv = UnpackPDU(pdu, UnpackConversion<uint8_t, int>(aArg1));
    if (NS_FAILED(rv)) {
      return rv;
    }
    rv = UnpackPDU(pdu, UnpackConversion<uint8_t, int>(aArg2));
    if (NS_FAILED(rv)) {
      return rv;
    }
    WarnAboutTrailingData();
    return NS_OK;
  }
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU,
          BluetoothHandsfreeVoiceRecognitionState& aOut)
{
  return UnpackPDU(
    aPDU,
    UnpackConversion<uint8_t, BluetoothHandsfreeVoiceRecognitionState>(aOut));
}
  nsresult
  operator () (BluetoothA2dpConnectionState& aArg1, nsString& aArg2) const
  {
    BluetoothDaemonPDU& pdu = GetPDU();

    /* Read state */
    nsresult rv = UnpackPDU(pdu, aArg1);
    if (NS_FAILED(rv)) {
      return rv;
    }

    /* Read address */
    rv = UnpackPDU(
      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg2));
    if (NS_FAILED(rv)) {
      return rv;
    }
    WarnAboutTrailingData();
    return NS_OK;
  }
nsresult
UnpackPDU(BluetoothDaemonPDU& 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
  operator () (uint8_t& aArg1,
               nsAutoArrayPtr<BluetoothAvrcpMediaAttribute>& aArg2) const
  {
    BluetoothDaemonPDU& pdu = GetPDU();

    /* Read number of attributes */
    nsresult rv = UnpackPDU(pdu, aArg1);
    if (NS_FAILED(rv)) {
      return rv;
    }

    /* Read attributes */
    rv = UnpackPDU(
      pdu, UnpackArray<BluetoothAvrcpMediaAttribute>(aArg2, aArg1));
    if (NS_FAILED(rv)) {
      return rv;
    }
    WarnAboutTrailingData();
    return NS_OK;
  }
  nsresult
  operator () (nsString& aArg1, unsigned long& aArg2) const
  {
    BluetoothDaemonPDU& pdu = GetPDU();

    /* Read address */
    nsresult rv = UnpackPDU(
      pdu, UnpackConversion<BluetoothAddress, nsAString>(aArg1));
    if (NS_FAILED(rv)) {
      return rv;
    }

    /* Read feature */
    rv = UnpackPDU(
      pdu,
      UnpackConversion<BluetoothAvrcpRemoteFeature, unsigned long>(aArg2));
    if (NS_FAILED(rv)) {
      return rv;
    }
    WarnAboutTrailingData();
    return NS_OK;
  }
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackString0& aOut)
{
  nsDependentCString cstring;

  nsresult rv = UnpackPDU(aPDU, cstring);
  if (NS_FAILED(rv)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }

  *aOut.mString = NS_ConvertUTF8toUTF16(cstring);

  return NS_OK;
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, const UnpackCString0& aOut)
{
  nsDependentCString cstring;

  nsresult rv = UnpackPDU(aPDU, cstring);
  if (NS_FAILED(rv)) {
    return NS_ERROR_ILLEGAL_VALUE;
  }

  aOut.mString->AssignASCII(cstring.get(), cstring.Length());

  return NS_OK;
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothHandsfreeVolumeType& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothHandsfreeVolumeType>(aOut));
}
nsresult
UnpackPDU(DaemonSocketPDU& aPDU, char& aOut)
{
  return UnpackPDU(aPDU, UnpackConversion<uint8_t, char>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothStatus& aOut)
{
  return UnpackPDU(aPDU, UnpackConversion<uint8_t, BluetoothStatus>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, char& aOut)
{
  return UnpackPDU(aPDU, UnpackConversion<uint8_t, char>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothSspVariant& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothSspVariant>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothA2dpConnectionState& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothA2dpConnectionState>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothScanMode& aOut)
{
  return UnpackPDU(aPDU, UnpackConversion<int32_t, BluetoothScanMode>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothPropertyType& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothPropertyType>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& 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 (NS_WARN_IF(!data)) {
          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<BluetoothAddress>(
        aPDU, UnpackConversion<BluetoothAddress, nsAString>(aOut.mString));
      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;
        nsAutoArrayPtr<BluetoothAddress> addresses;
        UnpackArray<BluetoothAddress> addressArray(addresses, numAddresses);
        rv = UnpackPDU(aPDU, addressArray);
        if (NS_FAILED(rv)) {
          return rv;
        }
        /* convert addresses to strings */
        aOut.mStringArray.SetLength(numAddresses);
        ConvertArray<BluetoothAddress> convertArray(addressArray.mData,
                                                    addressArray.mLength);
        rv = Convert(convertArray, aOut.mStringArray);
      }
      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(BluetoothDaemonPDU& aPDU, BluetoothAvrcpPlayerAttribute& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothAvrcpPlayerAttribute>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothAvrcpRemoteFeature& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<uint8_t, BluetoothAvrcpRemoteFeature>(aOut));
}
nsresult
UnpackPDU(BluetoothDaemonPDU& aPDU, BluetoothTypeOfDevice& aOut)
{
  return UnpackPDU(
    aPDU, UnpackConversion<int32_t, BluetoothTypeOfDevice>(aOut));
}