前段时间做了个android TV的项目,自己负责的模块是Launcher,这个Launcher上正好有个TV的小窗口,播放实时的TV信号。有一天一个测试跑到我这,感觉天要塌下来一样。
说: "Hi, 兄弟大事不好啦,完蛋啦。。。Launcher的TV不能播了"
我说“尼玛,竟有此事!”
于是跟着测试跑到问题机器上一看。。。尼玛电视电视,竟然无法播放电视了,我勒个去。。。。
于是分析了一下,经过一天的研究,终于发现,原来尼玛芯片厂商对media和tv信号的播放处理,在最底层使用都是同一个通道,存在缺陷,无法灵活切换。。这样的结果就是,如果用户使用播放某个视频文件,然后按住home键退出,回到launcher的TV播放页面,这个时候会出现tv无法播放的问题。
而某些应用,在被home处理到后台的时候,是不会去释放media资源的。。。这该如何是好。而且针对TV的这种场景,不同于手机,进入launcher是需要将一些后台播放的如音乐播放等这些给关掉的。。。
后来想了两个方法,一个叫笨笨一个叫聪聪:
笨笨的方法就是,把这些media应用加入黑名单,只要进入launcher的TV页,遍历一遍这些应用列表,然后判断在Launcher的onResume中判断刚才的包是否是这些应用,如果是的话就kill掉他们。。。
这种方法,虽然简单,但是不够灵活,如果用户装了多个多媒体软件,那么黑名单这种模式,就无法cover住了。。。
后来开动脑子,想了个歪主意,就是要使能够知道谁占用了media资源,然后根据它的pid号杀死它就好了。。。
那么就需要做两部:
1 如何获取占用media资源的pid.也就是我是谁?
2 如何把这个pid告诉给需要杀死我的人,这么贱。。。嘿嘿。。
如果获取占用media资源的id号呢。。。。嘿嘿。。。这个就需要修改Framework中的MeidaPlayer源生SDK。。。
Binder.getCallingPid();
这个方法写在某个方法中,就可以知道谁调用了我。。。我把它写在了MediaPlayer的start()方法中。
这样就顺利的完成了我是谁的任务了。。。但是如何通知Launcher应用杀我呢。。用广播?MediaPlayer SDK木有context啊。。。重新封装接口?改动太大。。想想android其他的跨进程通讯的方式。。。貌似都行不通,linux的呢?socket?共享内存?尼玛。。。太蛋疼了。。。超级复杂哇。。。
算了还是写文件告诉别人吧。。。可以由于android的安全机制,是不允许一个SDK写一个文件被应用来访问的。。。这个该如何是好啊。。。哎,根据本人之前的linux开发经验,想了个不是办法的办法。。。那就是利用tmpfs这个在内存中的文件系统。。。掉电消失。。。。呵呵。。。这个文件系统没有这么多权限,谁都可以访问。。。哈哈。。。太好了。。。可以遇到问题了,忙活了半天发现木有这个/tmp目录在我的电视中。。。咋办?
只好,自己丰衣足食了。。。囧
这个需要在自己的系统代码中的相应平台的xxxx_init.rc中添加,注意不是在init.rc中,那个木有用。。。至于为啥没用,请看android加载的init机制。。。
mkdir /tmp 0777 system system
mount tmpfs tmpfs /tmp mode=0777,gid=1000,size=1024k
这样就创建好了这个tmpfs的文件系统。。。烧完系统点亮机器,哎。。。尼玛有了。。。泪流满面啊。。
接下来就简单了。。。只需要将第一步中获取的pid写到这个tmpfs中就行了,我用pid号作为文件名,这个想法来自于早期学习linux /proc文件系统中看到的pid目录。。linux查看pid的ps命令就是检索这些目录名来获取pid的信息。。。呵呵。。。。
于是乎在MediaPlayer.start()中在tmpfs下创建以pid为名的文件。。。怎么创建一个文件,这里就不说了。。
记住,当MediaPlayer.realse()的时候一定要将tmpfs下的这个pid文件给删除掉,不要造成误删。
完成了,身份识别和通知机制,最后的杀手出场了,当进入到Launcher的TV页时候,我们通过读取tmpfs的pid文件,获取pid,然后通过pid获取package的名字,通过包名来杀死这个应用。。。注意这里杀死应用,需要用到java反射机制。
贴下核心杀人代码:
ActivityManager am = (ActivityManager)getSystemService(
Context.ACTIVITY_SERVICE);
Method forceStopPackage = am.getClass()
.getDeclaredMethod("forceStopPackage", String.class);
forceStopPackage.setAccessible(true);
forceStopPackage.invoke(am, kill_package);
是通过ps pid命令来获取输出,然后进行匹配。。。得到包名。。。。
注意你的Launcher如果没有system 权限,我这篇文章你看了也没用。。
自宫也无法练成辟邪剑谱,哈哈
打完收工。。。。哈哈。。。绕过android安全机制的一个方法。。。希望对大家有用。。。还好自己以前搞过linux,写过驱动,干过脏活。。。呵呵。。。