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

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

问题描述

我想创建一个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.

现在,我有这个code:

For now, I have this code:

清单文件

<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版本,只需要访问由URI 内容定义的内容提供商://短信/发送

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的变更管理的奇巧版本的短信模块的方式。根据接下来的文章中,只有默认的短信应用程序可以编写和更新新的短信息内容提供商(android.provider.Telephony.Sms.Sent - 在previous内容://短信/发送的也不可用):

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):

  • <一个href="http://android-developers.blogspot.pt/2013/10/getting-your-sms-apps-ready-for-kitkat.html">http://android-developers.blogspot.pt/2013/10/getting-your-sms-apps-ready-for-kitkat.html

考虑到这个应用程序的行为,它没有任何意义,将其默认的短信应用程序。这个程序doesn't需要从内容提供商读短信,不应由SmsManager.getDefault()。sendTextMessage发送的任何消息。它应该做的唯一的事情就是在已发提供商写一些消息。

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.

你可以理解,也不能接受的,切实可行,要求用户更改默认的应用程序,以我的,然后再回到previous SMS应用程序,每次有必要写在留言发送(这是建议在建议的短信备份和放大器;还原应用程序,在Android开发者Blogspot的部分)。

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:

  • <一个href="http://www.androidpolice.com/2013/12/06/non-default-sms-apps-in-kitkat-can-still-write-to-the-sms-database-using-a-switch-in-app-ops-no-root-required/">http://www.androidpolice.com/2013/12/06/non-default-sms-apps-in-kitkat-can-still-write-to-the-sms-database-using-a-switch-in-app-ops-no-root-required/

不幸的是,接下来的code停止工作为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.

推荐答案

SmsWriteOpUtil 类使用反射来访问 AppOpsManager 服务,以启用/禁用非默认的短信应用程序的写访问SMS提供的API 19(奇巧)。一旦设定,一个应用程序的访问模式将被保留,直到它被重置,或者应用程序卸载。

The SmsWriteOpUtil 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 19 (KitKat). Once set, an app's access mode will be retained until it is reset, or the app is uninstalled.

启用一个应用程序的写入权限允许应用程序所有的互动与SMS提供商,包括插入()的标准方法删除()

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 the 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.ApplicationInfo;
import android.content.pm.PackageManager;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public final class SmsWriteOpUtil {
    private static final int OP_WRITE_SMS = 15;

    public static boolean isWriteEnabled(Context context) {
        int uid = getUid(context);
        Object opRes = checkOp(context, OP_WRITE_SMS, uid);

        if (opRes instanceof Integer) {
            return (Integer) opRes == AppOpsManager.MODE_ALLOWED;
        }
        return false;
    }

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

        return setMode(context, OP_WRITE_SMS, uid, mode);
    }

    private static Object checkOp(Context context, int code, int uid) {
        AppOpsManager appOpsManager =
            (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        Class appOpsManagerClass = appOpsManager.getClass();

        try {
            Class[] types = new Class[3];
            types[0] = Integer.TYPE;
            types[1] = Integer.TYPE;
            types[2] = String.class;
            Method checkOpMethod =
                appOpsManagerClass.getMethod("checkOp", types);

            Object[] args = new Object[3];
            args[0] = Integer.valueOf(code);
            args[1] = Integer.valueOf(uid);
            args[2] = context.getPackageName();
            Object result = checkOpMethod.invoke(appOpsManager, args);

            return result;
        }
        catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static boolean setMode(Context context, int code,
                                   int uid, int mode) {
        AppOpsManager appOpsManager = 
            (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
        Class appOpsManagerClass = appOpsManager.getClass();

        try {
            Class[] types = new Class[4];
            types[0] = Integer.TYPE;
            types[1] = Integer.TYPE;
            types[2] = String.class;
            types[3] = Integer.TYPE;
            Method setModeMethod =
                appOpsManagerClass.getMethod("setMode", types);

            Object[] args = new Object[4];
            args[0] = Integer.valueOf(code);
            args[1] = Integer.valueOf(uid);
            args[2] = context.getPackageName();
            args[3] = Integer.valueOf(mode);
            setModeMethod.invoke(appOpsManager, args);

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

    private static int getUid(Context context) {
        try {
            int uid = context.getPackageManager()
                .getApplicationInfo(context.getPackageName(),
                                    PackageManager.GET_ACTIVITIES).uid;

            return uid;
        }
        catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
            return 0;
        }
    }
}

实例:

boolean canWriteSms;

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

注:看来这种方法,因为是,不会对棒棒堂(API 21)工作。当/如果我发现该版本的解决方案,我会更新这个帖子。

NB: It seems this method, as is, will not work on Lollipop (API 21). I will update this post when/if I find a solution for that version.

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

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