You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

访问/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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:42:44