MTP: host support for retrieving device property descriptors

Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index ec9f17b..9f684e1 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -61,6 +61,7 @@
                   MtpDeviceInfo.cpp                     \
                   MtpObjectInfo.cpp                     \
                   MtpPacket.cpp                         \
+                  MtpProperty.cpp                       \
                   MtpRequestPacket.cpp                  \
                   MtpResponsePacket.cpp                 \
                   MtpStorageInfo.cpp                    \
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index fa086c5..f96284c 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -70,6 +70,13 @@
     return result;
 }
 
+void MtpDataPacket::getUInt128(uint128_t& value) {
+    value[0] = getUInt32();
+    value[1] = getUInt32();
+    value[2] = getUInt32();
+    value[3] = getUInt32();
+}
+
 void MtpDataPacket::getString(MtpStringBuffer& string)
 {
     string.readFromPacket(this);
@@ -217,6 +224,20 @@
         mPacketSize = mOffset;
 }
 
+void MtpDataPacket::putInt128(const int128_t& value) {
+    putInt32(value[0]);
+    putInt32(value[1]);
+    putInt32(value[2]);
+    putInt32(value[3]);
+}
+
+void MtpDataPacket::putUInt128(const uint128_t& value) {
+    putUInt32(value[0]);
+    putUInt32(value[1]);
+    putUInt32(value[2]);
+    putUInt32(value[3]);
+}
+
 void MtpDataPacket::putAInt8(const int8_t* values, int count) {
     putUInt32(count);
     for (int i = 0; i < count; i++)
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index 03f2b4b..4e743b2 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -44,6 +44,8 @@
     inline int32_t      getInt32() { return (int32_t)getUInt32(); }
     uint64_t            getUInt64();
     inline int64_t      getInt64() { return (int64_t)getUInt64(); }
+    void                getUInt128(uint128_t& value);
+    inline void         getInt128(int128_t& value) { getUInt128((uint128_t&)value); }
     void                getString(MtpStringBuffer& string);
 
     Int8List*           getAInt8();
@@ -63,6 +65,8 @@
     void                putUInt32(uint32_t value);
     void                putInt64(int64_t value);
     void                putUInt64(uint64_t value);
+    void                putInt128(const int128_t& value);
+    void                putUInt128(const uint128_t& value);
 
     void                putAInt8(const int8_t* values, int count);
     void                putAUInt8(const uint8_t* values, int count);
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index 0282086..5c39628 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "MtpDevice"
+#include "utils/Log.h"
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/types.h>
@@ -28,6 +31,7 @@
 #include "MtpDebug.h"
 #include "MtpDeviceInfo.h"
 #include "MtpObjectInfo.h"
+#include "MtpProperty.h"
 #include "MtpStorageInfo.h"
 #include "MtpStringBuffer.h"
 
@@ -50,6 +54,8 @@
 
 MtpDevice::~MtpDevice() {
     close();
+    for (int i = 0; i < mDeviceProperties.size(); i++)
+        delete mDeviceProperties[i];
 }
 
 void MtpDevice::initialize() {
@@ -57,6 +63,18 @@
     mDeviceInfo = getDeviceInfo();
     if (mDeviceInfo) {
         mDeviceInfo->print();
+
+        if (mDeviceInfo->mDeviceProperties) {
+            int count = mDeviceInfo->mDeviceProperties->size();
+            for (int i = 0; i < count; i++) {
+                MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
+                MtpProperty* property = getDevicePropDesc(propCode);
+                if (property) {
+                    property->print();
+                    mDeviceProperties.push(property);
+                }
+            }
+        }
     }
 }
 
@@ -76,7 +94,6 @@
 }
 
 bool MtpDevice::openSession() {
-printf("openSession\n");
     mSessionID = 0;
     mTransactionID = 0;
     MtpSessionID newSession = 1;
@@ -107,7 +124,6 @@
     if (!readData())
         return NULL;
     MtpResponseCode ret = readResponse();
-printf("getDeviceInfo returned %04X\n", ret);
     if (ret == MTP_RESPONSE_OK) {
         MtpDeviceInfo* info = new MtpDeviceInfo;
         info->read(mData);
@@ -137,7 +153,6 @@
     if (!readData())
         return NULL;
     MtpResponseCode ret = readResponse();
-printf("getStorageInfo returned %04X\n", ret);
     if (ret == MTP_RESPONSE_OK) {
         MtpStorageInfo* info = new MtpStorageInfo(storageID);
         info->read(mData);
@@ -157,7 +172,6 @@
     if (!readData())
         return NULL;
     MtpResponseCode ret = readResponse();
-printf("getObjectHandles returned %04X\n", ret);
     if (ret == MTP_RESPONSE_OK) {
         return mData.getAUInt32();
     }
@@ -172,7 +186,6 @@
     if (!readData())
         return NULL;
     MtpResponseCode ret = readResponse();
-printf("getObjectInfo returned %04X\n", ret);
     if (ret == MTP_RESPONSE_OK) {
         MtpObjectInfo* info = new MtpObjectInfo(handle);
         info->read(mData);
@@ -181,8 +194,25 @@
     return NULL;
 }
 
+MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
+    mRequest.reset();
+    mRequest.setParameter(1, code);
+    if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
+        return NULL;
+    if (!readData())
+        return NULL;
+    MtpResponseCode ret = readResponse();
+    if (ret == MTP_RESPONSE_OK) {
+        MtpProperty* property = new MtpProperty;
+        property->read(mData);
+        return property;
+    }
+    return NULL;
+}
+
+
 bool MtpDevice::sendRequest(MtpOperationCode operation) {
-    printf("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
+    LOGD("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
     mRequest.setOperationCode(operation);
     if (mTransactionID > 0)
         mRequest.setTransactionID(mTransactionID++);
@@ -192,7 +222,7 @@
 }
 
 bool MtpDevice::sendData(MtpOperationCode operation) {
-    printf("sendData\n");
+    LOGD("sendData\n");
     mData.setOperationCode(mRequest.getOperationCode());
     mData.setTransactionID(mRequest.getTransactionID());
     int ret = mData.write(mEndpointOut);
@@ -203,26 +233,26 @@
 bool MtpDevice::readData() {
     mData.reset();
     int ret = mData.read(mEndpointIn);
-    printf("readData returned %d\n", ret);
+    LOGD("readData returned %d\n", ret);
     if (ret >= MTP_CONTAINER_HEADER_SIZE) {
         mData.dump();
         return true;
     }
     else {
-        printf("readResponse failed\n");
+        LOGD("readResponse failed\n");
         return false;
     }
 }
 
 MtpResponseCode MtpDevice::readResponse() {
-    printf("readResponse\n");
+    LOGD("readResponse\n");
     int ret = mResponse.read(mEndpointIn);
     if (ret >= MTP_CONTAINER_HEADER_SIZE) {
         mResponse.dump();
         return mResponse.getResponseCode();
     }
     else {
-        printf("readResponse failed\n");
+        LOGD("readResponse failed\n");
         return -1;
     }
 }
diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h
index fe4f1bd..226b247 100644
--- a/media/mtp/MtpDevice.h
+++ b/media/mtp/MtpDevice.h
@@ -36,6 +36,7 @@
     struct usb_endpoint*    mEndpointOut;
     struct usb_endpoint*    mEndpointIntr;
     MtpDeviceInfo*          mDeviceInfo;
+    MtpPropertyList         mDeviceProperties;
 
     // a unique ID for the device
     int                     mID;
@@ -70,6 +71,8 @@
     MtpObjectHandleList*    getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent);
     MtpObjectInfo*          getObjectInfo(MtpObjectHandle handle);
 
+    MtpProperty*            getDevicePropDesc(MtpDeviceProperty code);
+
 private:
     bool                    sendRequest(MtpOperationCode operation);
     bool                    sendData(MtpOperationCode operation);
diff --git a/media/mtp/MtpDeviceInfo.cpp b/media/mtp/MtpDeviceInfo.cpp
index 210dfcc..eb25fb3 100644
--- a/media/mtp/MtpDeviceInfo.cpp
+++ b/media/mtp/MtpDeviceInfo.cpp
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#include <stdlib.h>
+#define LOG_TAG "MtpDeviceInfo"
+#include "utils/Log.h"
 
 #include "MtpDataPacket.h"
 #include "MtpDeviceInfo.h"
@@ -87,9 +88,9 @@
 }
 
 void MtpDeviceInfo::print() {
-    printf("Device Info:\n\tmStandardVersion: %d\n\tmVendorExtensionID: %d\n\tmVendorExtensionVersiony: %d\n",
+    LOGD("Device Info:\n\tmStandardVersion: %d\n\tmVendorExtensionID: %d\n\tmVendorExtensionVersiony: %d\n",
             mStandardVersion, mVendorExtensionID, mVendorExtensionVersion);
-    printf("\tmVendorExtensionDesc: %s\n\tmFunctionalCode: %d\n\tmManufacturer: %s\n\tmModel: %s\n\tmVersion: %s\n\tmSerial: %s\n",
+    LOGD("\tmVendorExtensionDesc: %s\n\tmFunctionalCode: %d\n\tmManufacturer: %s\n\tmModel: %s\n\tmVersion: %s\n\tmSerial: %s\n",
             mVendorExtensionDesc, mFunctionalCode, mManufacturer, mModel, mVersion, mSerial);
 }
 
diff --git a/media/mtp/MtpObjectInfo.cpp b/media/mtp/MtpObjectInfo.cpp
index 8ca2880..a0ee2b6 100644
--- a/media/mtp/MtpObjectInfo.cpp
+++ b/media/mtp/MtpObjectInfo.cpp
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#include <stdlib.h>
+#define LOG_TAG "MtpObjectInfo"
+#include "utils/Log.h"
 
 #include "MtpDataPacket.h"
 #include "MtpObjectInfo.h"
@@ -90,7 +91,7 @@
 }
 
 void MtpObjectInfo::print() {
-    printf("MtpObject Info %08X: %s\n", mHandle, mName);
+    LOGD("MtpObject Info %08X: %s\n", mHandle, mName);
 }
 
 }  // namespace android
diff --git a/media/mtp/MtpProperty.cpp b/media/mtp/MtpProperty.cpp
new file mode 100644
index 0000000..65ce4d4
--- /dev/null
+++ b/media/mtp/MtpProperty.cpp
@@ -0,0 +1,161 @@
+/*
+ * 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 "utils/Log.h"
+
+#include "MtpDataPacket.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),
+        mFormFlag(kFormNone),
+        mEnumLength(0),
+        mEnumValues(NULL)
+{
+    mDefaultValue.str = NULL;
+    mCurrentValue.str = NULL;
+    mMinimumValue.str = NULL;
+    mMaximumValue.str = NULL;
+}
+
+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 (int i = 0; i < mDefaultArrayLength; i++)
+                free(mDefaultArrayValues[i].str);
+        }
+        if (mCurrentArrayValues) {
+            for (int i = 0; i < mCurrentArrayLength; i++)
+                free(mCurrentArrayValues[i].str);
+        }
+        if (mEnumValues) {
+            for (int i = 0; i < mEnumLength; i++)
+                free(mEnumValues[i].str);
+        }
+    }
+    delete[] mDefaultArrayValues;
+    delete[] mCurrentArrayValues;
+    delete[] mEnumValues;
+}
+
+void MtpProperty::read(MtpDataPacket& packet) {
+    MtpStringBuffer string;
+
+    mCode = packet.getUInt16();
+    mType = packet.getUInt16();
+    mWriteable = (packet.getUInt8() == 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);
+            mCurrentArrayValues = readArrayValues(packet, mCurrentArrayLength);
+            break;
+        default:
+            readValue(packet, mDefaultValue);
+            readValue(packet, mCurrentValue);
+    }
+    mFormFlag = packet.getUInt8();
+
+    if (mFormFlag == kFormRange) {
+            readValue(packet, mMinimumValue);
+            readValue(packet, mMaximumValue);
+            readValue(packet, mStepSize);
+    } else if (mFormFlag == kFormEnum) {
+        mEnumLength = packet.getUInt16();
+        mEnumValues = new MtpPropertyValue[mEnumLength];
+        for (int i = 0; i < mEnumLength; i++)
+            readValue(packet, mEnumValues[i]);
+    }
+}
+
+void MtpProperty::print() {
+    LOGD("MtpProperty %04X\n", mCode);
+    LOGD("    type %04X\n", mType);
+    LOGD("    writeable %s\n", (mWriteable ? "true" : "false"));
+}
+
+void MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) {
+    switch (mType) {
+        case MTP_TYPE_INT8:
+            value.i8 = packet.getInt8();
+            break;
+        case MTP_TYPE_UINT8:
+            value.u8 = packet.getUInt8();
+            break;
+        case MTP_TYPE_INT16:
+            value.i16 = packet.getInt16();
+            break;
+        case MTP_TYPE_UINT16:
+            value.u16 = packet.getUInt16();
+            break;
+        case MTP_TYPE_INT32:
+            value.i32 = packet.getInt32();
+            break;
+        case MTP_TYPE_UINT32:
+            value.u32 = packet.getUInt32();
+            break;
+        case MTP_TYPE_INT64:
+            value.i64 = packet.getInt64();
+            break;
+        case MTP_TYPE_UINT64:
+            value.u64 = packet.getUInt64();
+            break;
+        case MTP_TYPE_INT128:
+            packet.getInt128(value.i128);
+            break;
+        case MTP_TYPE_UINT128:
+            packet.getUInt128(value.u128);
+            break;
+        default:
+            fprintf(stderr, "unknown type %d in MtpProperty::readValue\n", mType);
+    }
+}
+
+MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, int& length) {
+    length = packet.getUInt32();
+    if (length == 0)
+        return NULL;
+    MtpPropertyValue* result = new MtpPropertyValue[length];
+    for (int i = 0; i < length; i++)
+        readValue(packet, result[i]);
+    return result;
+}
+
+}  // namespace android
diff --git a/media/mtp/MtpProperty.h b/media/mtp/MtpProperty.h
new file mode 100644
index 0000000..6372290
--- /dev/null
+++ b/media/mtp/MtpProperty.h
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#ifndef _MTP_PROPERTY_H
+#define _MTP_PROPERTY_H
+
+#include "MtpTypes.h"
+
+namespace android {
+
+class MtpDataPacket;
+
+class MtpProperty {
+public:
+    MtpPropertyCode     mCode;
+    MtpDataType         mType;
+    bool                mWriteable;
+    MtpPropertyValue    mDefaultValue;
+    MtpPropertyValue    mCurrentValue;
+
+    // for array types
+    int                 mDefaultArrayLength;
+    MtpPropertyValue*   mDefaultArrayValues;
+    int                 mCurrentArrayLength;
+    MtpPropertyValue*   mCurrentArrayValues;
+
+    enum {
+        kFormNone = 0,
+        kFormRange = 1,
+        kFormEnum = 2,
+    };
+    uint8_t             mFormFlag;
+
+    // for range form
+    MtpPropertyValue    mMinimumValue;
+    MtpPropertyValue    mMaximumValue;
+    MtpPropertyValue    mStepSize;
+
+    // for enum form
+    int                 mEnumLength;
+    MtpPropertyValue*   mEnumValues;
+
+public:
+                        MtpProperty();
+    virtual             ~MtpProperty();
+
+    void                read(MtpDataPacket& packet);
+
+    void                print();
+
+private:
+    void                readValue(MtpDataPacket& packet, MtpPropertyValue& value);
+    MtpPropertyValue*   readArrayValues(MtpDataPacket& packet, int& length);
+};
+
+}; // namespace android
+
+#endif // _MTP_PROPERTY_H
diff --git a/media/mtp/MtpStorageInfo.cpp b/media/mtp/MtpStorageInfo.cpp
index 7116e2b..5a5306b 100644
--- a/media/mtp/MtpStorageInfo.cpp
+++ b/media/mtp/MtpStorageInfo.cpp
@@ -14,7 +14,8 @@
  * limitations under the License.
  */
 
-#include <stdlib.h>
+#define LOG_TAG "MtpStorageInfo"
+#include "utils/Log.h"
 
 #include "MtpDataPacket.h"
 #include "MtpStorageInfo.h"
@@ -60,11 +61,11 @@
 }
 
 void MtpStorageInfo::print() {
-    printf("Storage Info %08X:\n\tmStorageType: %d\n\tmFileSystemType: %d\n\tmAccessCapability: %d\n",
+    LOGD("Storage Info %08X:\n\tmStorageType: %d\n\tmFileSystemType: %d\n\tmAccessCapability: %d\n",
             mStorageID, mStorageType, mFileSystemType, mAccessCapability);
-    printf("\tmMaxCapacity: %lld\n\tmFreeSpaceBytes: %lld\n\tmFreeSpaceObjects: %d\n",
+    LOGD("\tmMaxCapacity: %lld\n\tmFreeSpaceBytes: %lld\n\tmFreeSpaceObjects: %d\n",
             mMaxCapacity, mFreeSpaceBytes, mFreeSpaceObjects);
-    printf("\tmStorageDescription: %s\n\tmVolumeIdentifier: %s\n",
+    LOGD("\tmStorageDescription: %s\n\tmVolumeIdentifier: %s\n",
             mStorageDescription, mVolumeIdentifier);
 }
 
diff --git a/media/mtp/MtpTypes.h b/media/mtp/MtpTypes.h
index e3389c0..6a33a2b 100644
--- a/media/mtp/MtpTypes.h
+++ b/media/mtp/MtpTypes.h
@@ -23,20 +23,39 @@
 
 namespace android {
 
+typedef int32_t int128_t[4];
+typedef uint32_t uint128_t[4];
+
 typedef uint16_t MtpOperationCode;
 typedef uint16_t MtpResponseCode;
 typedef uint32_t MtpSessionID;
 typedef uint32_t MtpStorageID;
 typedef uint32_t MtpTransactionID;
-typedef uint16_t MtpDeviceProperty;
+typedef uint16_t MtpPropertyCode;
+typedef uint16_t MtpDataType;
 typedef uint16_t MtpObjectFormat;
-typedef uint16_t MtpObjectProperty;
+typedef MtpPropertyCode MtpDeviceProperty;
+typedef MtpPropertyCode MtpObjectProperty;
 
 // object handles are unique across all storage but only within a single session.
 // object handles cannot be reused after an object is deleted.
 // values 0x00000000 and 0xFFFFFFFF are reserved for special purposes.
 typedef uint32_t MtpObjectHandle;
 
+typedef union MtpPropertyValue {
+    int8_t          i8;
+    uint8_t         u8;
+    int16_t         i16;
+    uint16_t        u16;
+    int32_t         i32;
+    uint32_t        u32;
+    int64_t         i64;
+    uint64_t        u64;
+    int128_t        i128;
+    uint128_t       u128;
+    char*           str;
+};
+
 // Special values
 #define MTP_PARENT_ROOT         0xFFFFFFFF       // parent is root of the storage
 #define kInvalidObjectHandle    0xFFFFFFFF
@@ -53,16 +72,18 @@
 
 class MtpStorage;
 class MtpDevice;
+class MtpProperty;
 
 typedef Vector<MtpStorage *> MtpStorageList;
 typedef Vector<MtpDevice*> MtpDeviceList;
+typedef Vector<MtpProperty*> MtpPropertyList;
 
 typedef Vector<uint8_t> UInt8List;
-typedef Vector<uint32_t> UInt16List;
+typedef Vector<uint16_t> UInt16List;
 typedef Vector<uint32_t> UInt32List;
 typedef Vector<uint64_t> UInt64List;
 typedef Vector<int8_t> Int8List;
-typedef Vector<int32_t> Int16List;
+typedef Vector<int16_t> Int16List;
 typedef Vector<int32_t> Int32List;
 typedef Vector<int64_t> Int64List;