夕游记 發表於 2023-11-2 20:55:00

Android系统开发 AppOpsManager 应用权限管理

<p>                      本文来自博客园,作者:观心静&nbsp;,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/17374096.html</p>
<div>                      本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。</div>
<h1><span style="color: rgba(22, 145, 121, 1)">前言</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">   AppOpsManager是应用权限管理器,负责控制应用权限设置。appops是在现有权限机制上新增的一套权限管理机制,主要针对一些高危的非必须系统应用的权限。</span></p>
<p><span style="color: rgba(0, 0, 0, 1)"><strong>&nbsp; &nbsp; &nbsp; &nbsp;请注意!调用AppOpsManager需要系统级权限</strong>。所以此API通常不适用于第三方应用程序开发人员; 大多数功能仅适用于系统应用程序。 通过<code>Context.getSystemService</code>与<code>Context.APP_OPS_SERVICE</code>获取它的一个实例。</span></p>
<h1><span style="background-color: rgba(255, 255, 255, 1); color: rgba(22, 145, 121, 1)">常量</span></h1>
<h2><span style="color: rgba(35, 111, 161, 1)">mode参数</span></h2>
<pre class="highlighter-hljs" data-dark-theme="true"><code>/**
* 允许权限
*/
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;</code></pre>
<h2><span style="color: rgba(35, 111, 161, 1)">可申请的权限列表</span></h2>
<pre class="language-cpp highlighter-hljs" data-dark-theme="true"><code>/** 访问粗略的位置信息。*/
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";</code></pre>
<h1><span style="color: rgba(22, 145, 121, 1)">检查权限</span></h1>
<h2><span style="color: rgba(35, 111, 161, 1)">checkOp</span></h2>
<pre class="highlighter-hljs" data-dark-theme="true"><code>/**
* 检查权限
* 这个参数请参考 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
    }
}</code></pre>
<h2><span style="color: rgba(35, 111, 161, 1)">checkOpNoThrow&nbsp;</span></h2>
<p><span style="color: rgba(0, 0, 0, 1)">此方法不会抛出异常,所以不需要做异常捕获,其他部分与上面的差不多</span></p>
<pre class="highlighter-hljs" data-dark-theme="true"><code>/**
* 检查权限
* 这个参数请参考 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
}</code></pre>
<h1><span style="color: rgba(22, 145, 121, 1)">设置权限</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">验证相关代码的时候有一些疑惑。在设置-应用-权限管理,如果这个应用一次都没有授权过(安装授权),那么权限还是能设置的,但是在设置-应用-权限管理不会发生变化(实际上还是有授权的)。但是,如果你执行过安装授权,那么这个地方就会有权限变化了。</span></p>
<h2><span style="color: rgba(35, 111, 161, 1)">setMode</span></h2>
<p><span style="color: rgba(0, 0, 0, 1)">这里的setMode可能会爆红,在Android studio中,kotlin上可以直接编译过去,但是java可能不行,需要反射</span></p>
<pre class="highlighter-hljs" data-dark-theme="true"><code>/**
* 设置权限
* 这个参数请参考 AppOpsManager.OPSTR_CAMERA 这些常量
* 这个参数请参考 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()
    }
}</code></pre>
<p><span style="color: rgba(0, 0, 0, 1)">上面的代码使用例子</span></p>
<pre class="highlighter-hljs" data-dark-theme="true"><code>//检查权限
val permissionsState = PermissionsHelp.checkPermissions(this, "xxx.xxx.xxx", AppOpsManager.OPSTR_CAMERA)
//设置权限
PermissionsHelp.setPermissions(this, "xxx.xxx.xxx", AppOpsManager.OPSTR_CAMERA, AppOpsManager.MODE_ALLOWED)</code></pre>
<h1><span style="color: rgba(22, 145, 121, 1)">监听权限的变化</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">代码</span></p>
<pre class="highlighter-hljs" data-dark-theme="true"><code>/**
* 请参考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}")
            }
      })
}</code></pre>
<p><span style="color: rgba(0, 0, 0, 1)">请注意,如果不需要监听了,请调用<span style="color: rgba(186, 55, 42, 1)"><strong>stopWatchingMode</strong></span>方法取消监听</span></p>
<h1><span style="color: rgba(22, 145, 121, 1)">一次性权限</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">一次性权限是在Android 10才有的概念(这个请自行百度了解)。 AppOpsManager 通过2个方法,startOp与finishOp实现了一次性权限功能。</span></p>
<p>&nbsp;</p>
<p><span style="color: rgba(0, 0, 0, 1)">&nbsp; 此功能暂时无法给出,有一些问题,我不太明白需要什么样的条件才能调用startOp与finishOp并且发挥作用。我看了源码也不太明白,待后续有时间研究。</span></p>
<p>&nbsp;</p>
<h1><span style="color: rgba(22, 145, 121, 1)">验证包名是否属于UID</span></h1>
<p>代码</p>
<pre class="highlighter-hljs" data-dark-theme="true"><code>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
    }
}</code></pre>
<p>使用</p>
<pre class="highlighter-hljs" data-dark-theme="true"><code>val uid: Int = this.getPackageManager().getPackageUid("你的包名", 0)
AppOpsManagerUtil.checkPackage(this, "你的包名", uid)</code></pre>
<h1><span style="color: rgba(22, 145, 121, 1)">检索所有应用程序的当前操作状态</span></h1>
<p><strong><span style="color: rgba(186, 55, 42, 1)">请注意!</span></strong><span style="color: rgba(186, 55, 42, 1)"><span style="color: rgba(0, 0, 0, 1)">getPackagesForOps() 方法并不是返回全部应用的权限信息,而是你使用AppOpsManager 操作过的应用它才会返回。如果你是在Android原生设置-应用-权限中的操作,它是不会有记录的。</span></span></p>
<p><span style="color: rgba(0, 0, 0, 1)">这里的很多方法与类可能都会爆红,在Android studio中,kotlin上可以不用管爆红直接编译过去,但是java可能不行,需要反射。</span></p>
<pre class="highlighter-hljs" data-dark-theme="true"><code>/**
* 检索所有应用程序的当前操作状态。
*/
fun getPackagesForOps(context: Context) {
    try {
      val appOpsManager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
         val list : List&lt;AppOpsManager.PackageOps&gt; = appOpsManager.getPackagesForOps(intArrayOf(AppOpsManager.OP_CAMERA))
      list.forEach {
            Log.e("zh", "PackageName = ${it.getPackageName()} Uid = ${it.getUid()}")
            //保存有关这个应用程序的全部权限类型的信息
            (it.getOps() as List&lt;AppOpsManager.OpEntry&gt;).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()
    }
}</code></pre>
<p><span style="color: rgba(0, 0, 0, 1)">结果</span></p>
<pre class="language-java highlighter-hljs" data-dark-theme="true"><code>2023-11-04 16:50:25.017 20730-20730 zh                        EPackageName = com.zh.demo Uid = 10118
2023-11-04 16:50:25.017 20730-20730 zh                        EOp = 26
2023-11-04 16:50:25.017 20730-20730 zh                        EMode = 0
2023-11-04 16:50:25.017 20730-20730 zh                        ETime = 1699080166754
2023-11-04 16:50:25.017 20730-20730 zh                        EisRunning = false</code></pre>
<p>&nbsp;</p>
<p><span style="color: rgba(22, 145, 121, 1)">end</span></p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="text-align: center">
    <p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/17374096.html </p>
    <div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/17374096.html
頁: [1]
查看完整版本: Android系统开发 AppOpsManager 应用权限管理