am d3e4290c
: DO NOT MERGE MTP and media provider support for multiple storage devices:
* commit 'd3e4290c0442b6dcf24bcf642f4fc26d12d8e7aa': DO NOT MERGE MTP and media provider support for multiple storage devices:
This commit is contained in:
@ -343,6 +343,13 @@ public final class MediaStore {
|
||||
* Table also contains MediaColumns._ID, DATA, SIZE and DATE_MODIFIED.
|
||||
*/
|
||||
public interface FileColumns extends MediaColumns {
|
||||
/**
|
||||
* The MTP storage ID of the file
|
||||
* <P>Type: INTEGER</P>
|
||||
* @hide
|
||||
*/
|
||||
public static final String STORAGE_ID = "storage_id";
|
||||
|
||||
/**
|
||||
* The MTP format code of the file
|
||||
* <P>Type: INTEGER</P>
|
||||
|
@ -114,6 +114,23 @@
|
||||
removable. -->
|
||||
<bool name="config_externalStorageRemovable" product="default">true</bool>
|
||||
|
||||
<!-- List of mount points for external storage devices.
|
||||
The first item on the list should be the primary external storage and should match the
|
||||
value returned by Environment.getExternalStorageDirectory (/mnt/sdcard).
|
||||
MTP storage IDs will be generated based on the position of the mountpoint in this list:
|
||||
0x00010001 - ID for primary external storage (/mnt/sdcard)
|
||||
0x00020001 - ID for first secondary external storage
|
||||
0x00030001 - ID for second secondary external storage
|
||||
etc. -->
|
||||
<string-array translatable="false" name="config_externalStoragePaths">
|
||||
<item>"/mnt/sdcard"</item>
|
||||
</string-array>
|
||||
|
||||
<!-- User visible descriptions of the volumes in the config_externalStoragePaths array. -->
|
||||
<string-array translatable="true" name="config_externalStorageDescriptions">
|
||||
<item>"SD card"</item>
|
||||
</string-array>
|
||||
|
||||
<!-- Number of megabytes of space to leave unallocated by MTP.
|
||||
MTP will subtract this value from the free space it reports back
|
||||
to the host via GetStorageInfo, and will not allow new files to
|
||||
|
@ -50,7 +50,8 @@ public class MtpDatabase {
|
||||
private final IContentProvider mMediaProvider;
|
||||
private final String mVolumeName;
|
||||
private final Uri mObjectsUri;
|
||||
private final String mMediaStoragePath;
|
||||
private final String mMediaStoragePath; // path to primary storage
|
||||
private final HashMap<String, MtpStorage> mStorageMap = new HashMap<String, MtpStorage>();
|
||||
|
||||
// cached property groups for single properties
|
||||
private final HashMap<Integer, MtpPropertyGroup> mPropertyGroupsByProperty
|
||||
@ -67,9 +68,6 @@ public class MtpDatabase {
|
||||
private SharedPreferences mDeviceProperties;
|
||||
private static final int DEVICE_PROPERTIES_DATABASE_VERSION = 1;
|
||||
|
||||
// FIXME - this should be passed in via the constructor
|
||||
private final int mStorageID = 0x00010001;
|
||||
|
||||
private static final String[] ID_PROJECTION = new String[] {
|
||||
Files.FileColumns._ID, // 0
|
||||
};
|
||||
@ -85,17 +83,22 @@ public class MtpDatabase {
|
||||
};
|
||||
private static final String[] OBJECT_INFO_PROJECTION = new String[] {
|
||||
Files.FileColumns._ID, // 0
|
||||
Files.FileColumns.DATA, // 1
|
||||
Files.FileColumns.STORAGE_ID, // 1
|
||||
Files.FileColumns.FORMAT, // 2
|
||||
Files.FileColumns.PARENT, // 3
|
||||
Files.FileColumns.SIZE, // 4
|
||||
Files.FileColumns.DATE_MODIFIED, // 5
|
||||
Files.FileColumns.DATA, // 4
|
||||
Files.FileColumns.SIZE, // 5
|
||||
Files.FileColumns.DATE_MODIFIED, // 6
|
||||
};
|
||||
private static final String ID_WHERE = Files.FileColumns._ID + "=?";
|
||||
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
|
||||
private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
|
||||
private static final String PARENT_FORMAT_WHERE = PARENT_WHERE + " AND "
|
||||
+ Files.FileColumns.FORMAT + "=?";
|
||||
private static final String PARENT_STORAGE_WHERE = PARENT_WHERE + " AND "
|
||||
+ Files.FileColumns.STORAGE_ID + "=?";
|
||||
private static final String PARENT_STORAGE_FORMAT_WHERE = PARENT_STORAGE_WHERE + " AND "
|
||||
+ Files.FileColumns.FORMAT + "=?";
|
||||
|
||||
private final MediaScanner mMediaScanner;
|
||||
|
||||
@ -124,6 +127,14 @@ public class MtpDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
public void addStorage(MtpStorage storage) {
|
||||
mStorageMap.put(storage.getPath(), storage);
|
||||
}
|
||||
|
||||
public void removeStorage(MtpStorage storage) {
|
||||
mStorageMap.remove(storage.getPath());
|
||||
}
|
||||
|
||||
private void initDeviceProperties(Context context) {
|
||||
final String devicePropertiesName = "device-properties";
|
||||
mDeviceProperties = context.getSharedPreferences(devicePropertiesName, Context.MODE_PRIVATE);
|
||||
@ -160,7 +171,7 @@ public class MtpDatabase {
|
||||
}
|
||||
|
||||
private int beginSendObject(String path, int format, int parent,
|
||||
int storage, long size, long modified) {
|
||||
int storageId, long size, long modified) {
|
||||
// first make sure the object does not exist
|
||||
if (path != null) {
|
||||
Cursor c = null;
|
||||
@ -185,7 +196,7 @@ public class MtpDatabase {
|
||||
values.put(Files.FileColumns.DATA, path);
|
||||
values.put(Files.FileColumns.FORMAT, format);
|
||||
values.put(Files.FileColumns.PARENT, parent);
|
||||
// storage is ignored for now
|
||||
values.put(Files.FileColumns.STORAGE_ID, storageId);
|
||||
values.put(Files.FileColumns.SIZE, size);
|
||||
values.put(Files.FileColumns.DATE_MODIFIED, modified);
|
||||
|
||||
@ -237,19 +248,35 @@ public class MtpDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
private int[] getObjectList(int storageID, int format, int parent) {
|
||||
// we can ignore storageID until we support multiple storages
|
||||
Cursor c = null;
|
||||
try {
|
||||
private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {
|
||||
if (storageID != 0) {
|
||||
if (format != 0) {
|
||||
c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
|
||||
return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
|
||||
PARENT_STORAGE_FORMAT_WHERE,
|
||||
new String[] { Integer.toString(parent), Integer.toString(storageID),
|
||||
Integer.toString(format) }, null);
|
||||
} else {
|
||||
return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
|
||||
PARENT_STORAGE_WHERE, new String[]
|
||||
{ Integer.toString(parent), Integer.toString(storageID) }, null);
|
||||
}
|
||||
} else {
|
||||
if (format != 0) {
|
||||
return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
|
||||
PARENT_FORMAT_WHERE,
|
||||
new String[] { Integer.toString(parent), Integer.toString(format) },
|
||||
null);
|
||||
} else {
|
||||
c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
|
||||
return mMediaProvider.query(mObjectsUri, ID_PROJECTION,
|
||||
PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int[] getObjectList(int storageID, int format, int parent) {
|
||||
Cursor c = null;
|
||||
try {
|
||||
c = createObjectQuery(storageID, format, parent);
|
||||
if (c == null) {
|
||||
return null;
|
||||
}
|
||||
@ -273,18 +300,9 @@ public class MtpDatabase {
|
||||
}
|
||||
|
||||
private int getNumObjects(int storageID, int format, int parent) {
|
||||
// we can ignore storageID until we support multiple storages
|
||||
Cursor c = null;
|
||||
try {
|
||||
if (format != 0) {
|
||||
c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
|
||||
PARENT_FORMAT_WHERE,
|
||||
new String[] { Integer.toString(parent), Integer.toString(format) },
|
||||
null);
|
||||
} else {
|
||||
c = mMediaProvider.query(mObjectsUri, ID_PROJECTION,
|
||||
PARENT_WHERE, new String[] { Integer.toString(parent) }, null);
|
||||
}
|
||||
c = createObjectQuery(storageID, format, parent);
|
||||
if (c != null) {
|
||||
return c.getCount();
|
||||
}
|
||||
@ -508,7 +526,7 @@ public class MtpDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
return propertyGroup.getPropertyList((int)handle, format, depth, mStorageID);
|
||||
return propertyGroup.getPropertyList((int)handle, format, depth);
|
||||
}
|
||||
|
||||
private int renameFile(int handle, String newName) {
|
||||
@ -631,12 +649,12 @@ public class MtpDatabase {
|
||||
c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
|
||||
ID_WHERE, new String[] { Integer.toString(handle) }, null);
|
||||
if (c != null && c.moveToNext()) {
|
||||
outStorageFormatParent[0] = mStorageID;
|
||||
outStorageFormatParent[0] = c.getInt(1);
|
||||
outStorageFormatParent[1] = c.getInt(2);
|
||||
outStorageFormatParent[2] = c.getInt(3);
|
||||
|
||||
// extract name from path
|
||||
String path = c.getString(1);
|
||||
String path = c.getString(4);
|
||||
int lastSlash = path.lastIndexOf('/');
|
||||
int start = (lastSlash >= 0 ? lastSlash + 1 : 0);
|
||||
int end = path.length();
|
||||
@ -646,8 +664,8 @@ public class MtpDatabase {
|
||||
path.getChars(start, end, outName, 0);
|
||||
outName[end - start] = 0;
|
||||
|
||||
outSizeModified[0] = c.getLong(4);
|
||||
outSizeModified[1] = c.getLong(5);
|
||||
outSizeModified[0] = c.getLong(5);
|
||||
outSizeModified[1] = c.getLong(6);
|
||||
return true;
|
||||
}
|
||||
} catch (RemoteException e) {
|
||||
|
@ -93,7 +93,7 @@ class MtpPropertyGroup {
|
||||
|
||||
switch (code) {
|
||||
case MtpConstants.PROPERTY_STORAGE_ID:
|
||||
// no query needed until we support multiple storage units
|
||||
column = Files.FileColumns.STORAGE_ID;
|
||||
type = MtpConstants.TYPE_UINT32;
|
||||
break;
|
||||
case MtpConstants.PROPERTY_OBJECT_FORMAT:
|
||||
@ -134,6 +134,7 @@ class MtpPropertyGroup {
|
||||
break;
|
||||
case MtpConstants.PROPERTY_PERSISTENT_UID:
|
||||
// PUID is concatenation of storageID and object handle
|
||||
column = Files.FileColumns.STORAGE_ID;
|
||||
type = MtpConstants.TYPE_UINT128;
|
||||
break;
|
||||
case MtpConstants.PROPERTY_DURATION:
|
||||
@ -280,7 +281,7 @@ class MtpPropertyGroup {
|
||||
return path.substring(start, end);
|
||||
}
|
||||
|
||||
MtpPropertyList getPropertyList(int handle, int format, int depth, int storageID) {
|
||||
MtpPropertyList getPropertyList(int handle, int format, int depth) {
|
||||
//Log.d(TAG, "getPropertyList handle: " + handle + " format: " + format + " depth: " + depth);
|
||||
if (depth > 1) {
|
||||
// we only support depth 0 and 1
|
||||
@ -348,10 +349,6 @@ class MtpPropertyGroup {
|
||||
|
||||
// handle some special cases
|
||||
switch (propertyCode) {
|
||||
case MtpConstants.PROPERTY_STORAGE_ID:
|
||||
result.append(handle, propertyCode, MtpConstants.TYPE_UINT32,
|
||||
storageID);
|
||||
break;
|
||||
case MtpConstants.PROPERTY_PROTECTION_STATUS:
|
||||
// protection status is always 0
|
||||
result.append(handle, propertyCode, MtpConstants.TYPE_UINT16, 0);
|
||||
@ -398,7 +395,7 @@ class MtpPropertyGroup {
|
||||
break;
|
||||
case MtpConstants.PROPERTY_PERSISTENT_UID:
|
||||
// PUID is concatenation of storageID and object handle
|
||||
long puid = storageID;
|
||||
long puid = c.getLong(column);
|
||||
puid <<= 32;
|
||||
puid += handle;
|
||||
result.append(handle, propertyCode, MtpConstants.TYPE_UINT128, puid);
|
||||
|
@ -33,8 +33,8 @@ public class MtpServer {
|
||||
System.loadLibrary("media_jni");
|
||||
}
|
||||
|
||||
public MtpServer(MtpDatabase database, String storagePath, long reserveSpace) {
|
||||
native_setup(database, storagePath, reserveSpace);
|
||||
public MtpServer(MtpDatabase database) {
|
||||
native_setup(database);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
@ -65,18 +65,20 @@ public class MtpServer {
|
||||
native_set_ptp_mode(usePtp);
|
||||
}
|
||||
|
||||
// Used to disable MTP by removing all storage units.
|
||||
// This is done to disable access to file transfer when the device is locked.
|
||||
public void setLocked(boolean locked) {
|
||||
native_set_locked(locked);
|
||||
public void addStorage(MtpStorage storage) {
|
||||
native_add_storage(storage);
|
||||
}
|
||||
|
||||
private native final void native_setup(MtpDatabase database, String storagePath,
|
||||
long reserveSpace);
|
||||
public void removeStorage(MtpStorage storage) {
|
||||
native_remove_storage(storage.getStorageId());
|
||||
}
|
||||
|
||||
private native final void native_setup(MtpDatabase database);
|
||||
private native final void native_start();
|
||||
private native final void native_stop();
|
||||
private native final void native_send_object_added(int handle);
|
||||
private native final void native_send_object_removed(int handle);
|
||||
private native final void native_set_ptp_mode(boolean usePtp);
|
||||
private native final void native_set_locked(boolean locked);
|
||||
private native final void native_add_storage(MtpStorage storage);
|
||||
private native final void native_remove_storage(int storageId);
|
||||
}
|
||||
|
89
media/java/android/mtp/MtpStorage.java
Normal file
89
media/java/android/mtp/MtpStorage.java
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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.
|
||||
*/
|
||||
|
||||
package android.mtp;
|
||||
|
||||
/**
|
||||
* This class represents a storage unit on an MTP device.
|
||||
* Used only for MTP support in USB responder mode.
|
||||
* MtpStorageInfo is used in MTP host mode
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class MtpStorage {
|
||||
|
||||
private final int mStorageId;
|
||||
private final String mPath;
|
||||
private final String mDescription;
|
||||
private final long mReserveSpace;
|
||||
|
||||
public MtpStorage(int id, String path, String description, long reserveSpace) {
|
||||
mStorageId = id;
|
||||
mPath = path;
|
||||
mDescription = description;
|
||||
mReserveSpace = reserveSpace;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the storage ID for the storage unit
|
||||
*
|
||||
* @return the storage ID
|
||||
*/
|
||||
public final int getStorageId() {
|
||||
return mStorageId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a storage ID for storage of given index.
|
||||
* Index 0 is for primary external storage
|
||||
*
|
||||
* @return the storage ID
|
||||
*/
|
||||
public static int getStorageId(int index) {
|
||||
// storage ID is 0x00010001 for primary storage,
|
||||
// then 0x00020001, 0x00030001, etc. for secondary storages
|
||||
return ((index + 1) << 16) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the file path for the storage unit's storage in the file system
|
||||
*
|
||||
* @return the storage file path
|
||||
*/
|
||||
public final String getPath() {
|
||||
return mPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the description string for the storage unit
|
||||
*
|
||||
* @return the storage unit description
|
||||
*/
|
||||
public final String getDescription() {
|
||||
return mDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of space to reserve on the storage file system.
|
||||
* This can be set to a non-zero value to prevent MTP from filling up the entire storage.
|
||||
*
|
||||
* @return the storage unit description
|
||||
*/
|
||||
public final long getReserveSpace() {
|
||||
return mReserveSpace;
|
||||
}
|
||||
|
||||
}
|
@ -39,6 +39,17 @@
|
||||
|
||||
using namespace android;
|
||||
|
||||
// MtpStorage class
|
||||
jclass clazz_MtpStorage;
|
||||
|
||||
// MtpStorage fields
|
||||
static jfieldID field_MtpStorage_storageId;
|
||||
static jfieldID field_MtpStorage_path;
|
||||
static jfieldID field_MtpStorage_description;
|
||||
static jfieldID field_MtpStorage_reserveSpace;
|
||||
|
||||
static Mutex sMutex;
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
// in android_mtp_MtpDatabase.cpp
|
||||
@ -57,70 +68,77 @@ class MtpThread : public Thread {
|
||||
private:
|
||||
MtpDatabase* mDatabase;
|
||||
MtpServer* mServer;
|
||||
MtpStorage* mStorage;
|
||||
Mutex mMutex;
|
||||
MtpStorageList mStorageList;
|
||||
bool mUsePtp;
|
||||
bool mLocked;
|
||||
int mFd;
|
||||
|
||||
public:
|
||||
MtpThread(MtpDatabase* database, MtpStorage* storage)
|
||||
MtpThread(MtpDatabase* database)
|
||||
: mDatabase(database),
|
||||
mServer(NULL),
|
||||
mStorage(storage),
|
||||
mUsePtp(false),
|
||||
mLocked(false),
|
||||
mFd(-1)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~MtpThread() {
|
||||
delete mStorage;
|
||||
}
|
||||
|
||||
void setPtpMode(bool usePtp) {
|
||||
mMutex.lock();
|
||||
mUsePtp = usePtp;
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
void setLocked(bool locked) {
|
||||
mMutex.lock();
|
||||
if (locked != mLocked) {
|
||||
if (mServer) {
|
||||
if (locked)
|
||||
mServer->removeStorage(mStorage);
|
||||
else
|
||||
mServer->addStorage(mStorage);
|
||||
void addStorage(MtpStorage *storage) {
|
||||
mStorageList.push(storage);
|
||||
if (mServer)
|
||||
mServer->addStorage(storage);
|
||||
}
|
||||
|
||||
void removeStorage(MtpStorageID id) {
|
||||
MtpStorage* storage = mServer->getStorage(id);
|
||||
if (storage) {
|
||||
for (int i = 0; i < mStorageList.size(); i++) {
|
||||
if (mStorageList[i] == storage) {
|
||||
mStorageList.removeAt(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
mLocked = locked;
|
||||
if (mServer)
|
||||
mServer->removeStorage(storage);
|
||||
delete storage;
|
||||
}
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
void start() {
|
||||
run("MtpThread");
|
||||
}
|
||||
|
||||
virtual bool threadLoop() {
|
||||
mMutex.lock();
|
||||
sMutex.lock();
|
||||
|
||||
mFd = open("/dev/mtp_usb", O_RDWR);
|
||||
if (mFd >= 0) {
|
||||
ioctl(mFd, MTP_SET_INTERFACE_MODE,
|
||||
(mUsePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP));
|
||||
|
||||
mServer = new MtpServer(mFd, mDatabase, AID_MEDIA_RW, 0664, 0775);
|
||||
if (!mLocked)
|
||||
mServer->addStorage(mStorage);
|
||||
|
||||
mMutex.unlock();
|
||||
mServer->run();
|
||||
mMutex.lock();
|
||||
|
||||
close(mFd);
|
||||
mFd = -1;
|
||||
delete mServer;
|
||||
mServer = NULL;
|
||||
for (int i = 0; i < mStorageList.size(); i++) {
|
||||
mServer->addStorage(mStorageList[i]);
|
||||
}
|
||||
} else {
|
||||
LOGE("could not open MTP driver, errno: %d", errno);
|
||||
}
|
||||
mMutex.unlock();
|
||||
|
||||
sMutex.unlock();
|
||||
mServer->run();
|
||||
sMutex.lock();
|
||||
|
||||
close(mFd);
|
||||
mFd = -1;
|
||||
delete mServer;
|
||||
mServer = NULL;
|
||||
|
||||
sMutex.unlock();
|
||||
// delay a bit before retrying to avoid excessive spin
|
||||
if (!exitPending()) {
|
||||
sleep(1);
|
||||
@ -130,17 +148,13 @@ public:
|
||||
}
|
||||
|
||||
void sendObjectAdded(MtpObjectHandle handle) {
|
||||
mMutex.lock();
|
||||
if (mServer)
|
||||
mServer->sendObjectAdded(handle);
|
||||
mMutex.unlock();
|
||||
}
|
||||
|
||||
void sendObjectRemoved(MtpObjectHandle handle) {
|
||||
mMutex.lock();
|
||||
if (mServer)
|
||||
mServer->sendObjectRemoved(handle);
|
||||
mMutex.unlock();
|
||||
}
|
||||
};
|
||||
|
||||
@ -150,18 +164,11 @@ static sp<MtpThread> sThread;
|
||||
#endif // HAVE_ANDROID_OS
|
||||
|
||||
static void
|
||||
android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase,
|
||||
jstring storagePath, jlong reserveSpace)
|
||||
android_mtp_MtpServer_setup(JNIEnv *env, jobject thiz, jobject javaDatabase)
|
||||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
MtpDatabase* database = getMtpDatabase(env, javaDatabase);
|
||||
const char *storagePathStr = env->GetStringUTFChars(storagePath, NULL);
|
||||
|
||||
// create the thread and assign it to the smart pointer
|
||||
MtpStorage* storage = new MtpStorage(MTP_FIRST_STORAGE_ID, storagePathStr, reserveSpace);
|
||||
sThread = new MtpThread(database, storage);
|
||||
|
||||
env->ReleaseStringUTFChars(storagePath, storagePathStr);
|
||||
sThread = new MtpThread(getMtpDatabase(env, javaDatabase));
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -169,9 +176,11 @@ static void
|
||||
android_mtp_MtpServer_start(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sMutex.lock();
|
||||
MtpThread *thread = sThread.get();
|
||||
if (thread)
|
||||
thread->run("MtpThread");
|
||||
thread->start();
|
||||
sMutex.unlock();
|
||||
#endif // HAVE_ANDROID_OS
|
||||
}
|
||||
|
||||
@ -179,11 +188,13 @@ static void
|
||||
android_mtp_MtpServer_stop(JNIEnv *env, jobject thiz)
|
||||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sMutex.lock();
|
||||
MtpThread *thread = sThread.get();
|
||||
if (thread) {
|
||||
thread->requestExitAndWait();
|
||||
sThread = NULL;
|
||||
}
|
||||
sMutex.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -191,9 +202,11 @@ static void
|
||||
android_mtp_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
|
||||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sMutex.lock();
|
||||
MtpThread *thread = sThread.get();
|
||||
if (thread)
|
||||
thread->sendObjectAdded(handle);
|
||||
sMutex.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -201,9 +214,11 @@ static void
|
||||
android_mtp_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
|
||||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sMutex.lock();
|
||||
MtpThread *thread = sThread.get();
|
||||
if (thread)
|
||||
thread->sendObjectRemoved(handle);
|
||||
sMutex.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -211,33 +226,68 @@ static void
|
||||
android_mtp_MtpServer_set_ptp_mode(JNIEnv *env, jobject thiz, jboolean usePtp)
|
||||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sMutex.lock();
|
||||
MtpThread *thread = sThread.get();
|
||||
if (thread)
|
||||
thread->setPtpMode(usePtp);
|
||||
sMutex.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
android_mtp_MtpServer_set_locked(JNIEnv *env, jobject thiz, jboolean locked)
|
||||
android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
|
||||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sMutex.lock();
|
||||
MtpThread *thread = sThread.get();
|
||||
if (thread) {
|
||||
jint storageID = env->GetIntField(jstorage, field_MtpStorage_storageId);
|
||||
jstring path = (jstring)env->GetObjectField(jstorage, field_MtpStorage_path);
|
||||
jstring description = (jstring)env->GetObjectField(jstorage, field_MtpStorage_description);
|
||||
jlong reserveSpace = env->GetLongField(jstorage, field_MtpStorage_reserveSpace);
|
||||
|
||||
const char *pathStr = env->GetStringUTFChars(path, NULL);
|
||||
const char *descriptionStr = env->GetStringUTFChars(description, NULL);
|
||||
|
||||
MtpStorage* storage = new MtpStorage(storageID, pathStr, descriptionStr, reserveSpace);
|
||||
thread->addStorage(storage);
|
||||
|
||||
env->ReleaseStringUTFChars(path, pathStr);
|
||||
env->ReleaseStringUTFChars(description, descriptionStr);
|
||||
} else {
|
||||
LOGE("MtpThread is null in add_storage");
|
||||
}
|
||||
sMutex.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
android_mtp_MtpServer_remove_storage(JNIEnv *env, jobject thiz, jint storageId)
|
||||
{
|
||||
#ifdef HAVE_ANDROID_OS
|
||||
sMutex.lock();
|
||||
MtpThread *thread = sThread.get();
|
||||
if (thread)
|
||||
thread->setLocked(locked);
|
||||
thread->removeStorage(storageId);
|
||||
else
|
||||
LOGE("MtpThread is null in remove_storage");
|
||||
sMutex.unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
static JNINativeMethod gMethods[] = {
|
||||
{"native_setup", "(Landroid/mtp/MtpDatabase;Ljava/lang/String;J)V",
|
||||
{"native_setup", "(Landroid/mtp/MtpDatabase;)V",
|
||||
(void *)android_mtp_MtpServer_setup},
|
||||
{"native_start", "()V", (void *)android_mtp_MtpServer_start},
|
||||
{"native_stop", "()V", (void *)android_mtp_MtpServer_stop},
|
||||
{"native_send_object_added", "(I)V", (void *)android_mtp_MtpServer_send_object_added},
|
||||
{"native_send_object_removed", "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
|
||||
{"native_set_ptp_mode", "(Z)V", (void *)android_mtp_MtpServer_set_ptp_mode},
|
||||
{"native_set_locked", "(Z)V", (void *)android_mtp_MtpServer_set_locked},
|
||||
{"native_add_storage", "(Landroid/mtp/MtpStorage;)V",
|
||||
(void *)android_mtp_MtpServer_add_storage},
|
||||
{"native_remove_storage", "(I)V", (void *)android_mtp_MtpServer_remove_storage},
|
||||
};
|
||||
|
||||
static const char* const kClassPathName = "android/mtp/MtpServer";
|
||||
@ -246,6 +296,33 @@ int register_android_mtp_MtpServer(JNIEnv *env)
|
||||
{
|
||||
jclass clazz;
|
||||
|
||||
clazz = env->FindClass("android/mtp/MtpStorage");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find android/mtp/MtpStorage");
|
||||
return -1;
|
||||
}
|
||||
field_MtpStorage_storageId = env->GetFieldID(clazz, "mStorageId", "I");
|
||||
if (field_MtpStorage_storageId == NULL) {
|
||||
LOGE("Can't find MtpStorage.mStorageId");
|
||||
return -1;
|
||||
}
|
||||
field_MtpStorage_path = env->GetFieldID(clazz, "mPath", "Ljava/lang/String;");
|
||||
if (field_MtpStorage_path == NULL) {
|
||||
LOGE("Can't find MtpStorage.mPath");
|
||||
return -1;
|
||||
}
|
||||
field_MtpStorage_description = env->GetFieldID(clazz, "mDescription", "Ljava/lang/String;");
|
||||
if (field_MtpStorage_description == NULL) {
|
||||
LOGE("Can't find MtpStorage.mDescription");
|
||||
return -1;
|
||||
}
|
||||
field_MtpStorage_reserveSpace = env->GetFieldID(clazz, "mReserveSpace", "J");
|
||||
if (field_MtpStorage_reserveSpace == NULL) {
|
||||
LOGE("Can't find MtpStorage.mStorageId");
|
||||
return -1;
|
||||
}
|
||||
clazz_MtpStorage = (jclass)env->NewGlobalRef(clazz);
|
||||
|
||||
clazz = env->FindClass("android/mtp/MtpServer");
|
||||
if (clazz == NULL) {
|
||||
LOGE("Can't find android/mtp/MtpServer");
|
||||
|
@ -70,6 +70,9 @@ public:
|
||||
int fileGroup, int filePerm, int directoryPerm);
|
||||
virtual ~MtpServer();
|
||||
|
||||
MtpStorage* getStorage(MtpStorageID id);
|
||||
inline bool hasStorage() { return mStorages.size() > 0; }
|
||||
bool hasStorage(MtpStorageID id);
|
||||
void addStorage(MtpStorage* storage);
|
||||
void removeStorage(MtpStorage* storage);
|
||||
|
||||
@ -79,9 +82,6 @@ public:
|
||||
void sendObjectRemoved(MtpObjectHandle handle);
|
||||
|
||||
private:
|
||||
MtpStorage* getStorage(MtpStorageID id);
|
||||
inline bool hasStorage() { return mStorages.size() > 0; }
|
||||
bool hasStorage(MtpStorageID id);
|
||||
void sendStoreAdded(MtpStorageID id);
|
||||
void sendStoreRemoved(MtpStorageID id);
|
||||
void sendEvent(MtpEventCode code, uint32_t param1);
|
||||
|
@ -32,9 +32,11 @@
|
||||
|
||||
namespace android {
|
||||
|
||||
MtpStorage::MtpStorage(MtpStorageID id, const char* filePath, uint64_t reserveSpace)
|
||||
MtpStorage::MtpStorage(MtpStorageID id, const char* filePath,
|
||||
const char* description, uint64_t reserveSpace)
|
||||
: mStorageID(id),
|
||||
mFilePath(filePath),
|
||||
mDescription(description),
|
||||
mMaxCapacity(0),
|
||||
mReserveSpace(reserveSpace)
|
||||
{
|
||||
@ -75,7 +77,7 @@ uint64_t MtpStorage::getFreeSpace() {
|
||||
}
|
||||
|
||||
const char* MtpStorage::getDescription() const {
|
||||
return "Device Storage";
|
||||
return (const char *)mDescription;
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
|
@ -29,13 +29,14 @@ class MtpStorage {
|
||||
private:
|
||||
MtpStorageID mStorageID;
|
||||
MtpString mFilePath;
|
||||
MtpString mDescription;
|
||||
uint64_t mMaxCapacity;
|
||||
// amount of free space to leave unallocated
|
||||
uint64_t mReserveSpace;
|
||||
|
||||
public:
|
||||
MtpStorage(MtpStorageID id, const char* filePath,
|
||||
uint64_t reserveSpace);
|
||||
const char* description, uint64_t reserveSpace);
|
||||
virtual ~MtpStorage();
|
||||
|
||||
inline MtpStorageID getStorageID() const { return mStorageID; }
|
||||
|
@ -22,8 +22,6 @@
|
||||
|
||||
#define MTP_STANDARD_VERSION 100
|
||||
|
||||
#define MTP_FIRST_STORAGE_ID 0x00010001
|
||||
|
||||
// Container Types
|
||||
#define MTP_CONTAINER_TYPE_UNDEFINED 0
|
||||
#define MTP_CONTAINER_TYPE_COMMAND 1
|
||||
|
Reference in New Issue
Block a user