AppOpsMananger source

AppOpsMananger 

之前在开发一个统计应用使用情况功能,自然用到了UsageState。想要从UsageState中读到数据,需要我们手动添加权限。
 
  Log.i("UsageStateUtil", "跳转到软件使用情况权限设置");
            Intent intent = new Intent("android.settings.USAGE_ACCESS_SETTINGS");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            this.context.startActivity(intent);
            throw new Exception("读取应用使用情况,需要权限");
但是我们这悄无声息的统计应用使用,就不好弹出来要求用户去授予权限了。本着这个想法,我觉得翻翻Settings应用,找找这个权限界面到底是怎么授予权限的。
 
 

行动

很快找到关于UsageState的代码:
 
 
 
最后定位在UsageAccessSettings.java中
 
 @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
       ……

        // Check if we need to do any work.
        if (pe.appOpMode != newMode) {
            if (newMode != AppOpsManager.MODE_ALLOWED) {
                // Turning off the setting has no warning.
                setNewMode(pe, newMode);
                return true;
            }          																		  ……																		  ……
 
最后调用了setMode
 
void setNewMode(PackageEntry pe, int newMode) {
        mAppOpsManager.setMode(AppOpsManager.OP_GET_USAGE_STATS,
        pe.packageInfo.applicationInfo.uid, pe.packageName, newMode);
        pe.appOpMode = newMode;
    }


不用多想,这种授予权限的API自然不会随便就能用的。系统权限!我的APP 就可以有系统签名啊,哈哈哈哈~
 
 

惊奇发现

这么多权限!?
 
 /** @hide No operation specified. */
    public static final int OP_NONE = -1;
    /** @hide Access to coarse location information. */
    public static final int OP_COARSE_LOCATION = 0;
    /** @hide Access to fine location information. */
    public static final int OP_FINE_LOCATION = 1;
    /** @hide Causing GPS to run. */
    public static final int OP_GPS = 2;
    /** @hide */
    public static final int OP_VIBRATE = 3;
    /** @hide */
    public static final int OP_READ_CONTACTS = 4;
    /** @hide */
    public static final int OP_WRITE_CONTACTS = 5;
    /** @hide */
    public static final int OP_READ_CALL_LOG = 6;
    /** @hide */
    public static final int OP_WRITE_CALL_LOG = 7;
    /** @hide */
    public static final int OP_READ_CALENDAR = 8;
    /** @hide */
    public static final int OP_WRITE_CALENDAR = 9;
    /** @hide */
    public static final int OP_WIFI_SCAN = 10;
    /** @hide */
    public static final int OP_POST_NOTIFICATION = 11;
    /** @hide */
    public static final int OP_NEIGHBORING_CELLS = 12;
    /** @hide */
    public static final int OP_CALL_PHONE = 13;
    /** @hide */
    public static final int OP_READ_SMS = 14;
    /** @hide */
    public static final int OP_WRITE_SMS = 15;
    /** @hide */
    public static final int OP_RECEIVE_SMS = 16;
    /** @hide */
    public static final int OP_RECEIVE_EMERGECY_SMS = 17;
    /** @hide */
    public static final int OP_RECEIVE_MMS = 18;
    /** @hide */
    public static final int OP_RECEIVE_WAP_PUSH = 19;
    /** @hide */
    public static final int OP_SEND_SMS = 20;
    /** @hide */
    public static final int OP_READ_ICC_SMS = 21;
    /** @hide */
    public static final int OP_WRITE_ICC_SMS = 22;
    /** @hide */
    public static final int OP_WRITE_SETTINGS = 23;
    /** @hide */
    public static final int OP_SYSTEM_ALERT_WINDOW = 24;
    /** @hide */
    public static final int OP_ACCESS_NOTIFICATIONS = 25;
    /** @hide */
    public static final int OP_CAMERA = 26;
    /** @hide */
    public static final int OP_RECORD_AUDIO = 27;
    /** @hide */
    public static final int OP_PLAY_AUDIO = 28;
    /** @hide */
    public static final int OP_READ_CLIPBOARD = 29;
    /** @hide */
    public static final int OP_WRITE_CLIPBOARD = 30;
    /** @hide */
    public static final int OP_TAKE_MEDIA_BUTTONS = 31;
    /** @hide */
    public static final int OP_TAKE_AUDIO_FOCUS = 32;
    /** @hide */
    public static final int OP_AUDIO_MASTER_VOLUME = 33;
    /** @hide */
    public static final int OP_AUDIO_VOICE_VOLUME = 34;
    /** @hide */
    public static final int OP_AUDIO_RING_VOLUME = 35;
    /** @hide */
    public static final int OP_AUDIO_MEDIA_VOLUME = 36;
    /** @hide */
    public static final int OP_AUDIO_ALARM_VOLUME = 37;
    /** @hide */
    public static final int OP_AUDIO_NOTIFICATION_VOLUME = 38;
    /** @hide */
    public static final int OP_AUDIO_BLUETOOTH_VOLUME = 39;
    /** @hide */
    public static final int OP_WAKE_LOCK = 40;
    /** @hide Continually monitoring location data. */
    public static final int OP_MONITOR_LOCATION = 41;
    /** @hide Continually monitoring location data with a relatively high power request. */
    public static final int OP_MONITOR_HIGH_POWER_LOCATION = 42;
    /** @hide Retrieve current usage stats via {@link UsageStatsManager}. */
    public static final int OP_GET_USAGE_STATS = 43;
    /** @hide */
    public static final int OP_MUTE_MICROPHONE = 44;
    /** @hide */
    public static final int OP_TOAST_WINDOW = 45;
    /** @hide Capture the device's display contents and/or audio */
    public static final int OP_PROJECT_MEDIA = 46;
    /** @hide Activate a VPN connection without user intervention. */
    public static final int OP_ACTIVATE_VPN = 47;
    /** @hide */
    public static final int _NUM_OP = 48;

 
不看不知道,一看吓一跳。这么多权限可以通过setMode来设置,这可把我高兴坏了。
 
二话不说,先试试我需要的权限:
 
    /** @hide Retrieve current usage stats via {@link UsageStatsManager}. */
    public static final int OP_GET_USAGE_STATS = 43;
 
 
 String pkg = intent.getStringExtra("packageName");

            int newMode = intent.getIntExtra("mode", AppOpsManager.MODE_ALLOWED);
            int code = intent.getIntExtra("code", -1);

         
            AppOpsManager aom;

            try {
                aom = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);

               

                Class<AppOpsManager> appOpsManagerClass;

                appOpsManagerClass = AppOpsManager.class;

                // int code, int uid, String packageName, int mode
                Method method = appOpsManagerClass.getMethod("setMode", int.class, int.class, String.class, int.class);
                method.setAccessible(true);
                try {
                    int uid = 0;

                    PackageManager manager = context.getPackageManager();
                    ApplicationInfo ai = manager.getApplicationInfo(pkg, PackageManager.GET_META_DATA);

                    uid = ai.uid;

                   
                    method.invoke(aom, code, uid, pkg, newMode);

                    

                } catch (Exception e) {
                   
                }

            } catch (Exception e) {
               
            }

亲测可行!
 

冷水

查看用户应用使用情况权限的获取就这样拿到了,是不是其他的权限也可以?满怀希望地开始测试,结果却不是那样的。
 
整理的结果如下:
 
    验证AppOpsManager权限开启情况

设备:HUAWEI Mate7 -TL00;

Android 版本:6.0;


GPS 的权限:0,1,2,41,42-------------------》(该权限在设置应用中打开,手机管家也存在,页面一致)执行完页面无变化。(有禁用、提示、启用选项)




读写联系人权限:4,5(该权限在设置应用中打开,手机管家也存在,页面一致)执行完页面无变化。(有禁用、提示、启用选项)

 
……(所有权限选择中有禁用、提示、启用选项的,无法通过API来开启,受限于手机管家)



通知栏显示权限:11,25,38)11就可以开启通知栏权限,25,38无具体作用(该权限在设置应用中打开,手机管家也存在,页面一致)(没有有禁用、提示、启用选项,只  有一个开关)

悬浮窗权限:24; 可以开启(该权限在设置应用中打开,手机管家也存在,页面一致)(无有禁用、提示、启用选项,只有一个开关)

读取应用使用情况权限:43; 可以开启(该权限在设置应用中打开,手机管家也存在,页面一致)(无有禁用、提示、启用选项,只有一个开关)


自动启动权限:没有


受保护应用:没有
 
 
 
------------------------------------------------------我是分隔符------------------------------------------------------------------------------
哪些个权限无法通过setMode 来管控,我的猜想是各个厂商对比较隐私的权限做了特殊处理,只有手机管家这样的预置软件才能进行管控。
 
 
但是如果权限管理的数据保存在一个地方,我们可以尝试修改这个文件即可获取到权限不是么?秉着这个想法,源码继续看下去:
 
 
/** @hide */
    public void setMode(int code, int uid, String packageName, int mode) {
        try {
            mService.setMode(code, uid, packageName, mode);
        } catch (RemoteException e) {
        }
    }
这个mSerivice 就是 final IAppOpsService mService; 一个通过Aidl通信的接口。
 
interface IAppOpsService {
    // These first methods are also called by native code, so must
    // be kept in sync with frameworks/native/include/binder/IAppOpsService.h
    int checkOperation(int code, int uid, String packageName);
    int noteOperation(int code, int uid, String packageName);
    int startOperation(IBinder token, int code, int uid, String packageName);
    void finishOperation(IBinder token, int code, int uid, String packageName);
    void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
    void stopWatchingMode(IAppOpsCallback callback);
    IBinder getToken(IBinder clientToken);

    // Remaining methods are only used in Java.
    int checkPackage(int uid, String packageName);
    List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
    List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
    void setMode(int code, int uid, String packageName, int mode);
    void resetAllModes();
    int checkAudioOperation(int code, int usage, int uid, String packageName);
    void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages);

    void setUserRestrictions(in Bundle restrictions, int userHandle);
    void removeUser(int userHandle);

}
/ These first methods are also called by native code, so must
    // be kept in sync with frameworks/native/include/binder/IAppOpsService.h
    int checkOperation(int code, int uid, String packageName);
    int noteOperation(int code, int uid, String packageName);
    int startOperation(IBinder token, int code, int uid, String packageName);
    void finishOperation(IBinder token, int code, int uid, String packageName);
    void startWatchingMode(int op, String packageName, IAppOpsCallback callback);
    void stopWatchingMode(IAppOpsCallback callback);
    IBinder getToken(IBinder clientToken);

    // Remaining methods are only used in Java.
    int checkPackage(int uid, String packageName);
    List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops);
    List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops);
    void setMode(int code, int uid, String packageName, int mode);
    void resetAllModes();
    int checkAudioOperation(int code, int usage, int uid, String packageName);
    void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages);

    void setUserRestrictions(in Bundle restrictions, int userHandle);
    void removeUser(int userHandle);

}
 
实现接口就是AppOpsService 的setMode
 
仔细看下来,主要是两个动作:readState() 和writeState(),顾名思义就是对于权限进行读取然后重新更新写入。
 
 
 
   File dataDir = Environment.getDataDirectory();
   File systemDir = new File(dataDir, "system");
 
修改的文件就是:new File(systemDir, "appops.xml")
 
 
将该文件打印出来,发现结果蛮清晰,但是有些权限的授予或者取消并不是都体现在此;
 
<Ops>
<package>
<package>
……
 
 
尝试修改该文件,但是并没有获取到对应的权限(这些就不做展示了,有兴趣自己来改写看看)
 
修改过的文件,过一段时间就会恢复成原来的内容;
 
 

结论

 这些我的猜想是:1.厂商修改过这个服务 ;2.权限管理文件appops.xml 可能有备份,并被其他服务监听;3.修改权限时会获取手机管家的权限部分的数据库,隐私权限的管理,系统会根据这部分数据库来管理。

这只是猜想,如果有哪些不完整或者错误的说法,希望大家指出。

对于权限管理的方案,如果大家有比较清晰、正确的思路,希望可以分享。

 
 
 
 
 
 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值