/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
#include <stdint.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <UniquePtr.h>
#include <utils/StrongPointer.h>
#include "audio/Buffer.h"
#include "Log.h"
#include "audio/AudioProtocol.h"
bool AudioProtocol::sendCommand(AudioParam& param)
{
mBuffer[0] = htonl(mCommand);
mBuffer[1] = 0;
return sendData((char*)mBuffer, 8);
}
bool AudioProtocol::handleReply(const uint32_t* data, AudioParam* param)
{
if (!checkHeaderId(data, mCommand)) {
return false;
}
if (data[1] != 0) { // no endian change for 0
LOGE("error in reply %d", ntohl(data[1]));
return false;
}
if (data[2] != 0) {
LOGE("payload length %d not zero", ntohl(data[2]));
return false;
}
return true;
}
bool AudioProtocol::handleReplyHeader(ClientSocket& socket, uint32_t* data, CommandId& id)
{
if (!socket.readData((char*)data, REPLY_HEADER_SIZE)) {
LOGE("handleReplyHeader cannot read");
return false;
}
uint32_t command = ntohl(data[0]);
if ((command & 0xffff0000) != 0x43210000) {
LOGE("Wrong header %x %x", command, data[0]);
return false;
}
command = (command & 0xffff) | 0x12340000; // convert to id
if (command < ECmdStart) {
LOGE("Wrong header %x %x", command, data[0]);
return false;
}
if (command > (ECmdLast - 1)) {
LOGE("Wrong header %x %x", command, data[0]);
return false;
}
id = (CommandId)command;
LOGD("received reply with command %x", command);
return true;
}
bool AudioProtocol::checkHeaderId(const uint32_t* data, uint32_t command)
{
if (ntohl(data[0]) != ((command & 0xffff) | 0x43210000)) {
LOGE("wrong reply ID 0x%x", ntohl(data[0]));
return false;
}
return true;
}
/**
* param0 u32 data id
* param1 sp<Buffer>
*/
bool CmdDownload::sendCommand(AudioParam& param)
{
mBuffer[0] = htonl(ECmdDownload);
mBuffer[1] = htonl(4 + param.mBuffer->getSize());
mBuffer[2] = htonl(param.mId);
if(!sendData((char*)mBuffer, 12)) {
return false;
}
return sendData(param.mBuffer->getData(), param.mBuffer->getSize());
}
/**
* param0 u32 data id
* param1 u32 sampling rate
* param2 u32 mono / stereo(MSB) | mode
* param3 u32 volume
* param4 u32 repeat
*/
bool CmdStartPlayback::sendCommand(AudioParam& param)
{
mBuffer[0] = htonl(ECmdStartPlayback);
mBuffer[1] = htonl(20);
mBuffer[2] = htonl(param.mId);
mBuffer[3] = htonl(param.mSamplingF);
uint32_t mode = param.mStereo ? 0x80000000 : 0;
mode |= param.mMode;
mBuffer[4] = htonl(mode);
mBuffer[5] = htonl(param.mVolume);
mBuffer[6] = htonl(param.mNumberRepetition);
return sendData((char*)mBuffer, 28);
}
/**
* param0 u32 sampling rate
* param1 u32 mono / stereo(MSB) | mode
* param2 u32 volume
* param3 u32 samples
*/
bool CmdStartRecording::sendCommand(AudioParam& param)
{
mBuffer[0] = htonl(ECmdStartRecording);
mBuffer[1] = htonl(16);
mBuffer[2] = htonl(param.mSamplingF);
uint32_t mode = param.mStereo ? 0x80000000 : 0;
mode |= param.mMode;
mBuffer[3] = htonl(mode);
mBuffer[4] = htonl(param.mVolume);
uint32_t samples = param.mBuffer->getSize() / (param.mStereo ? 4 : 2);
mBuffer[5] = htonl(samples);
return sendData((char*)mBuffer, 24);
}
/**
* param0 sp<Buffer>
*/
bool CmdStartRecording::handleReply(const uint32_t* data, AudioParam* param)
{
if (!checkHeaderId(data, ECmdStartRecording)) {
return false;
}
if (data[1] != 0) { // no endian change for 0
LOGE("error in reply %d", ntohl(data[1]));
return false;
}
int len = ntohl(data[2]);
if (len > (int)param->mBuffer->getCapacity()) {
LOGE("received data %d exceeding buffer capacity %d", len, param->mBuffer->getCapacity());
// read and throw away
//Buffer tempBuffer(len);
//readData(tempBuffer.getData(), len);
return false;
}
if (!readData(param->mBuffer->getData(), len)) {
return false;
}
LOGI("received data %d from device", len);
param->mBuffer->setHandled(len);
param->mBuffer->setSize(len);
return true;
}
bool CmdGetDeviceInfo::handleReply(const uint32_t* data, AudioParam* param)
{
if (!checkHeaderId(data, ECmdGetDeviceInfo)) {
return false;
}
if (data[1] != 0) { // no endian change for 0
LOGE("error in reply %d", ntohl(data[1]));
return false;
}
int len = ntohl(data[2]);
UniquePtr<char, DefaultDelete<char[]> > infoString(new char[len + 1]);
if (!readData(infoString.get(), len)) {
return false;
}
(infoString.get())[len] = 0;
LOGI("received data %s from device", infoString.get());
android::String8* string = reinterpret_cast<android::String8*>(param->mExtra);
string->setTo(infoString.get(), len);
return true;
}