在Android 4.2中如何读取APNs?

11

我在Android v4.2中读取APNs时遇到了问题(是读取而不是写入APNS),它会抛出一个安全异常:

没有写入APN设置的权限:用户10068和当前进程都没有android.permission.WRITE_APN_SETTINGS权限。

相同的代码在所有先前的平台上都可以工作,请问有没有任何方法来解决这个问题?

谢谢!


4
听起来像是一个bug,或者是保护APN数据的一种笨拙方式(如果是这样,应该有专门的 READ_APN_SETTINGS 权限)。我没有看到对此问题进行的记录,如果你能创建一个可以重现该错误的项目,请将其发布在 http://b.android.com 上,并提供说明。 - CommonsWare
1
完成了,您可以在此处检查:http://code.google.com/p/android/issues/detail?id=39987 - Shatazone
2
请参见http://code.google.com/p/android/issues/detail?id=29264。希望相同的解决方案适用于读取和写入APN。希望在其他制造商采用4.2之前能够实现。称我为一个梦想家。 - Tom
我把我的应用程序放在/system/app中,但仍然收到此错误。难道不应该将此权限授予系统文件夹中的任何应用程序或由OEM签名的应用程序吗? - Miguel Ribeiro
4个回答

13
如果您想阅读Android 4.2及更高版本的APN配置,是有方法可以实现的。我已经测试过并且可以正常工作。
在Android 4.1及以下版本,请使用以下配置:
Cursor c = getContentResolver().query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"), null, null, null, null);

对于 Android 4.2 及以上版本,请使用以下代码:

private static final String[] APN_PROJECTION = {
     Telephony.Carriers.TYPE,            // 0
     Telephony.Carriers.MMSC,            // 1
     Telephony.Carriers.MMSPROXY,        // 2
     Telephony.Carriers.MMSPORT          // 3
 };

并且这一行:

final Cursor apnCursor =SqliteWrapper.query(context, this.context.getContentResolver(), Uri.withAppendedPath(Carriers.CONTENT_URI, "current"), APN_PROJECTION, null, null, null);

SQLiteWrapperClass被隐藏了(在互联网上发现了这个类)

import android.database.sqlite.SqliteWrapper;

我的英语不太好,对此感到抱歉。


嗨。我在导入android.provider.Telephony.Carriers时遇到了问题,即使Android文档说它是公共的自API 19以来。我该如何导入它? - Narseo
2
在api 19中,这不是问题。该Carriers类可用于此导入import android.provider.Telephony; 如果这不起作用,请将java类取出源代码并导入到src文件夹中。 - vincent091
1
我不得不更新sdk,然后它就可以工作了。但是,除了您使用SqliteWrapper报告的字段之外,我还遇到了一些阅读特定字段的问题(第一种方法对我没有起作用)。在http://developer.android.com/reference/android/provider/Telephony.Carriers.html中报告的字段中,只有以下字段似乎可读:Telephony.Carriers.APN, Telephony.Carriers.MMSPROXY, Telephony.Carriers.MMSPORT, Telephony.Carriers.MMSC, Telephony.Carriers.TYPE我是否缺少权限(我已经拥有read_apn_settings)或者做错了什么? - Narseo
@ashish你找到解决方案了吗? - Dakait
太棒了,这个可行。在三星Galaxy S4上测试过,系统版本为5.0.1。谢谢伙计 :) :D :D - Hardik Joshi
显示剩余3条评论

8
这似乎是一个有意的更改。添加此防御措施的git提交包括以下评论:
“由于数据库可能包含公司密码,因此我们应该对其进行保护。与写入数据库相同的权限读取也可能像写入一样具有破坏性。”
你的问题可能会导致他们考虑添加单独的读取权限,但至少目前来看,这是4.2版本中的一个退化。

所有使用移动数据执行操作(例如发送MMS)且无法了解设备中存在哪些默认APN设置的应用程序的替代方案是什么? - Etienne Lawlor
1
@toobsco42:我也不知道。你可能想要开一个新的StackOverflow问题,看看是否有人有解决方案。 - CommonsWare

3
您可以从 /etc/apns-conf.xml 读取默认设置:
private boolean getSettingsFromApnsFile(Context context, String apnName) {
    FileReader reader = null;
    boolean sawValidApn = false;

    try {
        reader = new FileReader("/etc/apns-conf.xml");

        XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
        factory.setNamespaceAware(true);
        XmlPullParser xpp = factory.newPullParser();
        xpp.setInput(reader);

        TelephonyManager telephonyManager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
        String simOperator = telephonyManager.getSimOperator();
        if (TextUtils.isEmpty(simOperator)) {
            logger.warn("unable to get sim operator - so unable to get mms config");
            return false;
        }

        int eventType = xpp.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_TAG && xpp.getName().equals("apn")) {
                HashMap<String, String> attributes = new HashMap<String, String>();
                for (int i=0; i<xpp.getAttributeCount(); i++) {
                    attributes.put(xpp.getAttributeName(i), xpp.getAttributeValue(i));
                }
                if (attributes.containsKey("mcc") && attributes.containsKey("mnc") && simOperator.equals(attributes.get("mcc")+attributes.get("mnc"))) {
                    if (!TextUtils.isEmpty(apnName) && !apnName.trim().equals(attributes.get("apn"))) {
                        eventType = xpp.next();
                        continue;
                    }

                    if (isValidApnType(attributes.get("type"), PhoneConstants.APN_TYPE_MMS)) {
                        sawValidApn = true;

                        String mmsc = attributes.get("mmsc");
                        if (mmsc == null) {
                            eventType = xpp.next();
                            continue;
                        }

                        mServiceCenter = NetworkUtil.trimV4AddrZeros(mmsc.trim());
                        mProxyAddress = NetworkUtil.trimV4AddrZeros(
                                attributes.get("mmsproxy"));
                        if (isProxySet()) {
                            String portString = attributes.get("mmsport");
                            try {
                                mProxyPort = Integer.parseInt(portString);
                            } catch (NumberFormatException e) {
                                if (TextUtils.isEmpty(portString)) {
                                    logger.warn("mms port not set!");
                                } else {
                                    logger.error("Bad port number format: " + portString, e);
                                }
                            }
                        }
                    }

                }
            }
            eventType = xpp.next();
        }
    } catch (Exception e) {
        logger.warn("unable to get mmsc config from apns-conf file", e);
    } finally {
        if (reader != null) {
            try {
                reader.close();
            } catch (Exception e) {
            }
        }
    }
    return sawValidApn;
}

但这不是 SDK 的一部分,它可能无法在所有 Android 设备上运行,你觉得呢? - Shatazone
是的,现在我知道HTC已经在至少一台设备上删除了该文件。 - notz
这只是Android上默认APN配置列表。正如@Shatazone所提到的,它取决于手机型号。 - Narseo
是的,没错。配置不依赖于设备型号。每个ROM上唯一不同的只有文件(在AOSP Git中往往会经常更改)。这只是解决Android Bug的一种方法。 你也可以在你的应用程序中提供更新版本。就像我们所做的那样。 - notz

-6
我也遇到了这种情况,我的解决方案是在AsyncTask中不要访问android_assets。确保只有主线程有权限访问您应用程序的资产目录
我在编写代码时遇到了问题:
@Override
protected void onResume() {
    super.onResume();
    //mWebView.loadUrl("file:///android_asset/95306.html");
    new LoadUrlTask().execute("file:///android_asset/95306.html");
}

...

class LoadUrlTask extends AsyncTask<String, Integer , String> {
    // progressDialog = new ProgressDialog(LoadActivity.this);

    @Override
    protected String doInBackground(String... strings) {
        mWebView.loadUrl(strings[0]);
        return "";
    }

    @Override
    protected void onPostExecute(String s) {
        super.onPostExecute(s);
        //progressDialog.dismiss();
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        //progressDialog.setMessage("loading...");
        //progressDialog.show();
    }
}

我通过以下方式修复它:

@Override
protected void onResume() {
    super.onResume();
    mWebView.loadUrl("file:///android_asset/95306.html");
    //new LoadUrlTask().execute("file:///android_asset/95306.html");
}

希望这能对你有所帮助!

3
这句话的意思是“这样做有什么帮助吗?你的回答完全超出了问题的范围,与访问APN设置没有任何关系。”请注意,翻译只涉及文本内容,不涉及解释。 - ar34z

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接