Merge "MTP: Implement support for getting/setting device properties"

This commit is contained in:
Mike Lockwood
2010-09-02 12:03:25 -07:00
committed by Android (Google) Code Review
6 changed files with 332 additions and 24 deletions

View File

@@ -21,6 +21,7 @@ import android.content.ContentValues;
import android.content.IContentProvider;
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.RemoteException;
import android.provider.MediaStore.Audio;
@@ -44,6 +45,10 @@ public class MtpDatabase {
// true if the database has been modified in the current MTP session
private boolean mDatabaseModified;
// database for writable MTP device properties
private SQLiteDatabase mDevicePropDb;
private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;
// FIXME - this should be passed in via the constructor
private final int mStorageID = 0x00010001;
@@ -69,6 +74,9 @@ public class MtpDatabase {
private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
+ MtpObjects.ObjectColumns.FORMAT + "=?";
private static final String[] DEVICE_PROPERTY_PROJECTION = new String[] { "_id", "value" };
private static final String DEVICE_PROPERTY_WHERE = "code=?";
private final MediaScanner mMediaScanner;
static {
@@ -83,6 +91,7 @@ public class MtpDatabase {
mVolumeName = volumeName;
mObjectsUri = MtpObjects.getContentUri(volumeName);
mMediaScanner = new MediaScanner(context);
openDevicePropertiesDatabase(context);
}
@Override
@@ -94,6 +103,22 @@ public class MtpDatabase {
}
}
private void openDevicePropertiesDatabase(Context context) {
mDevicePropDb = context.openOrCreateDatabase("device-properties", Context.MODE_PRIVATE, null);
int version = mDevicePropDb.getVersion();
// initialize if necessary
if (version != DEVICE_PROPERTIES_DATABASE_VERSION) {
mDevicePropDb.execSQL("CREATE TABLE properties (" +
"_id INTEGER PRIMARY KEY AUTOINCREMENT," +
"code INTEGER UNIQUE ON CONFLICT REPLACE," +
"value TEXT" +
");");
mDevicePropDb.execSQL("CREATE INDEX property_index ON properties (code);");
mDevicePropDb.setVersion(DEVICE_PROPERTIES_DATABASE_VERSION);
}
}
private int beginSendObject(String path, int format, int parent,
int storage, long size, long modified) {
mDatabaseModified = true;
@@ -257,8 +282,10 @@ public class MtpDatabase {
}
private int[] getSupportedDeviceProperties() {
// no device properties yet
return null;
return new int[] {
MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
};
}
private int getObjectProperty(int handle, int property,
@@ -342,6 +369,68 @@ public class MtpDatabase {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}
private int setObjectProperty(int handle, int property,
long intValue, String stringValue) {
Log.d(TAG, "setObjectProperty: " + property);
return MtpConstants.RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
}
private int getDeviceProperty(int property, long[] outIntValue, char[] outStringValue) {
Log.d(TAG, "getDeviceProperty: " + property);
switch (property) {
case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
// writable string properties kept in our device property database
Cursor c = null;
try {
c = mDevicePropDb.query("properties", DEVICE_PROPERTY_PROJECTION,
DEVICE_PROPERTY_WHERE, new String[] { Integer.toString(property) },
null, null, null);
if (c != null && c.moveToNext()) {
String value = c.getString(1);
int length = value.length();
if (length > 255) {
length = 255;
}
value.getChars(0, length, outStringValue, 0);
outStringValue[length] = 0;
} else {
outStringValue[0] = 0;
}
return MtpConstants.RESPONSE_OK;
} finally {
if (c != null) {
c.close();
}
}
}
return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
}
private int setDeviceProperty(int property, long intValue, String stringValue) {
Log.d(TAG, "setDeviceProperty: " + property + " : " + stringValue);
switch (property) {
case MtpConstants.DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
case MtpConstants.DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
// writable string properties kept in our device property database
try {
ContentValues values = new ContentValues();
values.put("code", property);
values.put("value", stringValue);
mDevicePropDb.insert("properties", "code", values);
return MtpConstants.RESPONSE_OK;
} catch (Exception e) {
return MtpConstants.RESPONSE_GENERAL_ERROR;
}
}
return MtpConstants.RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
}
private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
char[] outName, long[] outSizeModified) {
Log.d(TAG, "getObjectInfo: " + handle);

View File

@@ -30,6 +30,7 @@
#include "MtpDatabase.h"
#include "MtpDataPacket.h"
#include "MtpProperty.h"
#include "MtpStringBuffer.h"
#include "MtpUtils.h"
#include "mtp.h"
@@ -47,6 +48,8 @@ static jmethodID method_getSupportedObjectProperties;
static jmethodID method_getSupportedDeviceProperties;
static jmethodID method_getObjectProperty;
static jmethodID method_setObjectProperty;
static jmethodID method_getDeviceProperty;
static jmethodID method_setDeviceProperty;
static jmethodID method_getObjectInfo;
static jmethodID method_getObjectFilePath;
static jmethodID method_deleteFile;
@@ -127,7 +130,8 @@ public:
int64_t& fileLength);
virtual MtpResponseCode deleteFile(MtpObjectHandle handle);
bool getPropertyInfo(MtpObjectProperty property, int& type);
bool getObjectPropertyInfo(MtpObjectProperty property, int& type);
bool getDevicePropertyInfo(MtpDeviceProperty property, int& type);
virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle handle);
@@ -323,14 +327,16 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
MtpDataPacket& packet) {
int type;
if (!getPropertyInfo(property, type))
return MTP_RESPONSE_INVALID_OBJECT_PROP_CODE;
if (!getObjectPropertyInfo(property, type))
return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
JNIEnv* env = AndroidRuntime::getJNIEnv();
jint result = env->CallIntMethod(mDatabase, method_getObjectProperty,
(jint)handle, (jint)property, mLongBuffer, mStringBuffer);
if (result != MTP_RESPONSE_OK)
if (result != MTP_RESPONSE_OK) {
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return result;
}
jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
jlong longValue = longValues[0];
@@ -384,8 +390,8 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
break;
}
default:
LOGE("unsupported object type\n");
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
LOGE("unsupported type in getObjectPropertyValue\n");
return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
}
checkAndClearExceptionFromCallback(env, __FUNCTION__);
@@ -395,17 +401,179 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
MtpObjectProperty property,
MtpDataPacket& packet) {
return -1;
int type;
if (!getObjectPropertyInfo(property, type))
return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
JNIEnv* env = AndroidRuntime::getJNIEnv();
jlong longValue = 0;
jstring stringValue = NULL;
switch (type) {
case MTP_TYPE_INT8:
longValue = packet.getInt8();
break;
case MTP_TYPE_UINT8:
longValue = packet.getUInt8();
break;
case MTP_TYPE_INT16:
longValue = packet.getInt16();
break;
case MTP_TYPE_UINT16:
longValue = packet.getUInt16();
break;
case MTP_TYPE_INT32:
longValue = packet.getInt32();
break;
case MTP_TYPE_UINT32:
longValue = packet.getUInt32();
break;
case MTP_TYPE_INT64:
longValue = packet.getInt64();
break;
case MTP_TYPE_UINT64:
longValue = packet.getUInt64();
break;
case MTP_TYPE_STR:
{
MtpStringBuffer buffer;
packet.getString(buffer);
stringValue = env->NewStringUTF((const char *)buffer);
break;
}
default:
LOGE("unsupported type in getObjectPropertyValue\n");
return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
}
jint result = env->CallIntMethod(mDatabase, method_setObjectProperty,
(jint)handle, (jint)property, longValue, stringValue);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return result;
}
MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
MtpDataPacket& packet) {
return -1;
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:
LOGE("unsupported type in getDevicePropertyValue\n");
return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
}
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return MTP_RESPONSE_OK;
}
MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property,
MtpDataPacket& packet) {
return -1;
int type;
if (!getDevicePropertyInfo(property, type))
return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
JNIEnv* env = AndroidRuntime::getJNIEnv();
jlong longValue = 0;
jstring stringValue = NULL;
switch (type) {
case MTP_TYPE_INT8:
longValue = packet.getInt8();
break;
case MTP_TYPE_UINT8:
longValue = packet.getUInt8();
break;
case MTP_TYPE_INT16:
longValue = packet.getInt16();
break;
case MTP_TYPE_UINT16:
longValue = packet.getUInt16();
break;
case MTP_TYPE_INT32:
longValue = packet.getInt32();
break;
case MTP_TYPE_UINT32:
longValue = packet.getUInt32();
break;
case MTP_TYPE_INT64:
longValue = packet.getInt64();
break;
case MTP_TYPE_UINT64:
longValue = packet.getUInt64();
break;
case MTP_TYPE_STR:
{
MtpStringBuffer buffer;
packet.getString(buffer);
stringValue = env->NewStringUTF((const char *)buffer);
break;
}
default:
LOGE("unsupported type in setDevicePropertyValue\n");
return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
}
jint result = env->CallIntMethod(mDatabase, method_setDeviceProperty,
(jint)property, longValue, stringValue);
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return result;
}
MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
@@ -473,8 +641,10 @@ MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
JNIEnv* env = AndroidRuntime::getJNIEnv();
jint result = env->CallIntMethod(mDatabase, method_getObjectFilePath,
(jint)handle, mStringBuffer, mLongBuffer);
if (result != MTP_RESPONSE_OK)
if (result != MTP_RESPONSE_OK) {
checkAndClearExceptionFromCallback(env, __FUNCTION__);
return result;
}
jchar* str = env->GetCharArrayElements(mStringBuffer, 0);
filePath.setTo(str, strlen16(str));
@@ -501,7 +671,7 @@ struct PropertyTableEntry {
int type;
};
static const PropertyTableEntry kPropertyTable[] = {
static const PropertyTableEntry kObjectPropertyTable[] = {
{ MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 },
{ MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 },
{ MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16 },
@@ -510,9 +680,26 @@ static const PropertyTableEntry kPropertyTable[] = {
{ MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR },
};
bool MyMtpDatabase::getPropertyInfo(MtpObjectProperty property, int& type) {
int count = sizeof(kPropertyTable) / sizeof(kPropertyTable[0]);
const PropertyTableEntry* entry = kPropertyTable;
static const PropertyTableEntry kDevicePropertyTable[] = {
{ MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER, MTP_TYPE_STR },
{ MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MTP_TYPE_STR },
};
bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
const PropertyTableEntry* entry = kObjectPropertyTable;
for (int i = 0; i < count; i++, entry++) {
if (entry->property == property) {
type = entry->type;
return true;
}
}
return false;
}
bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
const PropertyTableEntry* entry = kDevicePropertyTable;
for (int i = 0; i < count; i++, entry++) {
if (entry->property == property) {
type = entry->type;
@@ -587,7 +774,16 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
}
MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
return NULL;
MtpProperty* result = NULL;
switch (property) {
case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
// writeable string properties
result = new MtpProperty(property, MTP_TYPE_STR, true);
break;
}
return result;
}
void MyMtpDatabase::sessionStarted() {
@@ -695,6 +891,21 @@ int register_android_media_MtpDatabase(JNIEnv *env)
LOGE("Can't find getObjectProperty");
return -1;
}
method_setObjectProperty = env->GetMethodID(clazz, "setObjectProperty", "(IIJLjava/lang/String;)I");
if (method_setObjectProperty == NULL) {
LOGE("Can't find setObjectProperty");
return -1;
}
method_getDeviceProperty = env->GetMethodID(clazz, "getDeviceProperty", "(I[J[C)I");
if (method_getDeviceProperty == NULL) {
LOGE("Can't find getDeviceProperty");
return -1;
}
method_setDeviceProperty = env->GetMethodID(clazz, "setDeviceProperty", "(IJLjava/lang/String;)I");
if (method_setDeviceProperty == NULL) {
LOGE("Can't find setDeviceProperty");
return -1;
}
method_getObjectInfo = env->GetMethodID(clazz, "getObjectInfo", "(I[I[C[J)Z");
if (method_getObjectInfo == NULL) {
LOGE("Can't find getObjectInfo");

View File

@@ -342,7 +342,7 @@ MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
MtpResponseCode ret = readResponse();
if (ret == MTP_RESPONSE_OK) {
MtpProperty* property = new MtpProperty;
property->read(mData, true);
property->read(mData);
return property;
}
return NULL;

View File

@@ -120,7 +120,7 @@ MtpProperty::~MtpProperty() {
delete[] mEnumValues;
}
void MtpProperty::read(MtpDataPacket& packet, bool deviceProp) {
void MtpProperty::read(MtpDataPacket& packet) {
mCode = packet.getUInt16();
mType = packet.getUInt16();
@@ -141,7 +141,7 @@ void MtpProperty::read(MtpDataPacket& packet, bool deviceProp) {
break;
default:
readValue(packet, mDefaultValue);
if (deviceProp)
if (isDeviceProperty())
readValue(packet, mCurrentValue);
}
mGroupCode = packet.getUInt32();
@@ -159,7 +159,6 @@ void MtpProperty::read(MtpDataPacket& packet, bool deviceProp) {
}
}
// FIXME - only works for object properties
void MtpProperty::write(MtpDataPacket& packet) {
packet.putUInt16(mCode);
packet.putUInt16(mType);

View File

@@ -65,16 +65,22 @@ public:
inline MtpPropertyCode getPropertyCode() const { return mCode; }
void read(MtpDataPacket& packet, bool deviceProp);
void read(MtpDataPacket& packet);
void write(MtpDataPacket& packet);
void print();
inline bool isDeviceProperty() const {
return ( ((mCode & 0xF000) == 0x5000)
|| ((mCode & 0xF800) == 0xD000));
}
private:
void readValue(MtpDataPacket& packet, MtpPropertyValue& value);
void writeValue(MtpDataPacket& packet, MtpPropertyValue& value);
MtpPropertyValue* readArrayValues(MtpDataPacket& packet, int& length);
void writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, int length);
void writeArrayValues(MtpDataPacket& packet,
MtpPropertyValue* values, int length);
};
}; // namespace android

View File

@@ -55,7 +55,7 @@ static const MtpOperationCode kSupportedOperationCodes[] = {
// MTP_OPERATION_SELF_TEST,
// MTP_OPERATION_SET_OBJECT_PROTECTION,
// MTP_OPERATION_POWER_DOWN,
// MTP_OPERATION_GET_DEVICE_PROP_DESC,
MTP_OPERATION_GET_DEVICE_PROP_DESC,
MTP_OPERATION_GET_DEVICE_PROP_VALUE,
MTP_OPERATION_SET_DEVICE_PROP_VALUE,
MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
@@ -288,6 +288,9 @@ bool MtpServer::handleRequest() {
case MTP_OPERATION_GET_OBJECT_PROP_DESC:
response = doGetObjectPropDesc();
break;
case MTP_OPERATION_GET_DEVICE_PROP_DESC:
response = doGetDevicePropDesc();
break;
default:
response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
break;