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 .....