zoukankan      html  css  js  c++  java
  • 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;
        }
    }
    复制代码

    然后在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" />












  • 相关阅读:
    1212321
    123123
    好好干活
    VXLAN学习之路-结合VRF在Linux中实践VXLAN网络
    123456
    期望
    123456
    123456
    Jenkins管理员密码忘记修改操作
    Jenkins 安装与部署详细教程
  • 原文地址:https://www.cnblogs.com/huihuizhang/p/6062803.html
Copyright © 2011-2022 走看看