目录
问题描述
近日公司之前实现的自定义ContentProvider 在Android8上用content 命令操作时会报错:
在用content query 时 会是在调用setNotificationUri 的地方出错。
在用content update时 会是在调用notifyChange的地方出错。
DatabaseUtils: java.lang.SecurityException: Failed to find PID
分析思路
经过分析,得知报错点在ActivityManagerService.java 中的 checkContentProviderAccess 函数中,
2962应该是输入命令时命令所在的进程PID,最开始以为是用命令时,传进去的是命令所在进程的PID,而命令发送后进程退出找不到进程导致。看log也很符合。于是想着把自研应用MyContentProvider 放到系统进程中去,但有个疑惑的地方是MyContentProvider 所在的进程483 已经是system_server。为什么AMS里面不去找483进程呢?
难道是content 命令在高版本中用不了了?如果是这样的话这个命令不就用不了了,为何还要保留呢?于是用命令去查看SettingsProvider 是否能用。
结果发现用settings put global wifi_on 1能修改数据库,切不会报错。content update --uri content://settings/global/wifi_on --bind value:s:1 缺改不了数据库。发现用content update命令时在
isKeyValid 这里会返回去,后面不会再操作数据库了。
于是强制把这行屏蔽,让其能继续运行下去。缺发现操作settingsProvider时获取到的pid是system_server进程(这里由于重启过,进程号变为480了)
原因
这就尴尬了,为何settingsProvider和自定义的Provider传过去的PID不一致呢?最开始还以为是哪些配置没搞好,测了大半天。我一直是觉得Android源码是不会有问题的,另一方面也觉得分析源码很麻烦很耗时。后来下定决心想去研究下Binder.getCallingPid ,看是哪里出问题了。后来在https://blog.csdn.net/bbmcdull/article/details/52046690 这篇博客中找到了答案。
SettingsProvider中调用notifyChange 是通过绑定的handler处理的,传递的就是settingsProvider自身的PID,即system_server 故不会出问题。而MyContentProvider 中调用notifyChange 是直接在复写的update方法中调用的,那么传过去的进程就会是content 所在命令的进程。就会报上面这个错!
解决方法
那么至此也就找到解决方法了:
方法一、参照SettingsProvider 的方式调用notifyChange时,用个绑定新线程的handler去处理,
方法二、
在notifyChange 函数前后分别加上Binder.clearCallingIdentity(); 和Binder.restoreCallingIdentity(origId); 就可以了。
对于setNotificationUri 的处理方法也是一样的!
以后遇到问题,还是不能怂,不能害怕,多分析源码才好!