Android面试500:008android 4.4删除短信

xiaoxiao2021-02-28  46

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" />
转载请注明原文地址: https://www.6miu.com/read-2632299.html

最新回复(0)