博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android 4.4删除短信
阅读量:5258 次
发布时间:2019-06-14

本文共 11371 字,大约阅读时间需要 37 分钟。

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

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                     threadId = c.getLong(1);                    int result = CR.delete(Uri                            .parse("content:///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//services/java/com/android/server/AppOpsService.java中android系统添加了权限检查的函数

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

@Overridepublic 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;    }}@Overridepublic 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 ingPkg, 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(ingPkg, messageUri);        }        @Override        public boolean deleteStoredConversation(String ingPkg,  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(ingPkg, conversationId);        }

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

@Overridepublic void setMode(int code, int uid, String packageName, int mode) {    verifyIncomingUid(uid);    verifyIncomingOp(code);    ArrayList
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
cbs = mOpModeWatchers.get(code); if (cbs != null) { if (repCbs == null) { repCbs = new ArrayList
(); } repCbs.addAll(cbs); } cbs = mPackageModeWatchers.get(packageName); if (cbs != null) { if (repCbs == null) { repCbs = new ArrayList
(); } 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
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

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

/**

* Common implementation of Context API, which provides the
* context object for Activity and other lication 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 = OpsManager.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(OpsManager, 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 = OpsManager.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 = OpsManagerClass.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(OpsManager, 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(); ......

注意还别忘:

转载于:https://www.cnblogs.com/android-blogs/p/4959167.html

你可能感兴趣的文章
A-Softmax的总结及与L-Softmax的对比——SphereFace
查看>>
关于软件盘覆盖住布局
查看>>
Unity3D 控制物体移动、旋转、缩放
查看>>
UVa 11059 最大乘积
查看>>
UVa 12545 比特变换器
查看>>
数组分割问题求两个子数组的和差值的小
查看>>
10个著名的思想实验1
查看>>
06-js的逻辑结构
查看>>
composer 报 zlib_decode(): data error
查看>>
03_java基础(一)之计算机应用知识普及
查看>>
JAVA基础面试小结(知识点简要)
查看>>
MFC技术积累——基于MFC对话框类的那些事儿
查看>>
HDU 4884 TIANKENG’s rice shop (模拟)
查看>>
java Spring 各版本jar包下载地址
查看>>
NTP服务器和国内可用的NTP地址
查看>>
Linux 修改SSH端口及禁用ROOT远程SSH登陆
查看>>
Apache Lens —— 统计数据分析查询接口
查看>>
Weka Experimenter(实验者界面) 简解
查看>>
回忆Ajax ๑乛◡乛๑
查看>>
打印二叉树中所有分支之和等于某个数
查看>>