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: LOG(ERROR) << "unknown type " << std::hex << mType << std::dec << " in MtpProperty::writeValue"; } }
int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) { MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property); if (groupCode != 0) { MTPE("getObjectPropertyList: groupCode unsupported\n"); return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED } // TODO: support all the special stuff, like: // handle == 0 -> all objects at the root level // handle == 0xffffffff -> all objects (on all storages? how could we support that?) // format == 0 -> all formats, otherwise filter by ObjectFormatCode // property == 0xffffffff -> all properties except those with group code 0xffffffff // if property == 0 then use groupCode // groupCode == 0 -> return Specification_By_Group_Unsupported // depth == 0xffffffff -> all objects incl. and below handle std::vector<PropEntry> results; if (handle == 0xffffffff) { // TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage) } else if (handle == 0) { // all objects at the root level Tree* root = mtpmap[0]; MtpObjectHandleList list; root->getmtpids(&list); for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) { Node* node = root->findNode(*it); if (!node) { MTPE("BUG: node not found for root entry with handle %u\n", *it); break; } queryNodeProperties(results, node, property, groupCode, mStorageID); } } else { // single object Node* node = findNode(handle); if (!node) { // Item is not on this storage device return -1; } queryNodeProperties(results, node, property, groupCode, mStorageID); } MTPD("count: %u\n", results.size()); packet.putUInt32(results.size()); for (size_t i = 0; i < results.size(); ++i) { PropEntry& p = results[i]; MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n", p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property), p.datatype, p.intvalue); packet.putUInt32(p.handle); packet.putUInt16(p.property); packet.putUInt16(p.datatype); switch (p.datatype) { case MTP_TYPE_INT8: MTPD("MTP_TYPE_INT8\n"); packet.putInt8(p.intvalue); break; case MTP_TYPE_UINT8: MTPD("MTP_TYPE_UINT8\n"); packet.putUInt8(p.intvalue); break; case MTP_TYPE_INT16: MTPD("MTP_TYPE_INT16\n"); packet.putInt16(p.intvalue); break; case MTP_TYPE_UINT16: MTPD("MTP_TYPE_UINT16\n"); packet.putUInt16(p.intvalue); break; case MTP_TYPE_INT32: MTPD("MTP_TYPE_INT32\n"); packet.putInt32(p.intvalue); break; case MTP_TYPE_UINT32: MTPD("MTP_TYPE_UINT32\n"); packet.putUInt32(p.intvalue); break; case MTP_TYPE_INT64: MTPD("MTP_TYPE_INT64\n"); packet.putInt64(p.intvalue); break; case MTP_TYPE_UINT64: MTPD("MTP_TYPE_UINT64\n"); packet.putUInt64(p.intvalue); break; case MTP_TYPE_INT128: MTPD("MTP_TYPE_INT128\n"); packet.putInt128(p.intvalue); break; case MTP_TYPE_UINT128: MTPD("MTP_TYPE_UINT128\n"); packet.putUInt128(p.intvalue); break; case MTP_TYPE_STR: MTPD("MTP_TYPE_STR: %s\n", p.strvalue.c_str()); packet.putString(p.strvalue.c_str()); break; default: MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype); break; } } return 0; }
MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) { JNIEnv* env = AndroidRuntime::getJNIEnv(); jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList, (jlong)handle, (jint)format, (jlong)property, (jint)groupCode, (jint)depth); checkAndClearExceptionFromCallback(env, __FUNCTION__); if (!list) return MTP_RESPONSE_GENERAL_ERROR; int count = env->GetIntField(list, field_mCount); MtpResponseCode result = env->GetIntField(list, field_mResult); packet.putUInt32(count); if (count > 0) { jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles); jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes); jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes); jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues); jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues); jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0); jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0); jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0); jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL); for (int i = 0; i < count; i++) { packet.putUInt32(objectHandles[i]); packet.putUInt16(propertyCodes[i]); int type = dataTypes[i]; packet.putUInt16(type); switch (type) { case MTP_TYPE_INT8: packet.putInt8(longValues[i]); break; case MTP_TYPE_UINT8: packet.putUInt8(longValues[i]); break; case MTP_TYPE_INT16: packet.putInt16(longValues[i]); break; case MTP_TYPE_UINT16: packet.putUInt16(longValues[i]); break; case MTP_TYPE_INT32: packet.putInt32(longValues[i]); break; case MTP_TYPE_UINT32: packet.putUInt32(longValues[i]); break; case MTP_TYPE_INT64: packet.putInt64(longValues[i]); break; case MTP_TYPE_UINT64: packet.putUInt64(longValues[i]); break; case MTP_TYPE_INT128: packet.putInt128(longValues[i]); break; case MTP_TYPE_UINT128: packet.putUInt128(longValues[i]); break; case MTP_TYPE_STR: { jstring value = (jstring)env->GetObjectArrayElement(stringValuesArray, i); const char *valueStr = (value ? env->GetStringUTFChars(value, NULL) : NULL); if (valueStr) { packet.putString(valueStr); env->ReleaseStringUTFChars(value, valueStr); } else { packet.putEmptyString(); } env->DeleteLocalRef(value); break; } default: ALOGE("bad or unsupported data type in MyMtpDatabase::getObjectPropertyList"); break; } } env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0); env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0); env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0); if (longValues) env->ReleaseLongArrayElements(longValuesArray, longValues, 0); env->DeleteLocalRef(objectHandlesArray); env->DeleteLocalRef(propertyCodesArray); env->DeleteLocalRef(dataTypesArray); if (longValuesArray) env->DeleteLocalRef(longValuesArray); if (stringValuesArray) env->DeleteLocalRef(stringValuesArray); } env->DeleteLocalRef(list); checkAndClearExceptionFromCallback(env, __FUNCTION__); return result; }
MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property, MtpDataPacket& packet) { int type; if (!getDevicePropertyInfo(property, type)) return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED; JNIEnv* env = AndroidRuntime::getJNIEnv(); jint result = env->CallIntMethod(mDatabase, method_getDeviceProperty, (jint)property, mLongBuffer, mStringBuffer); if (result != MTP_RESPONSE_OK) { checkAndClearExceptionFromCallback(env, __FUNCTION__); return result; } jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0); jlong longValue = longValues[0]; env->ReleaseLongArrayElements(mLongBuffer, longValues, 0); switch (type) { case MTP_TYPE_INT8: packet.putInt8(longValue); break; case MTP_TYPE_UINT8: packet.putUInt8(longValue); break; case MTP_TYPE_INT16: packet.putInt16(longValue); break; case MTP_TYPE_UINT16: packet.putUInt16(longValue); break; case MTP_TYPE_INT32: packet.putInt32(longValue); break; case MTP_TYPE_UINT32: packet.putUInt32(longValue); break; case MTP_TYPE_INT64: packet.putInt64(longValue); break; case MTP_TYPE_UINT64: packet.putUInt64(longValue); break; case MTP_TYPE_INT128: packet.putInt128(longValue); break; case MTP_TYPE_UINT128: packet.putInt128(longValue); break; case MTP_TYPE_STR: { jchar* str = env->GetCharArrayElements(mStringBuffer, 0); packet.putString(str); env->ReleaseCharArrayElements(mStringBuffer, str, 0); break; } default: ALOGE("unsupported type in getDevicePropertyValue\n"); return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT; } checkAndClearExceptionFromCallback(env, __FUNCTION__); return MTP_RESPONSE_OK; }
MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpDataPacket& packet) { JNIEnv* env = AndroidRuntime::getJNIEnv(); jobject list = env->CallObjectMethod(mDatabase, method_getObjectPropertyList, (jlong)handle, 0, (jlong)property, 0, 0); MtpResponseCode result = env->GetIntField(list, field_mResult); int count = env->GetIntField(list, field_mCount); if (result == MTP_RESPONSE_OK && count != 1) result = MTP_RESPONSE_GENERAL_ERROR; if (result == MTP_RESPONSE_OK) { jintArray objectHandlesArray = (jintArray)env->GetObjectField(list, field_mObjectHandles); jintArray propertyCodesArray = (jintArray)env->GetObjectField(list, field_mPropertyCodes); jintArray dataTypesArray = (jintArray)env->GetObjectField(list, field_mDataTypes); jlongArray longValuesArray = (jlongArray)env->GetObjectField(list, field_mLongValues); jobjectArray stringValuesArray = (jobjectArray)env->GetObjectField(list, field_mStringValues); jint* objectHandles = env->GetIntArrayElements(objectHandlesArray, 0); jint* propertyCodes = env->GetIntArrayElements(propertyCodesArray, 0); jint* dataTypes = env->GetIntArrayElements(dataTypesArray, 0); jlong* longValues = (longValuesArray ? env->GetLongArrayElements(longValuesArray, 0) : NULL); int type = dataTypes[0]; jlong longValue = (longValues ? longValues[0] : 0); // special case date properties, which are strings to MTP // but stored internally as a uint64 if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) { char date[20]; formatDateTime(longValue, date, sizeof(date)); packet.putString(date); goto out; } // release date is stored internally as just the year if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) { char date[20]; snprintf(date, sizeof(date), "%04" PRId64 "0101T000000", longValue); packet.putString(date); goto out; } switch (type) { case MTP_TYPE_INT8: packet.putInt8(longValue); break; case MTP_TYPE_UINT8: packet.putUInt8(longValue); break; case MTP_TYPE_INT16: packet.putInt16(longValue); break; case MTP_TYPE_UINT16: packet.putUInt16(longValue); break; case MTP_TYPE_INT32: packet.putInt32(longValue); break; case MTP_TYPE_UINT32: packet.putUInt32(longValue); break; case MTP_TYPE_INT64: packet.putInt64(longValue); break; case MTP_TYPE_UINT64: packet.putUInt64(longValue); break; case MTP_TYPE_INT128: packet.putInt128(longValue); break; case MTP_TYPE_UINT128: packet.putInt128(longValue); break; case MTP_TYPE_STR: { jstring stringValue = (jstring)env->GetObjectArrayElement(stringValuesArray, 0); const char* str = (stringValue ? env->GetStringUTFChars(stringValue, NULL) : NULL); if (stringValue) { packet.putString(str); env->ReleaseStringUTFChars(stringValue, str); } else { packet.putEmptyString(); } env->DeleteLocalRef(stringValue); break; } default: ALOGE("unsupported type in getObjectPropertyValue\n"); result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT; } out: env->ReleaseIntArrayElements(objectHandlesArray, objectHandles, 0); env->ReleaseIntArrayElements(propertyCodesArray, propertyCodes, 0); env->ReleaseIntArrayElements(dataTypesArray, dataTypes, 0); if (longValues) env->ReleaseLongArrayElements(longValuesArray, longValues, 0); env->DeleteLocalRef(objectHandlesArray); env->DeleteLocalRef(propertyCodesArray); env->DeleteLocalRef(dataTypesArray); if (longValuesArray) env->DeleteLocalRef(longValuesArray); if (stringValuesArray) env->DeleteLocalRef(stringValuesArray); } env->DeleteLocalRef(list); checkAndClearExceptionFromCallback(env, __FUNCTION__); return result; }