android 4.4删除短信

android 4.4之后非默认的短信应用已经没有办法删除短信了。像以前那样用如下方法是不会没法删除短信的(即使在xml中配置了短信的读写权限),同时也不会有报错或其他提示。

public void deleteSMS() {
        try {
            ContentResolver CR = getContentResolver();
            // Query SMS
            Uri uriSms = Uri.parse("content://sms/inbox");
            Cursor c = CR.query(uriSms, new String[] { "_id", "thread_id" },
                    null, null, null);
            if (null != c && c.moveToFirst()) {
                do {
                    // Delete SMS
                    long threadId = c.getLong(1);
                    int result = CR.delete(Uri
                            .parse("content://sms/conversations/" + threadId),
                            null, null);
                    Log.d("deleteSMS", "threadId:: " + threadId + "  result::"
                            + result);
                } while (c.moveToNext());
            }
        } catch (Exception e) {
            Log.d("deleteSMS", "Exception:: " + e);
        }
    }

但通过打印可以看到上述代码的result是等于0的,即没有删除掉短信。

这个是因为在:/frameworks/base/services/java/com/android/server/AppOpsService.java中android系统添加了权限检查的函数

检查用户设定权限的函数是:checkOperation() 和 noteOperation(),区别是 checkOperation() 只是检查 Operation 的情况,noteOperation() 还会记录访问时间等信息,代码如下:

@Override
public int checkOperation(int code, int uid, String packageName) {
    verifyIncomingUid(uid);
    verifyIncomingOp(code);
    synchronized (this) {
        Op op = getOpLocked(AppOpsManager.opToSwitch(code), uid, packageName, false);
        if (op == null) {
            return AppOpsManager.MODE_ALLOWED;
        }
        return op.mode;
    }
}

@Override
public int noteOperation(int code, int uid, String packageName) {
    verifyIncomingUid(uid);
    verifyIncomingOp(code);
    synchronized (this) {
        Ops ops = getOpsLocked(uid, packageName, true);
        if (ops == null) {
            if (DEBUG) Log.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
                    + " package " + packageName);
            return AppOpsManager.MODE_IGNORED;
        }
        Op op = getOpLocked(ops, code, true);
        if (op.duration == -1) {
            Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
                    + " code " + code + " time=" + op.time + " duration=" + op.duration);
        }
        op.duration = 0;
        final int switchCode = AppOpsManager.opToSwitch(code);
        final Op switchOp = switchCode != code ? getOpLocked(ops, switchCode, true) : op;
        if (switchOp.mode != AppOpsManager.MODE_ALLOWED) {
            if (DEBUG) Log.d(TAG, "noteOperation: reject #" + op.mode + " for code "
                    + switchCode + " (" + code + ") uid " + uid + " package " + packageName);
            op.rejectTime = System.currentTimeMillis();
            return switchOp.mode;
        }
        if (DEBUG) Log.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
                + " package " + packageName);
        op.time = System.currentTimeMillis();
        op.rejectTime = 0;
        return AppOpsManager.MODE_ALLOWED;
    }
}

然后在MmsServiceBroker服务中可以找到如下代码就是对应用删除短信的权限进行检查

        @Override
        public boolean deleteStoredMessage(String callingPkg, Uri messageUri)
                throws RemoteException {
            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS,
                    "Delete SMS/MMS message");
            if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
                return false;
            }
            return getServiceGuarded().deleteStoredMessage(callingPkg, messageUri);
        }

        @Override
        public boolean deleteStoredConversation(String callingPkg, long conversationId)
                throws RemoteException {
            mContext.enforceCallingPermission(Manifest.permission.WRITE_SMS, "Delete conversation");
            if (getAppOpsManager().noteOp(AppOpsManager.OP_WRITE_SMS, Binder.getCallingUid(),
                    callingPkg) != AppOpsManager.MODE_ALLOWED) {
                return false;
            }
            return getServiceGuarded().deleteStoredConversation(callingPkg, conversationId);
        }

不过幸运的是在AppOpsService.java中也提供了修改权限的接口:修改某个 App 的某项权限的函数是 setMode(),其中就是修改成员变量 mUidOps。mUidOps 是一个List 保存了某个package对应的所有权限的mode (允许,忽略),具体代码如下:

@Override
public void setMode(int code, int uid, String packageName, int mode) {
    verifyIncomingUid(uid);
    verifyIncomingOp(code);
    ArrayList<Callback> repCbs = null;
    code = AppOpsManager.opToSwitch(code);
    synchronized (this) {
        Op op = getOpLocked(code, uid, packageName, true);
        if (op != null) {
            if (op.mode != mode) {
                op.mode = mode;
                ArrayList<Callback> cbs = mOpModeWatchers.get(code);
                if (cbs != null) {
                    if (repCbs == null) {
                        repCbs = new ArrayList<Callback>();
                    }
                    repCbs.addAll(cbs);
                }
                cbs = mPackageModeWatchers.get(packageName);
                if (cbs != null) {
                    if (repCbs == null) {
                        repCbs = new ArrayList<Callback>();
                    }
                    repCbs.addAll(cbs);
                }
                if (mode == AppOpsManager.MODE_ALLOWED) {
                    // If going into the default mode, prune this op
                    // if there is nothing else interesting in it.
                    if (op.time == 0 && op.rejectTime == 0) {
                        Ops ops = getOpsLocked(uid, packageName, false);
                        if (ops != null) {
                            ops.remove(op.op);
                            if (ops.size() <= 0) {
                                HashMap<String, Ops> pkgOps = mUidOps.get(uid);
                                if (pkgOps != null) {
                                    pkgOps.remove(ops.packageName);
                                    if (pkgOps.size() <= 0) {
                                        mUidOps.remove(uid);
                                    }
                                }
                            }
                        }
                    }
                }
                scheduleWriteNowLocked();
            }
        }
    }
    if (repCbs != null) {
        for (int i=0; i<repCbs.size(); i++) {
            try {
                repCbs.get(i).mCallback.opChanged(code, packageName);
            } catch (RemoteException e) {
            }
        }
    }
}

AppOpsManager 是一个管理类来和 AppOpsService 通信,两者关联起来的代码如下:

/**
* Common implementation of Context API, which provides the base
* context object for Activity and other application components.
*/
class ContextImpl extends Context {

registerService(WINDOW_SERVICE, new ServiceFetcher() {
                Display mDefaultDisplay;
                public Object getService(ContextImpl ctx) {
                    Display display = ctx.mDisplay;
                    if (display == null) {
                        if (mDefaultDisplay == null) {
                            DisplayManager dm = (DisplayManager)ctx.getOuterContext().
                                    getSystemService(Context.DISPLAY_SERVICE);
                            mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
                        }
                        display = mDefaultDisplay;
                    }
                    return new WindowManagerImpl(display);
                }});

.....}

他的函数实现比较简单,重点是把控制转移到 AppOpsService 就可以了。例如 noteOperation() 和 setMode() 在 AppOpsManager 里面调用他们的函数是 noteOp() 和 setMode(),代码如下:

public int noteOp(int op, int uid, String packageName) {
    try {
        int mode = mService.noteOperation(op, uid, packageName);
        if (mode == MODE_ERRORED) {
            throw new SecurityException("Operation not allowed");
        }
        return mode;
    } catch (RemoteException e) {
    }
    return MODE_IGNORED;
}

public void setMode(int code, int uid, String packageName, int mode) {
    try {
        mService.setMode(code, uid, packageName, mode);
    } catch (RemoteException e) {
    }
}

OK,到这里我们应该就能有解决方法了,虽然接口没有公开,但我们在apk中利用反射来调用AppOpsManager,再利用setMode方法来给自己的应用打开权限,代码如下:

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_SERVICES).uid;

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

使用起起来也很方便:

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

注意还别忘:

<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.WRITE_SMS" />
时间: 2024-08-05 12:02:13

android 4.4删除短信的相关文章

Android开发删除短信

本人一直有一个需求,想要手机自动拦截黑名单里联系人的信息并自动删除这些短信,手机管家之类的软件可以拦截但是没找到能删除这些短信的,于是就萌生了想自己写一个android软件的想法. 加上物联网的兴起,安卓设备开发肯定前景很好. 随后又得知 安卓开发可以用java语言,就用eclipse,呵呵,本人还是有些java基础的,于是乎,本人就开始扑腾了. 把eclipse sdk adt搭建好之后,本人就开始了开发这款自动拦截并删除黑名单短信的软件.都已经实现. 在编写过程中,删除短信的模块倒是坑了我挺

android删除短信

代码如下: //删除短信 getContentResolver().delete(Uri.parse("content://sms/#"),"address=?", new String[]{"10001"}); getContentResolver().delete(Uri.parse("content://sms/#"),"body=?", new String[]{"10001"}

Android 删除短信

1.删除短信的函数,一条一条的删除所有短信 /* * Delete all SMS one by one */ public void deleteSMS() { try { ContentResolver CR = getContentResolver(); // Query SMS Uri uriSms = Uri.parse("content://sms/sent"); Cursor c = CR.query(uriSms, new String[] { "_id&qu

Android接收和发送短信

每一部手机都具有短信接收和发送功能,下面我们通过代码来实现接收和发送短信功能. 一.接收短信 1.创建内部广播接收器类,接收系统发出的短信广播 2.从获得的内容中解析出短信发送者和短信内容 3.在Activity中注册广播 4.添加接收短信权限 下面放上具体的代码 activity_main.xml文件用于显示短信发送者号码和显示短信内容 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout

Android 2.3发短信详细流程

在android中,APP通过SmsManager.java一系列方法实现发送短信的功能,而发送的内容有很很多种,比如sendTextMessage.sendMultipartTextMessage.sendDataMessage等等,在这篇文章里我们就以其中一个为例阐述发送短信的完整流程,如果有不对的地方,请大家指正,一起学习. 1. 起点:SmsManager.java (frameworks/base/telephony/java/android/telephony/SmsManager.

【转】如何向Android模拟器打电话发短信

转载地址:http://hi.baidu.com/jeremylai/item/420f9c9fe4881fccb62531f7 1. 启动Android Emulator, 查看标题栏找出端口.一般是android emulator(5554),其中5554就是端口. 2. 打开命令行,输入telnet localhost 5554.程序将会连接到android emulator,控制台会返回 Android Console: type ‘help’ for a list of command

android: 接收和发送短信

8.2    接收和发送短信 收发短信应该是每个手机最基本的功能之一了,即使是许多年前的老手机也都会具备这 项功能,而 Android 作为出色的智能手机操作系统,自然也少不了在这方面的支持.每个 Android 手机都会内置一个短信应用程序,使用它就可以轻松地完成收发短信的操作,如 图 8.4 所示. 图   8.4 不过作为一名开发者,仅仅满足于此显然是不够的.你要知道,Android 还提供了一系 列的 API,使得我们甚至可以在自己的应用程序里接收和发送短信.也就是说,只要你有足 够的信

向android模拟器打电话发短信的简单方法

在开发android应用程序时,有时候需要测试一下向android手机拨打电话发送短信时该应用程序的反应.譬如编写一个广播接收器,来提示用户有短信收到或者处理短信,就需要向该手机发送短信来进行测试.这里介绍一种简单的向android模拟器打电话发短信的方法. 该方法利用了eclipse ADT的DDMS来实现,首先点击打开DDMS,在eclipse界面的右上角,如图: 如果找不到,就点左边的图标,再点击others就会看到. 打开之后,在界面的左边中部会看见有一个Emulator Control

手机删除短信如何恢复?简单恢复小办法

手机删除短信如何恢复?具体的恢复小办法.很多的时候现在的人都是使用手机社交软件去进行一些交流的,那也并不是意味着我们用不到我们的手机短信了,因为有的时候我们还是会使用到我们的手机短信的. 那么我们该怎么去恢复我们的手机短信呢?若是我们的手机短信被我们删除了或者是误删了的话,我们是不是可以通过一些方法去找回我们的手机短信呢?若是你不清楚的话,我们可以一起去看看具体的恢复方法. 1.可以在过程开始之前需要我们在电脑上浏览器上下载安装"互盾安卓恢复大师",安装结束后用数据线将电脑和丢失数据手