[MediaProvider]MTP拷贝APK文件Observe
1、调用流程
发送ObjectInfo
MtpResponseCode MtpServer::doSendObjectInfo() //MtpServer.cpp
private int beginSendObject(String path, int format,...) //MtpDatabase.java
拷贝过程
存入Object表中
public Uri insert(Uri uri, ContentValues initialValues) //MediaProvider.java
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
int match = URI_MATCHER.match(uri);
ArrayList<Long> notifyRowIds = new ArrayList<Long>();
Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds);
if (uri != null) {
if (uri.toString().startsWith("content://media/external/")) {
notifyMtp(notifyRowIds);
}
}
// do not signal notification for MTP objects.
// we will signal instead after file transfer is successful.
// object不会发送通知
if (newUri != null && match != MTP_OBJECTS) {
// Report a general change to the media provider.
// We only report this to observers that are not looking at
// this specific URI and its descendants, because they will
// still see the following more-specific URI and thus get
// redundant info (and not be able to know if there was just
// the specific URI change or also some general change in the
// parent URI).
getContext().getContentResolver().notifyChange(uri, null, match != MEDIA_SCANNER
? ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS : 0);
// Also report the specific URIs that changed.
if (match != MEDIA_SCANNER) {
getContext().getContentResolver().notifyChange(newUri, null, 0);
}
}
return newUri;
}
private Uri insertInternal(Uri uri, int match, ContentValues initialValues, ...) //MediaProvider.java
private long insertFile(DatabaseHelper helper, Uri uri, ContentValues initialValues, ...) //MediaProvider.java
拷贝过程
MtpResponseCode MtpServer::doSendObject() //MtpServer.cpp
private void endSendObject(String path, int handle, int format,...) //MtpDatabase.java
public void scanMtpFile(String path, int objectHandle, int format) //MediaScanner.java
public void scanMtpFile(String path, int objectHandle, int format) {
MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
File file = new File(path);
long lastModifiedSeconds = file.lastModified() / 1000;
// 这里没有判别APK文件,将会返回,不会走到mClient.doScanFile,导致不会通知
if (!MediaFile.isAudioFileType(fileType) && !MediaFile.isVideoFileType(fileType) &&
!MediaFile.isImageFileType(fileType) && !MediaFile.isPlayListFileType(fileType) &&
!MediaFile.isDrmFileType(fileType)) {
// no need to use the media scanner, but we need to update last modified and file size
ContentValues values = new ContentValues();
values.put(Files.FileColumns.SIZE, file.length());
values.put(Files.FileColumns.DATE_MODIFIED, lastModifiedSeconds);
try {
String[] whereArgs = new String[] { Integer.toString(objectHandle) };
mMediaProvider.update(Files.getMtpObjectsUri(mVolumeName), values,
"_id=?", whereArgs);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in scanMtpFile", e);
}
return;
}
mMtpObjectHandle = objectHandle;
Cursor fileList = null;
try {
if (MediaFile.isPlayListFileType(fileType)) {
// build file cache so we can look up tracks in the playlist
prescan(null, true);
FileEntry entry = makeEntryFor(path);
if (entry != null) {
fileList = mMediaProvider.query(mFilesUri,
FILES_PRESCAN_PROJECTION, null, null, null, null);
processPlayList(entry, fileList);
}
} else {
// MTP will create a file entry for us so we don't want to do it in prescan
prescan(path, false);
// always scan the file, so we can return the content://media Uri for existing files
mClient.doScanFile(path, mediaFileType.mimeType, lastModifiedSeconds, file.length(),
(format == MtpConstants.FORMAT_ASSOCIATION), true, isNoMediaPath(path));
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in MediaScanner.scanFile()", e);
} finally {
mMtpObjectHandle = 0;
if (fileList != null) {
fileList.close();
}
releaseResources();
}
}
public Uri doScanFile(String path,...) //MediaScannerMyMediaScannerClient.java
拷贝完成之后存入files表中
public Uri insert(Uri uri, ContentValues initialValues) //MediaProvider.java
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
int match = URI_MATCHER.match(uri);
ArrayList<Long> notifyRowIds = new ArrayList<Long>();
Uri newUri = insertInternal(uri, match, initialValues, notifyRowIds);
if (uri != null) {
if (uri.toString().startsWith("content://media/external/")) {
notifyMtp(notifyRowIds);
}
}
// do not signal notification for MTP objects.
// we will signal instead after file transfer is successful.
// 此时已经是file了,不是Object了,将会发送通知事件
if (newUri != null && match != MTP_OBJECTS) {
// Report a general change to the media provider.
// We only report this to observers that are not looking at
// this specific URI and its descendants, because they will
// still see the following more-specific URI and thus get
// redundant info (and not be able to know if there was just
// the specific URI change or also some general change in the
// parent URI).
getContext().getContentResolver().notifyChange(uri, null, match != MEDIA_SCANNER
? ContentResolver.NOTIFY_SKIP_NOTIFY_FOR_DESCENDANTS : 0);
// Also report the specific URIs that changed.
if (match != MEDIA_SCANNER) {
getContext().getContentResolver().notifyChange(newUri, null, 0);
}
}
return newUri;
}
2、修改方案
查找原因是因为在doSendObjectInfo的时候获取的format不正确
MtpResponseCode MtpServer::doSendObjectInfo() {
...
// read only the fields we need
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // storage ID
if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
MtpObjectFormat format = temp16;//这里获取的format是0x3000,即MTP_FORMAT_UNDEFINED,未定义,导致后面不会执行mClient.doScanFile方法
if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER; // protection status
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
mSendObjectFileSize = temp32;
if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb format
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb compressed size
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb pix width
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // thumb pix height
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // image pix width
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // image pix height
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // image bit depth
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // parent
if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER; // sequence number
MtpStringBuffer name, created, modified;
if (!mData.getString(name)) return MTP_RESPONSE_INVALID_PARAMETER; // file name
if (name.getCharCount() == 0) {
ALOGE("empty name");
return MTP_RESPONSE_INVALID_PARAMETER;
}
if (!mData.getString(created)) return MTP_RESPONSE_INVALID_PARAMETER; // date created
if (!mData.getString(modified)) return MTP_RESPONSE_INVALID_PARAMETER; // date modified
// keywords follow
//在这里做一个筛选,把format等于UNDEFINED,并且是APK文件重新赋值为新定义的APK的format
if(format == MTP_FORMAT_UNDEFINED) {
const char *fileName = (const char *)name;
int len = strlen(fileName);
if(len >= 4 && fileName[len-4] == '.' && (fileName[len-3] == 'a' || fileName[len-3] == 'A')
&& (fileName[len-2] == 'p' || fileName[len-2] == 'P')
&& (fileName[len-1] == 'k' || fileName[len-1] == 'K')) {
format = MTP_FORMAT_APK;
}
}
ALOGV("name: %s format: %04X\n", (const char *)name, format);
...
}
在frameworks/av/media/mtp/mtp.h中加入MTP_FORMAT_APK
#define MTP_FORMAT_UNDEFINED_DOCUMENT 0xBA80
#define MTP_FORMAT_ABSTRACT_DOCUMENT 0xBA81
#define MTP_FORMAT_XML_DOCUMENT 0xBA82
#define MTP_FORMAT_MS_WORD_DOCUMENT 0xBA83
#define MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT 0xBA84
#define MTP_FORMAT_MS_EXCEL_SPREADSHEET 0xBA85
#define MTP_FORMAT_MS_POWERPOINT_PRESENTATION 0xBA86
#define MTP_FORMAT_APK 0xBA87
在frameworks/base/media/java/android/mtp/MtpConstants.java中加入对应java层的formot常量
/** Format code for undefined document files */
public static final int FORMAT_UNDEFINED_DOCUMENT = 0xBA80;
/** Format code for abstract documents */
public static final int FORMAT_ABSTRACT_DOCUMENT = 0xBA81;
/** Format code for XML documents */
public static final int FORMAT_XML_DOCUMENT = 0xBA82;
/** Format code for MS Word documents */
public static final int FORMAT_MS_WORD_DOCUMENT = 0xBA83;
/** Format code for MS Excel spreadsheets */
public static final int FORMAT_MS_EXCEL_SPREADSHEET = 0xBA85;
/** Format code for MS PowerPoint presentatiosn */
public static final int FORMAT_MS_POWERPOINT_PRESENTATION = 0xBA86;
public static final int FORMAT_APK = 0xBA87;
在frameworks/base/media/java/android/media/MediaFile.java新增APK FileType-MimeType-Format的关量表并定义file type
...
public static final int FILE_TYPE_MS_POWERPOINT = 106;
public static final int FILE_TYPE_ZIP = 107;
public static final int FILE_TYPE_APK = 108;
...
static {
...
addFileType("ZIP", FILE_TYPE_ZIP, "application/zip");
addFileType("MPG", FILE_TYPE_MP2PS, "video/mp2p");
addFileType("MPEG", FILE_TYPE_MP2PS, "video/mp2p");
addFileType("APK", FILE_TYPE_APK, "application/apk", MtpConstants.FORMAT_APK);
}
...