所以,我也在寻找一个解决方案 - 有一个生物识别库,并且在 DM 中出现的报告很少有同样的问题 - 当相机“静音”时,FaceUnlock 在 Pixel 4 上不起作用
现在,现在仍然修复,但也许我的研究可以帮助某人。
1. 我检查了 PrivacyToggle 的新 API。
Android 12 引入了一种新SensorPrivacyManager
的 withsupportsSensorToggle()
方法 - 如果设备能够“静音”摄像头或麦克风,它会返回 TRUE。
val sensorPrivacyManager = applicationContext
.getSystemService(SensorPrivacyManager::class.java)
as SensorPrivacyManager
val supportsMicrophoneToggle = sensorPrivacyManager
.supportsSensorToggle(Sensors.MICROPHONE)
val supportsCameraToggle = sensorPrivacyManager
.supportsSensorToggle(Sensors.CAMERA)
如果你查看 SensorPrivacyManager,你会发现它提供了一些更有用的方法,所以我开发了下面的代码:
fun isCameraAccessible(): Boolean {
return !checkIsPrivacyToggled(SensorPrivacyManager.Sensors.CAMERA)
}
@SuppressLint("PrivateApi")
private fun checkIsPrivacyToggled(sensor: Int): Boolean {
val sensorPrivacyManager: SensorPrivacyManager =
appContext.getSystemService(SensorPrivacyManager::class.java)
if (sensorPrivacyManager.supportsSensorToggle(sensor)) {
val userHandleField = UserHandle::class.java.getDeclaredField("USER_CURRENT")
userHandleField.isAccessible = true
val userHandle = userHandleField.get(null) as Int
val m = SensorPrivacyManager::class.java.getDeclaredMethod(
"isSensorPrivacyEnabled",
Int::class.javaPrimitiveType,
Int::class.javaPrimitiveType
)
m.isAccessible = true
return m.invoke(
sensorPrivacyManager,
sensor,
userHandle
) as Boolean
}
return false
}
不幸的是,由于 SecurityException - missing ,服务拒绝了这个调用android.permission.OBSERVE_SENSOR_PRIVACY
,即使我们在 Manifest 中声明了它。至少在模拟器上。
2. 我们可以尝试识别一个新的“sensor-in-use”指标
fun checkForIndicator(){
findViewById<View>(Window.ID_ANDROID_CONTENT)?.let {
it.setOnApplyWindowInsetsListener { view, windowInsets ->
val indicatorBounds = windowInsets.privacyIndicatorBounds
if(indicatorBounds !=null){
Toast.makeText(view.context, "Camera-in-use detected", Toast.LENGTH_LONG).show()
}
// change your UI to avoid overlapping
windowInsets
}
}
}
我没有测试这段代码(没有真正的设备),但对我来说 - 它不是很有用,因为我们只能在启动生物认证流程之后检查相机指示器,当我需要了解在启动生物认证之前是否可以访问相机.
3. 由于 PrivicyToogle 与 QuickSettings 相关,我认为可能存在 Tiles 确定当前隐私切换状态的方式。但是这个 API 使用了一个非常有趣的解决方案——它不使用Settings.Global
orSettings.Security
部分,而是保存所有的首选项,"system/sensor_privacy.xml"
并且不能被 3rd 方应用程序访问。
请参阅SensorPrivacyService.java
我相信存在一种如何找到相机被阻止的方法,但似乎需要进行更深入的研究
2021 年 10 月 28 日更新
所以在对AOSP源进行了一些挖掘之后,我发现APP_OP_CAMERA权限反映了“阻塞”状态。
只需调用if(SensorPrivacyCheck.isCameraBlocked()){ return }
- 此调用还会通知系统显示“解除阻止”对话框
例子
解决方案:
@TargetApi(Build.VERSION_CODES.S)
@RestrictTo(RestrictTo.Scope.LIBRARY)
object SensorPrivacyCheck {
fun isMicrophoneBlocked(): Boolean {
return Utils.isAtLeastS && checkIsPrivacyToggled(SensorPrivacyManager.Sensors.MICROPHONE)
}
fun isCameraBlocked(): Boolean {
return Utils.isAtLeastS && checkIsPrivacyToggled(SensorPrivacyManager.Sensors.CAMERA)
}
@SuppressLint("PrivateApi", "BlockedPrivateApi")
private fun checkIsPrivacyToggled(sensor: Int): Boolean {
val sensorPrivacyManager: SensorPrivacyManager =
AndroidContext.appContext.getSystemService(SensorPrivacyManager::class.java)
if (sensorPrivacyManager.supportsSensorToggle(sensor)) {
try {
val permissionToOp: String =
AppOpCompatConstants.getAppOpFromPermission(
if (sensor == SensorPrivacyManager.Sensors.CAMERA)
Manifest.permission.CAMERA else Manifest.permission.RECORD_AUDIO
) ?: return false
val noteOp: Int = try {
AppOpsManagerCompat.noteOpNoThrow(
AndroidContext.appContext,
permissionToOp,
Process.myUid(),
AndroidContext.appContext.packageName
)
} catch (ignored: Throwable) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT)
PermissionUtils.appOpPermissionsCheckMiui(
permissionToOp,
Process.myUid(),
AndroidContext.appContext.packageName
) else AppOpsManagerCompat.MODE_IGNORED
}
return noteOp != AppOpsManagerCompat.MODE_ALLOWED
} catch (e: Throwable) {
e.printStackTrace()
}
}
return false
}
}