CLOSED TREE Bug 1180556 - Pack PBAP replies to OBEX response message and reply to remote device. r=btian

Conflicts:

	dom/bluetooth/bluedroid/BluetoothPbapManager.cpp
This commit is contained in:
Jamin Liu 2015-08-24 10:30:07 +08:00
Родитель abf6b30110
Коммит 400ba4913c
4 изменённых файлов: 311 добавлений и 42 удалений

Просмотреть файл

@ -20,14 +20,14 @@ AppendHeader(uint8_t aHeaderId, uint8_t* aRetBuf, int aBufferSize,
const uint8_t* aData, int aLength)
{
int headerLength = aLength + 3;
int writtenLength = (headerLength < aBufferSize) ? headerLength : aBufferSize;
aRetBuf[0] = aHeaderId;
aRetBuf[1] = (headerLength & 0xFF00) >> 8;
aRetBuf[2] = headerLength & 0x00FF;
memcpy(&aRetBuf[3], aData, (aLength < aBufferSize - 3) ? aLength
: aBufferSize - 3);
memcpy(&aRetBuf[3], aData, writtenLength - 3);
return headerLength;
return writtenLength;
}
/**
@ -73,6 +73,34 @@ AppendHeaderWho(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aWho,
aWho, aLength);
}
int
AppendHeaderAppParameters(uint8_t* aRetBuf, int aBufferSize,
const uint8_t* aAppParameters, int aLength)
{
return AppendHeader(ObexHeaderId::AppParameters, aRetBuf, aBufferSize,
aAppParameters, aLength);
}
int
AppendAppParameter(uint8_t* aRetBuf, int aBufferSize, const uint8_t aTagId,
const uint8_t* aValue, int aLength)
{
// An application parameter is a [tag]-[length]-[value] triplet. The [tag] and
// [length] fields are 1-byte length each.
if (aBufferSize < aLength + 2) {
// aBufferSize should be larger than size of AppParameter + header.
BT_WARNING("Return buffer size is too small for the AppParameter");
return 0;
}
aRetBuf[0] = aTagId;
aRetBuf[1] = aLength;
memcpy(&aRetBuf[2], aValue, aLength);
return aLength + 2;
}
int
AppendHeaderLength(uint8_t* aRetBuf, int aObjectLength)
{

Просмотреть файл

@ -15,6 +15,27 @@ BEGIN_BLUETOOTH_NAMESPACE
const char FINAL_BIT = 0x80;
/**
* Section 3.2 "Response format", IrOBEX ver 1.2
* The format of an OBEX response header is
* [response code:1][response length:2]
*/
static const uint32_t kObexRespHeaderSize = 3;
/**
* Section 2.2.9 "Body, End-of-Body", IrOBEX ver 1.2
* The format of an OBEX Body header is
* [headerId:1][header length:2]
*/
static const uint32_t kObexBodyHeaderSize = 3;
/**
* Section 3.3.1.4 "Minimum OBEX Packet Length", IrOBEX ver 1.2
* The minimum size of the OBEX Maximum packet length allowed for negotiation is
* 255 bytes.
*/
static const uint32_t kObexLeastMaxSize = 255;
/*
* Defined in section 2.1 "OBEX Headers", IrOBEX ver 1.2
*/
@ -251,7 +272,8 @@ public:
}
/**
* Get a specified parameter from the 'Application Parameters' header.
* Get a specified parameter from the 'Application Parameters' header with
* big-endian byte ordering.
*
* @param aTagId [in] The tag ID of parameter which is defined by
* applications or upper protocol layer.
@ -323,6 +345,10 @@ int AppendHeaderBody(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aBody,
int aLength);
int AppendHeaderWho(uint8_t* aRetBuf, int aBufferSize, const uint8_t* aWho,
int aLength);
int AppendHeaderAppParameters(uint8_t* aRetBuf, int aBufferSize,
const uint8_t* aAppParameters, int aLength);
int AppendAppParameter(uint8_t* aRetBuf, int aBufferSize, const uint8_t aTagId,
const uint8_t* aValue, int aLength);
int AppendHeaderLength(uint8_t* aRetBuf, int aObjectLength);
int AppendHeaderConnectionId(uint8_t* aRetBuf, int aConnectionId);
int AppendHeaderEndOfBody(uint8_t* aRetBuf);

Просмотреть файл

@ -17,6 +17,7 @@
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "nsAutoPtr.h"
#include "nsIInputStream.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
@ -75,6 +76,8 @@ BluetoothPbapManager::HandleShutdown()
}
BluetoothPbapManager::BluetoothPbapManager() : mConnected(false)
, mRemoteMaxPacketLength(0)
, mRequirePhonebookSize(false)
{
mDeviceAddress.AssignLiteral(BLUETOOTH_ADDRESS_NONE);
mCurrentPath.AssignLiteral("");
@ -218,6 +221,17 @@ BluetoothPbapManager::ReceiveSocketData(BluetoothSocket* aSocket,
return;
}
// Save the max packet length from remote information
mRemoteMaxPacketLength = ((static_cast<int>(data[5]) << 8) | data[6]);
if (mRemoteMaxPacketLength < kObexLeastMaxSize) {
BT_LOGR("Remote maximum packet length %d is smaller than %d bytes",
mRemoteMaxPacketLength, kObexLeastMaxSize);
mRemoteMaxPacketLength = 0;
ReplyError(ObexResponseCode::BadRequest);
return;
}
ReplyToConnect();
AfterPbapConnected();
break;
@ -254,7 +268,28 @@ BluetoothPbapManager::ReceiveSocketData(BluetoothSocket* aSocket,
break;
}
case ObexRequestCode::Get:
// Section 6.2.2 "OBEX Headers in Multi-Packet Responses", IrOBEX 1.2
// All OBEX request messages shall be sent as one OBEX packet containing
// all of the headers. I.e. OBEX GET with opcode 0x83 shall always be
// used. OBEX GET with opcode 0x03 shall never be used.
BT_WARNING("PBAP shall always uses OBEX GetFinal instead of Get.");
// no break. Treat 'Get' as 'GetFinal' for error tolerance.
case ObexRequestCode::GetFinal: {
// As long as 'mVCardDataStream' requires multiple response packets to
// complete, the client should continue to issue GET requests until the
// final body information (in an End-of-Body header) arrives, along with
// the response code 0xA0 Success.
if (mVCardDataStream) {
if (!ReplyToGet(mVCardDataStream)) {
BT_WARNING("Failed to reply to PBAP GET request.");
ReplyError(ObexResponseCode::InternalServerError);
}
return;
}
// Section 3.1 "Request format", IrOBEX 1.2
// The format of an OBEX request is
// [opcode:1][length:2][Headers:var]
if (receivedLength < 3 ||
!ParseHeaders(&data[3], receivedLength - 3, &pktHeaders)) {
@ -551,6 +586,11 @@ BluetoothPbapManager::AppendBtNamedValueByTagId(
// convert big endian to little endian
maxListCount = (maxListCount >> 8) | (maxListCount << 8);
// Section 5 "Phone Book Access Profile Functions", PBAP 1.2
// Replying 'PhonebookSize' is mandatory if 'MaxListCount' parameter is
// present in the request with a value of 0, else it is excluded.
mRequirePhonebookSize = !maxListCount;
BT_APPEND_NAMED_VALUE(aValues, "maxListCount", (uint32_t) maxListCount);
break;
}
@ -654,6 +694,14 @@ void
BluetoothPbapManager::AfterPbapDisconnected()
{
mConnected = false;
mRemoteMaxPacketLength = 0;
mRequirePhonebookSize = false;
if (mVCardDataStream) {
mVCardDataStream->Close();
mVCardDataStream = nullptr;
}
}
bool
@ -678,21 +726,21 @@ BluetoothPbapManager::ReplyToConnect()
// Section 3.3.1 "Connect", IrOBEX 1.2
// [opcode:1][length:2][version:1][flags:1][MaxPktSizeWeCanReceive:2]
// [Headers:var]
uint8_t req[255];
uint8_t res[kObexLeastMaxSize];
int index = 7;
req[3] = 0x10; // version=1.0
req[4] = 0x00; // flag=0x00
req[5] = BluetoothPbapManager::MAX_PACKET_LENGTH >> 8;
req[6] = (uint8_t)BluetoothPbapManager::MAX_PACKET_LENGTH;
res[3] = 0x10; // version=1.0
res[4] = 0x00; // flag=0x00
res[5] = BluetoothPbapManager::MAX_PACKET_LENGTH >> 8;
res[6] = (uint8_t)BluetoothPbapManager::MAX_PACKET_LENGTH;
// Section 6.4 "Establishing an OBEX Session", PBAP 1.2
// Headers: [Who:16][Connection ID]
index += AppendHeaderWho(&req[index], 255, kPbapObexTarget.mUuid,
sizeof(BluetoothUuid));
index += AppendHeaderConnectionId(&req[index], 0x01);
index += AppendHeaderWho(&res[index], kObexLeastMaxSize,
kPbapObexTarget.mUuid, sizeof(BluetoothUuid));
index += AppendHeaderConnectionId(&res[index], 0x01);
SendObexData(req, ObexResponseCode::Success, index);
SendObexData(res, ObexResponseCode::Success, index);
}
void
@ -705,10 +753,10 @@ BluetoothPbapManager::ReplyToDisconnectOrAbort()
// Section 3.3.2 "Disconnect" and Section 3.3.5 "Abort", IrOBEX 1.2
// The format of response packet of "Disconnect" and "Abort" are the same
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
uint8_t res[kObexLeastMaxSize];
int index = kObexRespHeaderSize;
SendObexData(req, ObexResponseCode::Success, index);
SendObexData(res, ObexResponseCode::Success, index);
}
void
@ -720,10 +768,10 @@ BluetoothPbapManager::ReplyToSetPath()
// Section 3.3.6 "SetPath", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
uint8_t res[kObexLeastMaxSize];
int index = kObexRespHeaderSize;
SendObexData(req, ObexResponseCode::Success, index);
SendObexData(res, ObexResponseCode::Success, index);
}
InfallibleTArray<uint32_t>
@ -753,52 +801,183 @@ BluetoothPbapManager::PackPropertiesMask(uint8_t* aData, int aSize)
return propSelector;
}
void
bool
BluetoothPbapManager::ReplyToPullPhonebook(BlobParent* aActor,
uint16_t aPhonebookSize)
{
nsRefPtr<BlobImpl> impl = aActor->GetBlobImpl();
nsRefPtr<Blob> blob = Blob::Create(nullptr, impl);
ReplyToPullPhonebook(blob.get(), aPhonebookSize);
return ReplyToPullPhonebook(blob.get(), aPhonebookSize);
}
void
bool
BluetoothPbapManager::ReplyToPullPhonebook(Blob* aBlob, uint16_t aPhonebookSize)
{
// TODO: Implement this function (Bug 1180556)
if (!mConnected) {
return false;
}
if (!GetInputStreamFromBlob(mVCardDataStream, aBlob)) {
ReplyError(ObexResponseCode::InternalServerError);
return false;
}
return ReplyToGet(mVCardDataStream, aPhonebookSize);
}
void
bool
BluetoothPbapManager::ReplyToPullvCardListing(BlobParent* aActor,
uint16_t aPhonebookSize)
{
nsRefPtr<BlobImpl> impl = aActor->GetBlobImpl();
nsRefPtr<Blob> blob = Blob::Create(nullptr, impl);
ReplyToPullvCardListing(blob.get(), aPhonebookSize);
return ReplyToPullvCardListing(blob.get(), aPhonebookSize);
}
void
bool
BluetoothPbapManager::ReplyToPullvCardListing(Blob* aBlob,
uint16_t aPhonebookSize)
{
// TODO: Implement this function (Bug 1180556)
if (!mConnected) {
return false;
}
if (!GetInputStreamFromBlob(mVCardDataStream, aBlob)) {
ReplyError(ObexResponseCode::InternalServerError);
return false;
}
return ReplyToGet(mVCardDataStream, aPhonebookSize);
}
void
bool
BluetoothPbapManager::ReplyToPullvCardEntry(BlobParent* aActor)
{
nsRefPtr<BlobImpl> impl = aActor->GetBlobImpl();
nsRefPtr<Blob> blob = Blob::Create(nullptr, impl);
ReplyToPullvCardEntry(blob.get());
return ReplyToPullvCardEntry(blob.get());
}
void
bool
BluetoothPbapManager::ReplyToPullvCardEntry(Blob* aBlob)
{
// TODO: Implement this function (Bug 1180556)
if (!mConnected) {
return false;
}
if (!GetInputStreamFromBlob(mVCardDataStream, aBlob)) {
ReplyError(ObexResponseCode::InternalServerError);
return false;
}
return ReplyToGet(mVCardDataStream);
}
bool
BluetoothPbapManager::ReplyToGet(nsIInputStream* aStream,
uint16_t aPhonebookSize)
{
MOZ_ASSERT(aStream);
MOZ_ASSERT(mRemoteMaxPacketLength >= kObexLeastMaxSize);
// This response will be composed by these four parts.
// Part 1: [response code:1][length:2]
// Part 2: [headerId:1][length:2][PhonebookSize:4] (optional)
// Part 3: [headerId:1][length:2][Body:var]
// Part 4: [headerId:1][length:2][EndOfBody:0] (optional)
uint8_t* res = new uint8_t[mRemoteMaxPacketLength];
// ---- Part 1, move index for [response code:1][length:2] ---- //
// res[0~2] will be set in SendObexData()
unsigned int index = kObexRespHeaderSize;
// ---- Part 2, add [response code:1][length:2] to response ---- //
if (mRequirePhonebookSize) {
// convert little endian to big endian
uint8_t phonebookSize[2];
phonebookSize[0] = (aPhonebookSize & 0xFF00) >> 8;
phonebookSize[1] = aPhonebookSize & 0x00FF;
// Section 6.2.1 "Application Parameters Header", PBAP 1.2
// appParameters: [headerId:1][length:2][PhonebookSize:4], where
// [PhonebookSize:4] = [tagId:1][length:1][value:2]
uint8_t appParameters[4];
AppendAppParameter(appParameters,
sizeof(appParameters),
(uint8_t) AppParameterTag::PhonebookSize,
phonebookSize,
sizeof(phonebookSize));
index += AppendHeaderAppParameters(&res[index],
mRemoteMaxPacketLength,
appParameters,
sizeof(appParameters));
mRequirePhonebookSize = false;
}
// ---- Part 3, add [headerId:1][length:2][Body:var] to response ---- //
// Remaining packet size to append Body, excluding Body's header
uint32_t remainingPacketSize = mRemoteMaxPacketLength - kObexBodyHeaderSize
- index;
// Read vCard data from input stream
uint32_t numRead = 0;
nsAutoArrayPtr<char> buffer(new char[remainingPacketSize]);
nsresult rv = aStream->Read(buffer, remainingPacketSize, &numRead);
if (NS_FAILED(rv)) {
BT_WARNING("Failed to read from input stream.");
return false;
}
if (numRead) {
index += AppendHeaderBody(&res[index],
remainingPacketSize,
(uint8_t*) buffer.forget(),
numRead);
}
// More GET requests are required if remaining packet size isn't
// enough for 1) number of bytes read and 2) one EndOfBody's header
uint8_t opcode;
if (numRead + kObexBodyHeaderSize > remainingPacketSize) {
opcode = ObexResponseCode::Continue;
} else {
// ---- Part 4, add [headerId:1][length:2][EndOfBody:var] to response --- //
opcode = ObexResponseCode::Success;
index += AppendHeaderEndOfBody(&res[index]);
aStream->Close();
aStream = nullptr;
}
SendObexData(res, opcode, index);
delete [] res;
return true;
}
bool
BluetoothPbapManager::GetInputStreamFromBlob(nsIInputStream* aStream,
Blob* aBlob)
{
// PBAP can only handle one OBEX BODY transfer at the same time.
if (mVCardDataStream) {
BT_WARNING("Shouldn't handle multiple PBAP responses at the same time");
mVCardDataStream->Close();
mVCardDataStream = nullptr;
}
ErrorResult rv;
aBlob->GetInternalStream(getter_AddRefs(mVCardDataStream), rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
return true;
}
void
@ -807,11 +986,9 @@ BluetoothPbapManager::ReplyError(uint8_t aError)
BT_LOGR("[0x%x]", aError);
// Section 3.2 "Response Format", IrOBEX 1.2
// [opcode:1][length:2][Headers:var]
uint8_t req[255];
int index = 3;
SendObexData(req, aError, index);
// [response code:1][length:2][data:var]
uint8_t res[kObexLeastMaxSize];
SendObexData(res, aError, kObexBodyHeaderSize);
}
void

Просмотреть файл

@ -13,6 +13,8 @@
#include "mozilla/dom/bluetooth/BluetoothTypes.h"
#include "mozilla/ipc/SocketBase.h"
class nsIInputStream;
namespace mozilla {
namespace dom {
class Blob;
@ -69,46 +71,64 @@ public:
*
* @param aActor [in] a blob actor containing the vCard objects
* @param aPhonebookSize [in] the number of vCard indexes in the blob
*
* @return true if the response packet has been packed correctly and started
* to be sent to the remote device; false otherwise.
*/
void ReplyToPullPhonebook(BlobParent* aActor, uint16_t aPhonebookSize);
bool ReplyToPullPhonebook(BlobParent* aActor, uint16_t aPhonebookSize);
/**
* Reply vCard object to the *in-process* 'pullphonebook' request.
*
* @param aBlob [in] a blob contained the vCard objects
* @param aPhonebookSize [in] the number of vCard indexes in the blob
*
* @return true if the response packet has been packed correctly and started
* to be sent to the remote device; false otherwise.
*/
void ReplyToPullPhonebook(Blob* aBlob, uint16_t aPhonebookSize);
bool ReplyToPullPhonebook(Blob* aBlob, uint16_t aPhonebookSize);
/**
* Reply vCard object to the *IPC* 'pullvcardlisting' request.
*
* @param aActor [in] a blob actor containing the vCard objects
* @param aPhonebookSize [in] the number of vCard indexes in the blob
*
* @return true if the response packet has been packed correctly and started
* to be sent to the remote device; false otherwise.
*/
void ReplyToPullvCardListing(BlobParent* aActor, uint16_t aPhonebookSize);
bool ReplyToPullvCardListing(BlobParent* aActor, uint16_t aPhonebookSize);
/**
* Reply vCard object to the *in-process* 'pullvcardlisting' request.
*
* @param aBlob [in] a blob contained the vCard objects
* @param aPhonebookSize [in] the number of vCard indexes in the blob
*
* @return true if the response packet has been packed correctly and started
* to be sent to the remote device; false otherwise.
*/
void ReplyToPullvCardListing(Blob* aBlob, uint16_t aPhonebookSize);
bool ReplyToPullvCardListing(Blob* aBlob, uint16_t aPhonebookSize);
/**
* Reply vCard object to the *IPC* 'pullvcardentry' request.
*
* @param aActor [in] a blob actor containing the vCard objects
*
* @return true if the response packet has been packed correctly and started
* to be sent to the remote device; false otherwise.
*/
void ReplyToPullvCardEntry(BlobParent* aActor);
bool ReplyToPullvCardEntry(BlobParent* aActor);
/**
* Reply vCard object to the *in-process* 'pullvcardentry' request.
*
* @param aBlob [in] a blob contained the vCard objects
*
* @return true if the response packet has been packed correctly and started
* to be sent to the remote device; false otherwise.
*/
void ReplyToPullvCardEntry(Blob* aBlob);
bool ReplyToPullvCardEntry(Blob* aBlob);
protected:
virtual ~BluetoothPbapManager();
@ -123,6 +143,8 @@ private:
void ReplyToSetPath();
void ReplyError(uint8_t aError);
void SendObexData(uint8_t* aData, uint8_t aOpcode, int aSize);
bool ReplyToGet(nsIInputStream* aStream, uint16_t aPhonebookSize = 0);
bool GetInputStreamFromBlob(nsIInputStream* aStream, Blob* aBlob);
uint8_t SetPhoneBookPath(uint8_t flags, const ObexHeaderSet& aHeader);
uint8_t PullPhonebook(const ObexHeaderSet& aHeader);
@ -150,6 +172,11 @@ private:
bool mConnected;
nsString mDeviceAddress;
/**
* Maximum packet length that remote device can receive
*/
unsigned int mRemoteMaxPacketLength;
// If a connection has been established, mSocket will be the socket
// communicating with the remote socket. We maintain the invariant that if
// mSocket is non-null, mServerSocket must be null (and vice versa).
@ -159,6 +186,17 @@ private:
// over the ownership to mSocket, and get a new server socket while Listen()
// is called.
nsRefPtr<BluetoothSocket> mServerSocket;
/**
* The data stream of vCards which is used in current processing response.
*/
nsCOMPtr<nsIInputStream> mVCardDataStream;
/**
* A flag to indicate whether 'PhonebookSize' is mandatory for next OBEX
* response
*/
bool mRequirePhonebookSize;
};
END_BLUETOOTH_NAMESPACE