/*
* Copyright (C) 2010 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.
*/
#define LOG_TAG "MtpProperty"
#include <inttypes.h>
#include <cutils/compiler.h>
#include "MtpDataPacket.h"
#include "MtpDebug.h"
#include "MtpProperty.h"
#include "MtpStringBuffer.h"
#include "MtpUtils.h"
namespace android {
MtpProperty::MtpProperty()
: mCode(0),
mType(0),
mWriteable(false),
mDefaultArrayLength(0),
mDefaultArrayValues(NULL),
mCurrentArrayLength(0),
mCurrentArrayValues(NULL),
mGroupCode(0),
mFormFlag(kFormNone),
mEnumLength(0),
mEnumValues(NULL)
{
memset(&mDefaultValue, 0, sizeof(mDefaultValue));
memset(&mCurrentValue, 0, sizeof(mCurrentValue));
memset(&mMinimumValue, 0, sizeof(mMinimumValue));
memset(&mMaximumValue, 0, sizeof(mMaximumValue));
}
MtpProperty::MtpProperty(MtpPropertyCode propCode,
MtpDataType type,
bool writeable,
int defaultValue)
: mCode(propCode),
mType(type),
mWriteable(writeable),
mDefaultArrayLength(0),
mDefaultArrayValues(NULL),
mCurrentArrayLength(0),
mCurrentArrayValues(NULL),
mGroupCode(0),
mFormFlag(kFormNone),
mEnumLength(0),
mEnumValues(NULL)
{
memset(&mDefaultValue, 0, sizeof(mDefaultValue));
memset(&mCurrentValue, 0, sizeof(mCurrentValue));
memset(&mMinimumValue, 0, sizeof(mMinimumValue));
memset(&mMaximumValue, 0, sizeof(mMaximumValue));
if (defaultValue) {
switch (type) {
case MTP_TYPE_INT8:
mDefaultValue.u.i8 = defaultValue;
break;
case MTP_TYPE_UINT8:
mDefaultValue.u.u8 = defaultValue;
break;
case MTP_TYPE_INT16:
mDefaultValue.u.i16 = defaultValue;
break;
case MTP_TYPE_UINT16:
mDefaultValue.u.u16 = defaultValue;
break;
case MTP_TYPE_INT32:
mDefaultValue.u.i32 = defaultValue;
break;
case MTP_TYPE_UINT32:
mDefaultValue.u.u32 = defaultValue;
break;
case MTP_TYPE_INT64:
mDefaultValue.u.i64 = defaultValue;
break;
case MTP_TYPE_UINT64:
mDefaultValue.u.u64 = defaultValue;
break;
default:
ALOGE("unknown type %04X in MtpProperty::MtpProperty", type);
}
}
}
MtpProperty::~MtpProperty() {
if (mType == MTP_TYPE_STR) {
// free all strings
free(mDefaultValue.str);
free(mCurrentValue.str);
free(mMinimumValue.str);
free(mMaximumValue.str);
if (mDefaultArrayValues) {
for (uint32_t i = 0; i < mDefaultArrayLength; i++)
free(mDefaultArrayValues[i].str);
}
if (mCurrentArrayValues) {
for (uint32_t i = 0; i < mCurrentArrayLength; i++)
free(mCurrentArrayValues[i].str);
}
if (mEnumValues) {
for (uint16_t i = 0; i < mEnumLength; i++)
free(mEnumValues[i].str);
}
}
delete[] mDefaultArrayValues;
delete[] mCurrentArrayValues;
delete[] mEnumValues;
}
bool MtpProperty::read(MtpDataPacket& packet) {
uint8_t temp8;
if (!packet.getUInt16(mCode)) return false;
bool deviceProp = isDeviceProperty();
if (!packet.getUInt16(mType)) return false;
if (!packet.getUInt8(temp8)) return false;
mWriteable = (temp8 == 1);
switch (mType) {
case MTP_TYPE_AINT8:
case MTP_TYPE_AUINT8:
case MTP_TYPE_AINT16:
case MTP_TYPE_AUINT16:
case MTP_TYPE_AINT32:
case MTP_TYPE_AUINT32:
case MTP_TYPE_AINT64:
case MTP_TYPE_AUINT64:
case MTP_TYPE_AINT128:
case MTP_TYPE_AUINT128:
mDefaultArrayValues = readArrayValues(packet, mDefaultArrayLength);
if (!mDefaultArrayValues) return false;
if (deviceProp) {
mCurrentArrayValues = readArrayValues(packet, mCurrentArrayLength);
if (!mCurrentArrayValues) return false;
}
break;
default:
if (!readValue(packet, mDefaultValue)) return false;
if (deviceProp) {
if (!readValue(packet, mCurrentValue)) return false;
}
}
if (!deviceProp) {
if (!packet.getUInt32(mGroupCode)) return false;
}
if (!packet.getUInt8(mFormFlag)) return false;
if (mFormFlag == kFormRange) {
if (!readValue(packet, mMinimumValue)) return false;
if (!readValue(packet, mMaximumValue)) return false;
if (!readValue(packet, mStepSize)) return false;
} else if (mFormFlag == kFormEnum) {
if (!packet.getUInt16(mEnumLength)) return false;
mEnumValues = new MtpPropertyValue[mEnumLength];
for (int i = 0; i < mEnumLength; i++) {
if (!readValue(packet, mEnumValues[i])) return false;
}
}
return true;
}
void MtpProperty::write(MtpDataPacket& packet) {
bool deviceProp = isDeviceProperty();
packet.putUInt16(mCode);
packet.putUInt16(mType);
packet.putUInt8(mWriteable ? 1 : 0);
switch (mType) {
case MTP_TYPE_AINT8:
case MTP_TYPE_AUINT8:
case MTP_TYPE_AINT16:
case MTP_TYPE_AUINT16:
case MTP_TYPE_AINT32:
case MTP_TYPE_AUINT32:
case MTP_TYPE_AINT64:
case MTP_TYPE_AUINT64:
case MTP_TYPE_AINT128:
case MTP_TYPE_AUINT128:
writeArrayValues(packet, mDefaultArrayValues, mDefaultArrayLength);
if (deviceProp)
writeArrayValues(packet, mCurrentArrayValues, mCurrentArrayLength);
break;
default:
writeValue(packet, mDefaultValue);
if (deviceProp)
writeValue(packet, mCurrentValue);
}
if (!deviceProp)
packet.putUInt32(mGroupCode);
packet.putUInt8(mFormFlag);
if (mFormFlag == kFormRange) {
writeValue(packet, mMinimumValue);
writeValue(packet, mMaximumValue);
writeValue(packet, mStepSize);
} else if (mFormFlag == kFormEnum) {
packet.putUInt16(mEnumLength);
for (int i = 0; i < mEnumLength; i++)
writeValue(packet, mEnumValues[i]);
}
}
void MtpProperty::setDefaultValue(const uint16_t* string) {
free(mDefaultValue.str);
if (string) {
MtpStringBuffer buffer(string);
mDefaultValue.str = strdup(buffer);
}
else
mDefaultValue.str = NULL;
}
void MtpProperty::setCurrentValue(const uint16_t* string) {
free(mCurrentValue.str);
if (string) {
MtpStringBuffer buffer(string);
mCurrentValue.str = strdup(buffer);
}
else
mCurrentValue.str = NULL;
}
void MtpProperty::setCurrentValue(MtpDataPacket& packet) {
free(mCurrentValue.str);
mCurrentValue.str = NULL;
readValue(packet, mCurrentValue);
}
void MtpProperty::setFormRange(int min, int max, int step) {
mFormFlag = kFormRange;
switch (mType) {
case MTP_TYPE_INT8:
mMinimumValue.u.i8 = min;
mMaximumValue.u.i8 = max;
mStepSize.u.i8 = step;
break;
case MTP_TYPE_UINT8:
mMinimumValue.u.u8 = min;
mMaximumValue.u.u8 = max;
mStepSize.u.u8 = step;
break;
case MTP_TYPE_INT16:
mMinimumValue.u.i16 = min;
mMaximumValue.u.i16 = max;
mStepSize.u.i16 = step;
break;
case MTP_TYPE_UINT16:
mMinimumValue.u.u16 = min;
mMaximumValue.u.u16 = max;
mStepSize.u.u16 = step;
break;
case MTP_TYPE_INT32:
mMinimumValue.u.i32 = min;
mMaximumValue.u.i32 = max;
mStepSize.u.i32 = step;
break;
case MTP_TYPE_UINT32:
mMinimumValue.u.u32 = min;
mMaximumValue.u.u32 = max;
mStepSize.u.u32 = step;
break;
case MTP_TYPE_INT64:
mMinimumValue.u.i64 = min;
mMaximumValue.u.i64 = max;
mStepSize.u.i64 = step;
break;
case MTP_TYPE_UINT64:
mMinimumValue.u.u64 = min;
mMaximumValue.u.u64 = max;
mStepSize.u.u64 = step;
break;
default:
ALOGE("unsupported type for MtpProperty::setRange");
break;
}
}
void MtpProperty::setFormEnum(const int* values, int count) {
mFormFlag = kFormEnum;
delete[] mEnumValues;
mEnumValues = new MtpPropertyValue[count];
mEnumLength = count;
for (int i = 0; i < count; i++) {
int value = *values++;
switch (mType) {
case MTP_TYPE_INT8:
mEnumValues[i].u.i8 = value;
break;
case MTP_TYPE_UINT8:
mEnumValues[i].u.u8 = value;
break;
case MTP_TYPE_INT16:
mEnumValues[i].u.i16 = value;
break;
case MTP_TYPE_UINT16:
mEnumValues[i].u.u16 = value;
break;
case MTP_TYPE_INT32:
mEnumValues[i].u.i32 = value;
break;
case MTP_TYPE_UINT32:
mEnumValues[i].u.u32 = value;
break;
case MTP_TYPE_INT64:
mEnumValues[i].u.i64 = value;
break;
case MTP_TYPE_UINT64:
mEnumValues[i].u.u64 = value;
break;
default:
ALOGE("unsupported type for MtpProperty::setEnum");
break;
}
}
}
void MtpProperty::setFormDateTime() {
mFormFlag = kFormDateTime;
}
void MtpProperty::print() {
MtpString buffer;
bool deviceProp = isDeviceProperty();
if (deviceProp)
ALOGI(" %s (%04X)", MtpDebug::getDevicePropCodeName(mCode), mCode);
else
ALOGI(" %s (%04X)", MtpDebug::getObjectPropCodeName(mCode), mCode);
ALOGI(" type %04X", mType);
ALOGI(" writeable %s", (mWriteable ? "true" : "false"));
buffer = " default value: ";
print(mDefaultValue, buffer);
ALOGI("%s", (const char *)buffer);
if (deviceProp) {
buffer = " current value: ";
print(mCurrentValue, buffer);
ALOGI("%s", (const char *)buffer);
}
switch (mFormFlag) {
case kFormNone:
break;
case kFormRange:
buffer = " Range (";
print(mMinimumValue, buffer);
buffer += ", ";
print(mMaximumValue, buffer);
buffer += ", ";
print(mStepSize, buffer);
buffer += ")";
ALOGI("%s", (const char *)buffer);
break;
case kFormEnum:
buffer = " Enum { ";
for (int i = 0; i < mEnumLength; i++) {
print(mEnumValues[i], buffer);
buffer += " ";
}
buffer += "}";
ALOGI("%s", (const char *)buffer);
break;
case kFormDateTime:
ALOGI(" DateTime\n");
break;
default:
ALOGI(" form %d\n", mFormFlag);
break;
}
}
void MtpProperty::print(MtpPropertyValue& value, MtpString& buffer) {
switch (mType) {
case MTP_TYPE_INT8:
buffer.appendFormat("%d", value.u.i8);
break;
case MTP_TYPE_UINT8:
buffer.appendFormat("%d", value.u.u8);
break;
case MTP_TYPE_INT16:
buffer.appendFormat("%d", value.u.i16);
break;
case MTP_TYPE_UINT16:
buffer.appendFormat("%d", value.u.u16);
break;
case MTP_TYPE_INT32:
buffer.appendFormat("%d", value.u.i32);
break;
case MTP_TYPE_UINT32:
buffer.appendFormat("%d", value.u.u32);
break;
case MTP_TYPE_INT64:
buffer.appendFormat("%" PRId64, value.u.i64);
break;
case MTP_TYPE_UINT64:
buffer.appendFormat("%" PRIu64, value.u.u64);
break;
case MTP_TYPE_INT128:
buffer.appendFormat("%08X%08X%08X%08X", value.u.i128[0], value.u.i128[1],
value.u.i128[2], value.u.i128[3]);
break;
case MTP_TYPE_UINT128:
buffer.appendFormat("%08X%08X%08X%08X", value.u.u128[0], value.u.u128[1],
value.u.u128[2], value.u.u128[3]);
break;
case MTP_TYPE_STR:
buffer.appendFormat("%s", value.str);
break;
default:
ALOGE("unsupported type for MtpProperty::print\n");
break;
}
}
bool MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) {
MtpStringBuffer stringBuffer;
switch (mType) {
case MTP_TYPE_INT8:
case MTP_TYPE_AINT8:
if (!packet.getInt8(value.u.i8)) return false;
break;
case MTP_TYPE_UINT8:
case MTP_TYPE_AUINT8:
if (!packet.getUInt8(value.u.u8)) return false;
break;
case MTP_TYPE_INT16:
case MTP_TYPE_AINT16:
if (!packet.getInt16(value.u.i16)) return false;
break;
case MTP_TYPE_UINT16:
case MTP_TYPE_AUINT16:
if (!packet.getUInt16(value.u.u16)) return false;
break;
case MTP_TYPE_INT32:
case MTP_TYPE_AINT32:
if (!packet.getInt32(value.u.i32)) return false;
break;
case MTP_TYPE_UINT32:
case MTP_TYPE_AUINT32:
if (!packet.getUInt32(value.u.u32)) return false;
break;
case MTP_TYPE_INT64:
case MTP_TYPE_AINT64:
if (!packet.getInt64(value.u.i64)) return false;
break;
case MTP_TYPE_UINT64:
case MTP_TYPE_AUINT64:
if (!packet.getUInt64(value.u.u64)) return false;
break;
case MTP_TYPE_INT128:
case MTP_TYPE_AINT128:
if (!packet.getInt128(value.u.i128)) return false;
break;
case MTP_TYPE_UINT128:
case MTP_TYPE_AUINT128:
if (!packet.getUInt128(value.u.u128)) return false;
break;
case MTP_TYPE_STR:
if (!packet.getString(stringBuffer)) return false;
value.str = strdup(stringBuffer);
break;
default:
ALOGE("unknown type %04X in MtpProperty::readValue", mType);
return false;
}
return true;
}
void MtpProperty::writeValue(MtpDataPacket& packet, MtpPropertyValue& value) {
MtpStringBuffer stringBuffer;
switch (mType) {
case MTP_TYPE_INT8:
case MTP_TYPE_AINT8:
packet.putInt8(value.u.i8);
break;
case MTP_TYPE_UINT8:
case MTP_TYPE_AUINT8:
packet.putUInt8(value.u.u8);
break;
case MTP_TYPE_INT16:
case MTP_TYPE_AINT16:
packet.putInt16(value.u.i16);
break;
case MTP_TYPE_UINT16:
case MTP_TYPE_AUINT16:
packet.putUInt16(value.u.u16);
break;
case MTP_TYPE_INT32:
case MTP_TYPE_AINT32:
packet.putInt32(value.u.i32);
break;
case MTP_TYPE_UINT32:
case MTP_TYPE_AUINT32:
packet.putUInt32(value.u.u32);
break;
case MTP_TYPE_INT64:
case MTP_TYPE_AINT64:
packet.putInt64(value.u.i64);
break;
case MTP_TYPE_UINT64:
case MTP_TYPE_AUINT64:
packet.putUInt64(value.u.u64);
break;
case MTP_TYPE_INT128:
case MTP_TYPE_AINT128:
packet.putInt128(value.u.i128);
break;
case MTP_TYPE_UINT128:
case MTP_TYPE_AUINT128:
packet.putUInt128(value.u.u128);
break;
case MTP_TYPE_STR:
if (value.str)
packet.putString(value.str);
else
packet.putEmptyString();
break;
default:
ALOGE("unknown type %04X in MtpProperty::writeValue", mType);
}
}
MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, uint32_t& length) {
if (!packet.getUInt32(length)) return NULL;
// Fail if resulting array is over 2GB. This is because the maximum array
// size may be less than SIZE_MAX on some platforms.
if ( CC_UNLIKELY(
length == 0 ||
length >= INT32_MAX / sizeof(MtpPropertyValue)) ) {
length = 0;
return NULL;
}
MtpPropertyValue* result = new MtpPropertyValue[length];
for (uint32_t i = 0; i < length; i++)
if (!readValue(packet, result[i])) {
delete [] result;
return NULL;
}
return result;
}
void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, uint32_t length) {
packet.putUInt32(length);
for (uint32_t i = 0; i < length; i++)
writeValue(packet, values[i]);
}
} // namespace android