Android KitKat (API 19) - 如何从非默认应用程序在 SMS 内容提供程序中编写消息而不发送它们? [英] Android KitKat (API 19) - How to write messages in SMS Content Provider, without sending them, from Non-Default App?

查看:34
本文介绍了Android KitKat (API 19) - 如何从非默认应用程序在 SMS 内容提供程序中编写消息而不发送它们?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个在系统的发送箱中写入消息的 Android 应用程序.这些消息不应通过 GSM 网络发送给接收者,其想法只是将它们写入已发送的内容提供程序中.

I am trying to create an Android app that writes messages in the Sent Box of the system. These messages should not be sent over the GSM network to the recipient, the idea is only to write them in the Sent Content Provider.

现在,我有这个代码:

清单文件

<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.WRITE_SMS"/>

Java 类

private final String SENT_SMS_CONTENT_PROVIDER_URI_OLDER_API_19 = "content://sms/sent";

ContentValues values = new ContentValues();
values.put("address", mNumber);
values.put("body", mMessage);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
  mContext.getContentResolver().insert(Telephony.Sms.Sent.CONTENT_URI, values);
else mContext.getContentResolver().insert(Uri.parse(SENT_SMS_CONTENT_PROVIDER_URI_OLDER_API_19), values);

对于 API 版本低于 19 的设备,此实现工作正常.对于这些较旧的sdk版本,只需要访问uricontent://sms/sent定义的content provider即可.

For a device with an API version lower than 19, this implementation works just fine. For these older sdk versions, it is only necessary to access to the content provider defined by the uri content://sms/sent.

对于较新的 sdk 版本,这不起作用.显然,Android 在 KitKat 版本中改变了管理 SMS 模块的方式.根据下一篇文章,只有默认的短信应用才能编写和更新新的短信内容提供器(android.provider.Telephony.Sms.Sent - 之前的 content://sms/sent 也不可用):

For the newer sdk versions, this is not working. Apparently, Android changed its way of managing the SMS module in the KitKat release. According the next article, only the default SMS application can write and update the new SMS Content Provider (android.provider.Telephony.Sms.Sent - the previous content://sms/sent is also not available):

考虑到此应用的行为,将其设为默认短信应用是没有意义的.此应用程序不需要从内容提供商读取 SMS 消息,也不应通过 SmsManager.getDefault().sendTextMessage 发送任何消息.它唯一应该做的就是在 Sent Provider 中写入一些消息.

Considering the behavior of this app, it doesn't make sense to turn it the default SMS app. This app doesn´t need to read SMS messages from the content provider and should not send any message by SmsManager.getDefault().sendTextMessage. The only thing it should do is write some messages in the Sent Provider.

正如你所理解的,要求用户把默认的app改成我的,然后又回到之前的短信app,每次都要在Sent(这个Android Developers Blogspot 的有关 SMS 备份和恢复应用程序的建议"部分中提出了建议.

As you can understand, it is also not acceptable and practicable to request the user to change the default app to mine and then go back to the previous SMS app, each time it is necessary to write a message in the Sent (this is suggested in the "Advice for SMS backup & restore apps" section in the Android Developers Blogspot).

下一篇文章揭示了一些取消隐藏选项 OP_WRITE_SMS 的方法:

The next article reveals some ways to unhide the option OP_WRITE_SMS:

不幸的是,下一个代码不再适用于 Android 4.4.2:

Unfortunately, the next code stopped working for Android 4.4.2:

Intent intent = new Intent();
intent.setClassName("com.android.settings", "com.android.settings.Settings");
intent.setAction(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
intent.putExtra(":android:show_fragment", "com.android.settings.applications.AppOpsSummary");
startActivity(intent);

我没有解决此问题的解决方案.

I am out of solutions to overcome this problem.

推荐答案

SmsWriteOpUtils 类使用反射来访问 AppOpsManager 服务的方法,以启用/禁用非默认 SMS 应用程序对 API 级别 19 (KitKat) 中的 SMS 提供程序的写入访问权限.设置后,应用的访问模式将一直保留,直到重置或卸载应用.

The SmsWriteOpUtils class uses reflection to access methods of the AppOpsManager Service in order to enable/disable a non-default SMS app's write access to the SMS Provider in API Level 19 (KitKat). Once set, an app's access mode will be retained until it is reset, or the app is uninstalled.

启用应用程序的写访问权限允许该应用程序与 SMS 提供者交互的所有标准方法,包括 insert()delete().

Enabling an app's write access allows that app all of the standard methods of interaction with the SMS Provider, including insert() and delete().

请注意,该类不进行 API 级别检查,并且仍然需要 WRITE_SMS 权限.

Please note that this class does no API Level check, and that the WRITE_SMS permission is still required.

import android.app.AppOpsManager;
import android.content.Context;
import android.content.pm.PackageManager;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public final class SmsWriteOpUtils {
    private static final int WRITE_OP_CODE = 15;

    public static boolean isWriteEnabled(Context context) {
        int result = checkOp(context);
        return result == AppOpsManager.MODE_ALLOWED;
    }

    public static boolean setWriteEnabled(Context context, boolean enabled) {
        int mode = enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED;
        return setMode(context, mode);
    }

    private static int checkOp(Context context) {
        try {
            Method checkOpMethod = AppOpsManager.class.getMethod("checkOp",
                                                                 Integer.TYPE,
                                                                 Integer.TYPE,
                                                                 String.class);

            AppOpsManager appOpsManager =
                (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            int uid = context.getApplicationInfo().uid;
            String packageName = context.getPackageName();

            return checkOpMethod.invoke(appOpsManager, WRITE_OP_CODE, uid, packageName);
        }
        catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return -1;
    }

    private static boolean setMode(Context context, int mode) {
        try {
            Method setModeMethod = AppOpsManager.class.getMethod("setMode",
                                                                 Integer.TYPE,
                                                                 Integer.TYPE,
                                                                 String.class,
                                                                 Integer.TYPE);

            AppOpsManager appOpsManager =
                (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
            int uid = context.getApplicationInfo().uid;
            String packageName = context.getPackageName();

            setModeMethod.invoke(appOpsManager, WRITE_OP_CODE, uid, packageName, mode);

            return true;
        }
        catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return false;
    }
}

示例用法:

boolean canWriteSms;

if(!SmsWriteOpUtils.isWriteEnabled(getApplicationContext())) {
    canWriteSms = SmsWriteOpUtils.setWriteEnabled(getApplicationContext(), true);
}
...

<小时>

注意:对于普通用户应用,这仅适用于 API 级别 19 (KitKat).该漏洞在以后的版本中被修补.


NB: For regular user apps, this works only on API Level 19 (KitKat). The hole was patched in later versions.

这篇关于Android KitKat (API 19) - 如何从非默认应用程序在 SMS 内容提供程序中编写消息而不发送它们?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆