访问/data/user_de/下MMS附件文件遇权限拒绝问题求助
解决MMS附件读取时的FileNotFoundException(Permission denied)问题
这个权限拒绝的问题根源很明确:你尝试直接访问的/data/user_de/0/com.android.providers.telephony/app_parts/...路径是Telephony Provider应用的私有存储目录,Android的沙箱机制禁止其他应用直接读取这类私有路径下的文件——哪怕你申请了READ_SMS权限也不行,因为READ_SMS允许你通过Content Provider访问MMS数据,但不允许绕过Content Provider直接操作文件系统。
正确的做法是通过ContentResolver直接读取MMS Part的内容,而不是依赖_DATA字段返回的路径。下面是具体的修改方案:
步骤1:修改MMS附件查询逻辑,获取Part的ID
首先在getMmsContent方法中,查询MMS Part时要获取Telephony.Mms.Part._ID字段,这个ID是构造Content Uri的关键:
static void getMmsContent(Context context, ArrayList<Mms> mmsArrayList) { try { for (Mms unMms : mmsArrayList) { ContentResolver contentResolver = context.getContentResolver(); Uri uri = Uri.parse("content://mms/part"); String selection = Telephony.Mms.Part.MSG_ID + "=" + unMms.getId(); // 指定要查询的字段,避免获取冗余数据 String[] projection = { Telephony.Mms.Part._ID, Telephony.Mms.Part.NAME, Telephony.Mms.Part.CONTENT_TYPE, Telephony.Mms.Part.TEXT, Telephony.Mms.Part._DATA }; Cursor query = contentResolver.query(uri, projection, selection, null, null); if (query != null && query.moveToFirst()) { do { int partId = query.getInt(query.getColumnIndex(Telephony.Mms.Part._ID)); String name = query.getString(query.getColumnIndex(Telephony.Mms.Part.NAME)); String type = query.getString(query.getColumnIndex(Telephony.Mms.Part.CONTENT_TYPE)); String txt = query.getString(query.getColumnIndex(Telephony.Mms.Part.TEXT)); // _DATA字段可以保留,但不再用来直接访问文件 if (!type.equals("application/smil")) { // 把partId传入getContent方法 getContent(context, partId, name, type, txt, unMms); } } while (query.moveToNext()); } if (query != null) { query.close(); } } } catch (Exception e) { Log.d("Exception", e.toString()); } }
步骤2:修改getContent方法,通过ContentResolver读取附件内容
不再使用FileInputStream,而是用ContentResolver.openInputStream()来获取MMS Part的输入流,这个方法会通过Content Provider的授权机制正确访问到附件内容:
static private void getContent(Context context, int partId, String name, String type, String txt, Mms unMms){ if (type.equals("text/plain")) { unMms.setCorps(txt); } else { if ("image/jpeg".equals(type) || "image/bmp".equals(type) || "image/gif".equals(type) || "image/jpg".equals(type) || "image/png".equals(type)) { unMms.setTypeContenu(type); Bitmap bitmap = null; InputStream is = null; try { // 构造MMS Part的Content Uri Uri partUri = Uri.parse("content://mms/part/" + partId); // 通过ContentResolver打开输入流 is = context.getContentResolver().openInputStream(partUri); bitmap = BitmapFactory.decodeStream(is); } catch (IOException e) { Log.d("Exception", e.toString()); } finally { if (is != null) { try { is.close(); } catch (IOException e) { Log.d("Exception", e.toString()); } } } if (bitmap != null) { File file = new File(context.getApplicationInfo().dataDir + "/files/", name); OutputStream Fout = null; try { Fout = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.PNG, 100, Fout); Fout.flush(); Fout.close(); } catch (FileNotFoundException e) { Log.d("Exception", e.toString()); } catch (IOException e) { Log.d("Exception", e.toString()); } } } } }
额外注意事项
- 确保你的应用已经动态申请并获取了
READ_SMS权限(Android 6.0及以上需要动态权限申请,仅在Manifest中声明是不够的)。 - 对于Android 10及以上的设备,这种通过Content Provider访问MMS附件的方式不受Scoped Storage的限制,无需额外处理外部存储权限(当然如果你需要把处理后的图片保存到外部存储,还是需要对应的权限)。
内容的提问来源于stack exchange,提问作者M3TALzero1
相关产品推荐
相关产品推荐

