android mtp模式下连接PC后只显示指定文件夹

转载请注明文章出错及作者
作者:Xandy
出处:http://blog.csdn.net/xl19862005
一、mtp概述
android在3.0以后的版本加入了mtp的支持,相对于mass storage模式,由于mtp优越性,现在几乎所有的手机连接PC后都是以mtp的方式进行文件访问。
这里简单讲述一下mtp的优点:
1、Initiator和Responder可同时对文件进行存储。相对于mass storage的模式,这种优点是显而易见的。
这里写图片描述
PC连接上responder设备之后,不是直接对设备中的存储分区进行访问,而是通过vfs的方式间接访问存储分区中的文件,这个作为Initiator端的PC设备来说,就不再需要关心要访问的responder存储分区是什么文件系统了,通过公用的vfs就可以对不同文件系统的存储设备进行读写了。
2、mtp模式下Initiator可以知道Responder所支持的媒体文件格式有哪些
3、文件访问权限可控。这点是笔者根据android下mtp的架构自加的,也正是本文所需要说的重点。
二、android mtp启动流程
这里写图片描述
这里要提到一点的是:android设备启动之后,当在MediaScannerReceiver(android_src/providers/MediaProvider/src/com/android/providers/media/MediaScannerReceiver.java)中监听到开机完成广播(android.intent.action.BOOT_COMPLETED)时,会启动MeidaScannerService,对整个设备内部的存储设备进行扫描,并将扫描到的文件存入数据库!
这里要提一个原生android系统的bug:在开机完成之后,在android设备上拍照或者截图后,将设备连接上PC,是无法找到刚拍的照片或截图图片的!这是因为MediaScannerService的启动只在BOOT_COMPLETED时scan一次,此后新增加的文件都还没有更新到数据库,需要重启系统后在PC上才能发现新增加的文件。
为了解决此bug,我在MediaScannerReceiver中增加了对USB_STATE状态广播的监听,所以每次插拔USB时都会scan一次。
而从上图可知,MtpService启动后也是需要去数据库(MtpDatabase)里拿文件的,所以可以通过修改数据库的查找规则来达到连接PC后只显示指定文件/文件夹的功能。
三、文件过滤代码修改
首先来看看MtpDatabase(android_src/frameworks/base/media/java/android/mtp/MtpDatabase.java)里关于创建数据库查询的方法:

private Cursor createObjectQuery(int storageID, int format, int parent) throws RemoteException {String where;String[] whereArgs;if (storageID == 0xFFFFFFFF) {// query all storesif (format == 0) {// query all formatsif (parent == 0) {// query all objectswhere = null;whereArgs = null;} else {if (parent == 0xFFFFFFFF) {// all objects in root of storeparent = 0;}where = PARENT_WHERE;whereArgs = new String[] { Integer.toString(parent) };}} else {// query specific formatif (parent == 0) {// query all objectswhere = FORMAT_WHERE;whereArgs = new String[] { Integer.toString(format) };} else {if (parent == 0xFFFFFFFF) {// all objects in root of storeparent = 0;}where = FORMAT_PARENT_WHERE;whereArgs = new String[] { Integer.toString(format),Integer.toString(parent) };}}} else {// query specific storeif (format == 0) {// query all formatsif (parent == 0) {// query all objectswhere = STORAGE_WHERE;whereArgs = new String[] { Integer.toString(storageID) };} else {if (parent == 0xFFFFFFFF) {// all objects in root of storeparent = 0;where = STORAGE_PARENT_WHERE;whereArgs = new String[] { Integer.toString(storageID),Integer.toString(parent) };}} else {// query specific formatif (parent == 0) {// query all objectswhere = STORAGE_FORMAT_WHERE;whereArgs = new String[] {  Integer.toString(storageID),Integer.toString(format) };} else {if (parent == 0xFFFFFFFF) {// all objects in root of storeparent = 0;}where = STORAGE_FORMAT_PARENT_WHERE;whereArgs = new String[] { Integer.toString(storageID),Integer.toString(format),Integer.toString(parent) };}}}// if we are restricting queries to mSubDirectories, we need to add the restriction// onto our "where" argumentsif (mSubDirectoriesWhere != null) {if (where == null) {where = mSubDirectoriesWhere;whereArgs = mSubDirectoriesWhereArgs;} else {where = where + " AND " + mSubDirectoriesWhere;// create new array to hold whereArgs and mSubDirectoriesWhereArgsString[] newWhereArgs =new String[whereArgs.length + mSubDirectoriesWhereArgs.length];int i, j;for (i = 0; i < whereArgs.length; i++) {newWhereArgs[i] = whereArgs[i];}for (j = 0; j < mSubDirectoriesWhereArgs.length; i++, j++) {newWhereArgs[i] = mSubDirectoriesWhereArgs[j];}whereArgs = newWhereArgs;}} return mMediaProvider.query(mPackageName, mObjectsUri, ID_PROJECTION, where,whereArgs, null, null);}

而方法query的定义如下:

public Cursor query(String callingPkg, Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder, IcancellationSignal cancellationSignal)throws RemoteExceptioin;

不难理解,方法createObjectQuery中获得的where 字串是用于到数据库里查询与where匹配的文件的,这里还要说明一点的是mSubDirectoriersWhere,可以看到当这个subDirectoriersWhere不为空时,作了如下合并到where字串

where = where + " AND " + mSubDirectoriesWhere;

那么可以通过增加对这个mSubDirectoriesWhere的赋值(赋以指定需要到数据库里查询的文件夹名),就可以达到想要的文件过滤功能!
而mSubDirectoriesWhere只在MtpDatabase的构造方法里进行赋值:

public MtpDatabase(Context context, Stirng volumeName, String storagePath, String[] subDirectories){……if(subDirectories != null){StringBuilder builder = new StringBuilder();int count = subDirectories.lenght;for(int i=0;i

那么只要在创建 MtpDatabase的时候传入这个subDirectories String数组就可以达到想要的功能了!
在MtpService(android_src/packages/providers/MediaProvider/src/com/android/providers/media/MtpService.java)中增加如下代码:

private static final String[] MTP_DIRECTORIES_PRIVATE = new String[]{Environment.DIRECTORY_DCIM,Environment.DIRECTORY_PICTURES,Environment.DIRECTORY_MOVIES,Environment.XXXXXX,……
};
……
@Override
public int onStartCommand(Intent intent, int flags, int startId){……if(!mPtpMode){int num = MTP_DIRECTORIES_PRIVATE.length;subdirs = new String[num];for(int j=0;j//最后在这里创建MtpDatabase,并传入subdirs到前面提到的wheremDatabase = new MtpDatabase(this, MediaProvider.EXTERNAL_VOLUME, primary.getPath(), subdirs);……
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部