观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

                      本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/17374096.html

                      本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

前言

   AppOpsManager是应用权限管理器,负责控制应用权限设置。appops是在现有权限机制上新增的一套权限管理机制,主要针对一些高危的非必须系统应用的权限。

       请注意!调用AppOpsManager需要系统级权限。所以此API通常不适用于第三方应用程序开发人员; 大多数功能仅适用于系统应用程序。 通过Context.getSystemServiceContext.APP_OPS_SERVICE获取它的一个实例。

常量

mode参数

/**
 * 允许权限
 */
public static final int MODE_ALLOWED = 0;

/**
 * 忽略权限
 */
public static final int MODE_IGNORED = 1;

/**
 * 拒绝权限,并且会引起报错
 */
public static final int MODE_ERRORED = 2;

/**
 * 默认,通常不使用这种模式;它应该只在appop权限下使用,调用者必须显式地检查它并处理它。
 */
public static final int MODE_DEFAULT = 3;

/**
 * 特殊模式只允许应用处于前台时,当设置此模式时,当被检查的应用程序当前处于前台时返回{@link MODE_ALLOWED},否则返回{@link MODE_IGNORED}。
 * @hide
 */
public static final int MODE_FOREGROUND = 4;

可申请的权限列表

/** 访问粗略的位置信息。*/
public static final String OPSTR_COARSE_LOCATION = "android:coarse_location";
/** 访问精细的位置信息。*/
public static final String OPSTR_FINE_LOCATION ="android:fine_location";
/** 持续监控位置数据。*/
public static final String OPSTR_MONITOR_LOCATION = "android:monitor_location";
/** 在相对较高的功率要求下持续监控位置数据。*/
public static final String OPSTR_MONITOR_HIGH_POWER_LOCATION = "android:monitor_location_high_power";
/** 访问统计数据权限 */
public static final String OPSTR_GET_USAGE_STATS = "android:get_usage_stats";
/** 激活VPN连接,无需用户干预 */
public static final String OPSTR_ACTIVATE_VPN = "android:activate_vpn";
/** 允许应用程序读取用户的联系人数据. */
public static final String OPSTR_READ_CONTACTS = "android:read_contacts";
/** 允许应用程序写入用户的联系人数据. */
public static final String OPSTR_WRITE_CONTACTS = "android:write_contacts";
/** 允许程序读取用户的通话记录. */
public static final String OPSTR_READ_CALL_LOG = "android:read_call_log";
/** 允许应用程序写入用户的呼叫记录. */
public static final String OPSTR_WRITE_CALL_LOG = "android:write_call_log";
/** 允许应用程序读取用户的日历数据. */
public static final String OPSTR_READ_CALENDAR = "android:read_calendar";
/** 允许应用程序写入用户的日历数据. */
public static final String OPSTR_WRITE_CALENDAR = "android:write_calendar";
/** 允许应用程序发起一个电话呼叫. */
public static final String OPSTR_CALL_PHONE = "android:call_phone";
/** 允许程序读取短信. */
public static final String OPSTR_READ_SMS = "android:read_sms";
/** 允许应用程序接收短信. */
public static final String OPSTR_RECEIVE_SMS = "android:receive_sms";
/** 允许应用程序接收彩信. */
public static final String OPSTR_RECEIVE_MMS = "android:receive_mms";
/** 允许应用程序接收WAP推送消息. */
public static final String OPSTR_RECEIVE_WAP_PUSH = "android:receive_wap_push";
/** 允许应用程序发送短信. */
public static final String OPSTR_SEND_SMS = "android:send_sms";
/** 需要能够访问摄像设备. */
public static final String OPSTR_CAMERA = "android:camera";
/** 需要能够访问麦克风设备. */
public static final String OPSTR_RECORD_AUDIO = "android:record_audio";
/** 需要访问电话状态相关信息. */
public static final String OPSTR_READ_PHONE_STATE = "android:read_phone_state";
/** 需要访问电话状态相关信息. */
public static final String OPSTR_ADD_VOICEMAIL = "android:add_voicemail";
/** 通过VOIP或WiFi访问SIP呼叫的api */
public static final String OPSTR_USE_SIP = "android:use_sip";
/** 访问用于转移呼出的api */
public static final String OPSTR_PROCESS_OUTGOING_CALLS = "android:process_outgoing_calls";
/** 使用指纹API. */
public static final String OPSTR_USE_FINGERPRINT = "android:use_fingerprint";
/** 接入身体传感器,如心率等. */
public static final String OPSTR_BODY_SENSORS = "android:body_sensors";
/** 读取先前收到的蜂窝广播消息. */
public static final String OPSTR_READ_CELL_BROADCASTS = "android:read_cell_broadcasts";
/** 将模拟位置注入系统. */
public static final String OPSTR_MOCK_LOCATION = "android:mock_location";
/** 读取外部存储器. */
public static final String OPSTR_READ_EXTERNAL_STORAGE = "android:read_external_storage";
/** 写入外部存储器. */
public static final String OPSTR_WRITE_EXTERNAL_STORAGE = "android:write_external_storage";
/** 需要悬浮窗. */
public static final String OPSTR_SYSTEM_ALERT_WINDOW = "android:system_alert_window";
/** 需要写入、修改、更新系统设置. */
public static final String OPSTR_WRITE_SETTINGS = "android:write_settings";

检查权限

checkOp

/**
 * 检查权限
 * [op]这个参数请参考 AppOpsManager.OPSTR_CAMERA 这些常量
 * 返回值请参考 AppOpsManager.MODE_ALLOWED 这些常量
 */
fun checkPermissions(context: Context, packageName: String, op: String): Int {
    try {
        val uid: Int = context.getPackageManager().getPackageUid(packageName, 0)
        val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
        val result = appOpsManager.checkOp(op, uid, packageName)
        return result
    } catch (e: SecurityException) {
        e.printStackTrace()
        return -1
    }
}

checkOpNoThrow 

此方法不会抛出异常,所以不需要做异常捕获,其他部分与上面的差不多

/**
 * 检查权限
 * [op]这个参数请参考 AppOpsManager.OPSTR_CAMERA 这些常量
 * 返回值请参考 AppOpsManager.MODE_ALLOWED 这些常量
 */
fun checkPermissions(context: Context, packageName: String, op: String): Int {
    val uid: Int = context.getPackageManager().getPackageUid(packageName, 0)
    val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
    val result = appOpsManager.checkOpNoThrow(op, uid, packageName)
    return result
}

设置权限

验证相关代码的时候有一些疑惑。在设置-应用-权限管理,如果这个应用一次都没有授权过(安装授权),那么权限还是能设置的,但是在设置-应用-权限管理不会发生变化(实际上还是有授权的)。但是,如果你执行过安装授权,那么这个地方就会有权限变化了。

setMode

这里的setMode可能会爆红,在Android studio中,kotlin上可以直接编译过去,但是java可能不行,需要反射

/**
 * 设置权限
 * [op]这个参数请参考 AppOpsManager.OPSTR_CAMERA 这些常量
 * [mode]这个参数请参考 AppOpsManager.MODE_ALLOWED 这些常量
 */
fun setPermissions(context: Context, packageName: String, op: String, mode: Int) {
    try {
        val uid: Int = context.getPackageManager().getPackageUid(packageName, 0)
        val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
        appOpsManager.setMode(op, uid, packageName, mode)
    } catch (e: PackageManager.NameNotFoundException) {
        e.printStackTrace()
    }
}

上面的代码使用例子

//检查权限
val permissionsState = PermissionsHelp.checkPermissions(this, "xxx.xxx.xxx", AppOpsManager.OPSTR_CAMERA)
//设置权限
PermissionsHelp.setPermissions(this, "xxx.xxx.xxx", AppOpsManager.OPSTR_CAMERA, AppOpsManager.MODE_ALLOWED)

监听权限的变化

代码

/**
 * [op] 请参考AppOpsManager.OPSTR_CAMERA 这些常量
 */
fun startWatchingMode(context: Context, packageName: String, op: String) {
    val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
    appOpsManager.startWatchingMode(op, packageName, object : AppOpsManager.OnOpChangedListener {
            override fun onOpChanged(op2: String?, packageName2: String?) {
                val uid: Int = context.getPackageManager().getPackageUid(packageName2, 0)
                val result = appOpsManager.checkOpNoThrow(op2, uid, packageName2)
                Log.e("zh", "权限发生变更 op = ${op2}  packageName = ${packageName2} ${result}")
            }
        })
}

请注意,如果不需要监听了,请调用stopWatchingMode方法取消监听

一次性权限

一次性权限是在Android 10才有的概念(这个请自行百度了解)。 AppOpsManager 通过2个方法,startOp与finishOp实现了一次性权限功能。

 

  此功能暂时无法给出,有一些问题,我不太明白需要什么样的条件才能调用startOp与finishOp并且发挥作用。我看了源码也不太明白,待后续有时间研究。

 

验证包名是否属于UID

代码

fun checkPackage(context: Context, packageName: String, uid:Int):Boolean {
    try {
        val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
        appOpsManager.checkPackage(uid, packageName)
        Log.e("tag", "uid 与 packageName 匹配", )
        return true
    } catch (e: SecurityException) {
        e.printStackTrace()
        Log.e("tag", "uid 与 packageName 不匹配", )
        return false
    }
}

使用

val uid: Int = this.getPackageManager().getPackageUid("你的包名", 0)
AppOpsManagerUtil.checkPackage(this, "你的包名", uid)

检索所有应用程序的当前操作状态

请注意!getPackagesForOps() 方法并不是返回全部应用的权限信息,而是你使用AppOpsManager 操作过的应用它才会返回。如果你是在Android原生设置-应用-权限中的操作,它是不会有记录的。

这里的很多方法与类可能都会爆红,在Android studio中,kotlin上可以不用管爆红直接编译过去,但是java可能不行,需要反射。

/**
 * 检索所有应用程序的当前操作状态。
 */
fun getPackagesForOps(context: Context) {
    try {
        val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
         val list : List<AppOpsManager.PackageOps> = appOpsManager.getPackagesForOps(intArrayOf(AppOpsManager.OP_CAMERA))
        list.forEach {
            Log.e("zh", "PackageName = ${it.getPackageName()} Uid = ${it.getUid()}")
            //保存有关这个应用程序的全部权限类型的信息
            (it.getOps() as List<AppOpsManager.OpEntry>).forEach{
                Log.e("zh", "Op = ${it.getOp()}")
                Log.e("zh", "Mode = ${it.getMode()}")
                Log.e("zh", "Time = ${it.getTime()}")
                Log.e("zh", "isRunning = ${it.isRunning()}")
            }
        }
    } catch (e: SecurityException) {
        e.printStackTrace()
    }
}

结果

2023-11-04 16:50:25.017 20730-20730 zh                        E  PackageName = com.zh.demo Uid = 10118
2023-11-04 16:50:25.017 20730-20730 zh                        E  Op = 26
2023-11-04 16:50:25.017 20730-20730 zh                        E  Mode = 0
2023-11-04 16:50:25.017 20730-20730 zh                        E  Time = 1699080166754
2023-11-04 16:50:25.017 20730-20730 zh                        E  isRunning = false

 

end

posted on 2023-11-02 20:55  观心静  阅读(1695)  评论(0编辑  收藏  举报