Android 奥利奥版本早已来袭, 各个厂商ROM 需要适配, 最近发现信息模块能发送信息,却收不到信息, 刚开始以为是底层的问题,
随后框架反馈是在接收信息时那边为了Ack,需要将数据插入 raw表, 在插入数据库时有异常:
0-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: Exception dispatching message
10-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: java.lang.SecurityException: Failed to find provider raw for user 0; expected to find a valid ContentProvider for this authority
10-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: at android.os.Parcel.readException(Parcel.java:1942)
10-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: at android.os.Parcel.readException(Parcel.java:1888)
10-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: at android.content.IContentService$Stub$Proxy.notifyChange(IContentService.java:802)
10-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: at android.content.ContentResolver.notifyChange(ContentResolver.java:2049)
10-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: at com.android.providers.telephony.SmsProvider.notifyChange(SmsProvider.java:1832)
10-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: at com.android.providers.telephony.SmsProvider.insert(SmsProvider.java:652)
10-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: at android.content.ContentProvider$Transport.insert(ContentProvider.java:271)
10-23 09:43:35.184 27885 28039 E GsmInboundSmsHandler: at android.content.ContentResolver.insert(ContentResolver.java:1542)
所以需要应用分析, 刚开始从方法看是notifyChange 时出现问题,通过https://developer.android.com/about/versions/oreo/android-8.0-changes.html?hl=zh-cn
查询得知:
Android 8.0 更改了 ContentResolver.notifyChange()
和 registerContentObserver(Uri, boolean, ContentObserver)
在针对 Android 8.0 的应用中的行为方式。
现在,这些 API 需要在所有 URI 中为颁发机构定义一个有效的 ContentProvider
。使用相关权限定义一个有效的 ContentProvider
可帮助您的应用防范来自恶意应用的内容变更,并防止将可能的私密数据泄露给恶意应用。
但是这个说的很笼统,也没有实例,在网上查询了一下,stackoverFlow上也有类似问题反馈,但是也没找到合适的解决方法。
正当山穷水尽疑无路之时,突然想起了 read the f**king souce code 这句话,于是使用grok 搜索抛出 SecurityException 这个异常的源码,在ActivityManagerService.java中找到:
public String checkContentProviderAccess(String authority, int userId) {
11726 if (userId == UserHandle.USER_ALL) {
11727 mContext.enforceCallingOrSelfPermission(
11728 Manifest.permission.INTERACT_ACROSS_USERS_FULL, TAG);
11729 userId = UserHandle.getCallingUserId();
11730 }
11731
11732 ProviderInfo cpi = null;
11733 try {
11734 cpi = AppGlobals.getPackageManager().resolveContentProvider(authority,
11735 STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
11736 | PackageManager.MATCH_DISABLED_COMPONENTS
11737 | PackageManager.MATCH_DIRECT_BOOT_AWARE
11738 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
11739 userId);
11740 } catch (RemoteException ignored) {
11741 }
11742 if (cpi == null) {
11743 return "Failed to find provider " + authority + " for user " + userId
11744 + "; expected to find a valid ContentProvider for this authority";
11745 }
11746
最终看到了源码中对于SDK 在大于等于 Android O 的版本中的处理:
298 public void registerContentObserver(Uri uri, boolean notifyForDescendants,
299 IContentObserver observer, int userHandle, int targetSdkVersion) {
300 if (observer == null || uri == null) {
301 throw new IllegalArgumentException("You must pass a valid uri and observer");
302 }
303
304 final int uid = Binder.getCallingUid();
305 final int pid = Binder.getCallingPid();
306
307 userHandle = handleIncomingUser(uri, pid, uid,
308 Intent.FLAG_GRANT_READ_URI_PERMISSION, true, userHandle);
309
310 final String msg = LocalServices.getService(ActivityManagerInternal.class)
311 .checkContentProviderAccess(uri.getAuthority(), userHandle);
312 if (msg != null) {
313 if (targetSdkVersion >= Build.VERSION_CODES.O) {
314 throw new SecurityException(msg);
315 } else {
316 if (msg.startsWith("Failed to find provider")) {
317 // Sigh, we need to quietly let apps targeting older API
318 // levels notify on non-existent providers.
319 } else {
320 Log.w(TAG, "Ignoring content changes for " + uri + " from " + uid + ": " + msg);
321 return;
322 }
323 }
324 }
从而也明确了 log 中输出的 java.lang.SecurityException: Failed to find provider raw for user 0
raw 就是 authority ,这样问题就浮出水面了, 系统之所以跑那个异常,是因为找不到对应这种以raw 作为authority的provider, 我们的TelephonyProvider中只有 以 sms mms sms-mms 作为authority的provider
所以抛出异常,看到这里我还以为是框架那边在插入时候传入注册监听的registerContentObserver 有问题造成, 所以又想将包袱推给别人,结果框架那边根本就没有注册这种监听,但是对方确给我指明了道路,
就是框架在插入后,会返回uri, 这个出问题的地方就是notifyChange中使用了 insert 返回的uri, 所以是插入返回uri出现了问题, 最终发现是Provider 返回uri那块没有移植Android O 逻辑:
if (table == TABLE_SMS) {
uri = Uri.withAppendedPath(url, "/" + rowID);
} else {
uri = Uri.withAppendedPath(url, "/" + table + "/" + rowID );
}
OMG, read the souce code is so useful .....