android 监听挂载,android MountService实现------外设挂载及状态监听

事件起因:

我们的测试报出一个问题,插着U盘开机(我们的机顶盒),多媒体文件未扫描到(U盘里有视频文件还有音乐文件)。说本地的视频文件列表还有音乐列表也是空的。

我是做应用的,兼顾Framework仓库的维护。负责媒体扫描的是MediaProvider.apk。。。义不容辞,这个问题当然是我来负责解决。

问题分析:

1.首先,我显示查看MediaProvider.apk所对应的源码实现。其源码实现很简单,下面我做一下简单的说明。

主要的两个类如下:MediaScannerReceiver.java和MediaScannerService.java。

MediaScannerReceiver.java源码

private final static String TAG = "MediaScannerReceiver";

@Override

public void onReceive(Context context, Intent intent) {

String action = intent.getAction();

Log.d(TAG, "onReceive,action:" + action);

Uri uri = intent.getData();

if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {

// 收到开机广播,扫描内部存储

scan(context, MediaProvider.INTERNAL_VOLUME);

} else {

if (uri.getScheme().equals("file")) {

// 处理扫描外设的Intent

String path = uri.getPath();

String externalStoragePath = Environment.getExternalStorageDirectory().getPath();

Log.d(TAG, "action: " + action + " path: " + path);

if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {

// 扫描已挂载的外设

scan(context, MediaProvider.EXTERNAL_VOLUME);

} else if (action.equals(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE) &&

path != null && path.startsWith(externalStoragePath + "/")) {

scanFile(context, path);

}

}

}

}

//扫描指定的Volume

private void scan(Context context, String volume) {

Bundle args = new Bundle();

args.putString("volume", volume);

context.startService(

new Intent(context, MediaScannerService.class).putExtras(args));

}

//扫描指定的Path

private void scanFile(Context context, String path) {

Bundle args = new Bundle();

args.putString("filepath", path);

context.startService(

new Intent(context, MediaScannerService.class).putExtras(args));

}

从这段代码就可以知道,在收到开机广播和Intent.ACTION_MEDIA_MOUNTED广播都会进行文件的扫描。外设的扫描是接收到Intent.ACTION_MEDIA_MOUNTED广播时进行。

2.从开机抓的log分析,确实是没有收到Intent.ACTION_MEDIA_MOUNTED广播,但是在终端使用df命令可以看到U盘已经正常挂载了。

3.外设状态的广播发送都是的MountService里面进行的。所以接下来我们来看一下开机时MountService都做了些什么。

MountService.java的启动是在SystemServer.jar中进行的,ServiceManager.addService("mount",new MountService(context))。这样就是MountService.java的构造函数就会被回调。

MountService.java的构造函数中跟今天研究相关的关键的两个处理就是

1)mContext.registerReceiver(mBroadcastReceiver, filter, null, null);

2)mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG);

Thread thread = new Thread(mConnector, VOLD_TAG);

thread.start();

操作1)中的广播接收器接收的是Intent.ACTION_BOOT_COMPLETED(开机完成广播)。接收到开机广播后,最要的处理就是针对mVolumeStates中保存的外设路径做外设的挂载动作,挂载中的各种状态通知是在onEvent(int code, String raw, String[] cooked)回调中进行的。onEvent中调用的notifyVolumeStateChange方法会发送外设状态广播通知。所以现在最重要的是mVolumeStates中的路径是何时保存进去的?这就涉及到操作2)中的实现。

操作2)中的NativeDaemonConnector 是一个implements Runnable类,   当 thread.start()线程启动时,NativeDaemonConnector.java的run()方法就会执行。run()方法中关键的实现是了NativeDaemonConnector.java类中内部方法listenToSocket(),listenToSocket()方法中重要的实现是mCallbacks.onDaemonConnected(),onDaemonConnected()方法调用的是 MountService.java类中的onDaemonConnected()方法。onDaemonConnected()方法中重要的实现是:String[] vols = mConnector.doListCommand("volume list", VoldResponseCode.VolumeListResult); 其功能就是从底层获取当前的外设列表。然后通过调用updatePublicVolumeState(path, state);方法更新mVolumeStates。这样就解决了操作1)中的疑惑。

4.通过对步骤3的了解,最终定位到在notifyVolumeStateChange方法里调用getVolumeState(String mountPoint)时throw new IllegalArgumentException(),这样notifyVolumeStateChange方法里的外设状态广播通知没有进行。

5.为什么步骤 4中抛出异常呢?是因为getVolumeState(String mountPoint)里通过mVolumeStates.get(mountPoint);没有获得对应挂载点的状态。即该挂载点不再步骤3中的2)里从底层获取当前的外设列表里。

6.外设列表是从底层查询的(vold给的),onEvent里面的挂载点也是vold给的。那就是vold提供的这两个不一致。。。不一致我就找对应的负责人沟通了。。。他给了一句“臣妾做不到一致”,讲了一大堆,大概意思就是说有的U盘带分区,有的不带,他那边处理的要兼容的话就做不到那一点。

7.那怎么处理呢???既然U盘能正常挂载,就是没有通知广播,没有通知广播的原因也是抛出异常引起,那就不抛异常了。。。这样可以解决问题,但是不足之处在于别的地方通过MountService.java的getVolumeState(String mountPoint)查询挂载点状态时,传入真正的挂载点有可能获取不到对应的状态。这也是没有办法的办法了。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值