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;
}
}

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

        @Override
public int delete(String callingPkg, Uri uri, String selection, String[] selectionArgs) {
validateIncomingUri(uri);
uri = getUriWithoutUserId(uri);
if (enforceWritePermission(callingPkg, uri) != AppOpsManager.MODE_ALLOWED) {
return 0;
}
final String original = setCallingPackage(callingPkg);
try {
return ContentProvider.this.delete(uri, selection, selectionArgs);
} finally {
setCallingPackage(original);
}
}
    private int enforceWritePermission(String callingPkg, Uri uri) throws SecurityException {
enforceWritePermissionInner(uri);
if (mWriteOp != AppOpsManager.OP_NONE) {
return mAppOpsManager.noteOp(mWriteOp, Binder.getCallingUid(), callingPkg);
}
return AppOpsManager.MODE_ALLOWED;
}

不过幸运的是在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(APP_OPS_SERVICE, new ServiceFetcher() {
    public Object createService(ContextImpl ctx) {
    IBinder b = ServiceManager.getService(APP_OPS_SERVICE);
    IAppOpsService service = IAppOpsService.Stub.asInterface(b);
    return new AppOpsManager(ctx, service);
  }}); ..... }

他的函数实现比较简单,重点是把控制转移到 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" />
												

Android 4.4之后删除短信进行处理的更多相关文章

  1. Android 删除短信

    1.删除短信的函数,一条一条的删除所有短信 /* * Delete all SMS one by one */ public void deleteSMS() { try { ContentResol ...

  2. android 4.4删除短信

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

  3. android删除短信

    代码如下: //删除短信 getContentResolver().delete(Uri.parse("content://sms/#"),"address=?" ...

  4. Android的基本常用的短信操作

    1.调用系统发送短信界面(传入手机号码+短信内容) 2.隐藏发送短信(指定号码指定内容)(这里隐藏只是没有反写入数据库) 3.获得收件箱接收到的短信 4.Android屏蔽新短信通知提示信息:(Con ...

  5. Android手机上监听短信的两种方式

    Android手机上监听短信有两种方式: 1. 接受系统的短信广播,操作短信内容. 优点:操作方便,适合简单的短信应用. 缺点:来信会在状态栏显示通知信息. AndroidManifest.xml: ...

  6. 利用Android Lost通过互联网或短信远程控制安卓设备

    利用Android Lost通过互联网或短信远程控制安卓设备 作者:Jack Wallen| 杰克·瓦伦翻译:PurpleEndurer.2014-11-15第1版 使用智能手机要考虑的一个至关重要的 ...

  7. Android安卓电话拦截及短信过滤

    package com.focus.manager; import java.lang.reflect.Method; import Android .app.Activity; import And ...

  8. (转)[Android实例] 关于使用ContentObserver监听不到删除短信会话的解决方案

    最近做通讯录的项目,需要实时监听短信的删除,就用到了观察者ContentObserver,怪异的事情就此发生,当我删除一条短信的时候,可以监听到,但是,当我删除整条短信的时候,就无法监听到,查了很多资 ...

  9. Ztorg木马分析: 从Android root木马演变到短信吸血鬼

    本月第二次,Google 从官方应用商店 Google Play 中移除了伪装成合法程序的恶意应用.被移除的应用都属于名叫 Ztorg 的 Android 恶意程序家族.目前为止,发现的几十个新的Zt ...

随机推荐

  1. cocos2d-x游戏开发之烟花粒子效果

    //散烟花及“太”“棒”“了”效果 void mygame::playfire() { sprite *tai = sprite::create("tai.png"); tai-& ...

  2. spark streaming - kafka updateStateByKey 统计用户消费金额

    场景 餐厅老板想要统计每个用户来他的店里总共消费了多少金额,我们可以使用updateStateByKey来实现 从kafka接收用户消费json数据,统计每分钟用户的消费情况,并且统计所有时间所有用户 ...

  3. Fresco源码解析 - 创建一个ImagePipeline(一)

    在Fresco源码解析 - 初始化过程分析章节中, 我们分析了Fresco的初始化过程,两个initialize方法中都用到了 ImagePipelineFactory类. ImagePipeline ...

  4. angular Creating a Directive that Adds Event Listeners

    <span my-draggable>Drag ME</span> angular.module('dragModule', []) .directive('myDraggab ...

  5. thinphp讲解(三)——空操作、空控制器、跨控制器、命名空间

    一.“空操作”本质意思:一个对象(控制器)调用本身不存在的操作方法 一般网站处于安全考虑不给用户提示任何错误信息 在tp里面控制器controller.class.php里有个_call()方法 所以 ...

  6. 【RoR win32】新建rails项目找不到script/server的解决办法

    现象: D:\>rails new work/demo cd work/demo D:\work\demo>ruby script/server 这时显示出错: ruby: No such ...

  7. React+Node.js+Express+mongoskin+MongoDB

    首发:个人博客,更新&纠错&回复 采用React + Node.js + Express + mongoskin + MongoDB技术开发的一个示例,演示地址在这里,项目源码在这里. ...

  8. source insight资源

    http://www.cnblogs.com/Red_angelX/p/3713935.html https://github.com/redxu/sihook

  9. php版redis插件,SSDB数据库,增强型的Redis管理api实例

    php版redis插件,SSDB数据库,增强型的Redis管理api实例 SSDB是一套基于LevelDB存储引擎的非关系型数据库(NOSQL),可用于取代Redis,更适合海量数据的存储.另外,ro ...

  10. PHP获取不了React Native Fecth参数的解决办法是怎么样呢?

    fetch('https://mywebsite.com/endpoint/', { method: 'POST',headers: {'Accept': 'application/json','Co ...