• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.systemui
18 
19 import android.content.Context
20 import android.graphics.Path
21 import android.graphics.Rect
22 import android.hardware.camera2.CameraManager
23 import com.android.systemui.res.R
24 import java.util.concurrent.Executor
25 
26 /**
27  * Listens for usage of the Camera and controls the ScreenDecorations transition to show extra
28  * protection around a display cutout based on config_frontBuiltInDisplayCutoutProtection and
29  * config_enableDisplayCutoutProtection
30  */
31 class CameraAvailabilityListener(
32     private val cameraManager: CameraManager,
33     private val cameraProtectionInfoList: List<CameraProtectionInfo>,
34     excludedPackages: String,
35     private val executor: Executor
36 ) {
37     private var activeProtectionInfo: CameraProtectionInfo? = null
38     private var openCamera: OpenCameraInfo? = null
39     private val unavailablePhysicalCameras = mutableSetOf<String>()
40     private val excludedPackageIds: Set<String>
41     private val listeners = mutableListOf<CameraTransitionCallback>()
42     private val availabilityCallback: CameraManager.AvailabilityCallback =
43         object : CameraManager.AvailabilityCallback() {
onCameraClosednull44             override fun onCameraClosed(logicalCameraId: String) {
45                 openCamera = null
46                 if (activeProtectionInfo?.logicalCameraId == logicalCameraId) {
47                     notifyCameraInactive()
48                 }
49                 activeProtectionInfo = null
50             }
51 
onCameraOpenednull52             override fun onCameraOpened(logicalCameraId: String, packageId: String) {
53                 openCamera = OpenCameraInfo(logicalCameraId, packageId)
54                 if (isExcluded(packageId)) {
55                     return
56                 }
57                 val protectionInfo =
58                     cameraProtectionInfoList.firstOrNull {
59                         logicalCameraId == it.logicalCameraId &&
60                             it.physicalCameraId !in unavailablePhysicalCameras
61                     }
62                 if (protectionInfo != null) {
63                     activeProtectionInfo = protectionInfo
64                     notifyCameraActive(protectionInfo)
65                 }
66             }
67 
onPhysicalCameraAvailablenull68             override fun onPhysicalCameraAvailable(
69                 logicalCameraId: String,
70                 physicalCameraId: String
71             ) {
72                 unavailablePhysicalCameras -= physicalCameraId
73                 val openCamera = this@CameraAvailabilityListener.openCamera ?: return
74                 if (openCamera.logicalCameraId != logicalCameraId) {
75                     return
76                 }
77                 if (isExcluded(openCamera.packageId)) {
78                     return
79                 }
80                 val newActiveInfo =
81                     cameraProtectionInfoList.find {
82                         it.logicalCameraId == logicalCameraId &&
83                             it.physicalCameraId == physicalCameraId
84                     }
85                 if (newActiveInfo != null) {
86                     activeProtectionInfo = newActiveInfo
87                     notifyCameraActive(newActiveInfo)
88                 }
89             }
90 
onPhysicalCameraUnavailablenull91             override fun onPhysicalCameraUnavailable(
92                 logicalCameraId: String,
93                 physicalCameraId: String
94             ) {
95                 unavailablePhysicalCameras += physicalCameraId
96                 val activeInfo = activeProtectionInfo ?: return
97                 if (
98                     activeInfo.logicalCameraId == logicalCameraId &&
99                         activeInfo.physicalCameraId == physicalCameraId
100                 ) {
101                     activeProtectionInfo = null
102                     notifyCameraInactive()
103                 }
104             }
105         }
106 
107     init {
108         excludedPackageIds = excludedPackages.split(",").toSet()
109     }
110 
111     /**
112      * Start listening for availability events, and maybe notify listeners
113      *
114      * @return true if we started listening
115      */
startListeningnull116     fun startListening() {
117         registerCameraListener()
118     }
119 
stopnull120     fun stop() {
121         unregisterCameraListener()
122     }
123 
addTransitionCallbacknull124     fun addTransitionCallback(callback: CameraTransitionCallback) {
125         listeners.add(callback)
126     }
127 
removeTransitionCallbacknull128     fun removeTransitionCallback(callback: CameraTransitionCallback) {
129         listeners.remove(callback)
130     }
131 
debugFaceAuthnull132     fun debugFaceAuth(id: Int) {
133         val info = cameraProtectionInfoList?.getOrNull(id)
134         if (info != null) notifyCameraActive(info)
135     }
136 
isExcludednull137     private fun isExcluded(packageId: String): Boolean {
138         return excludedPackageIds.contains(packageId)
139     }
140 
registerCameraListenernull141     private fun registerCameraListener() {
142         cameraManager.registerAvailabilityCallback(executor, availabilityCallback)
143     }
144 
unregisterCameraListenernull145     private fun unregisterCameraListener() {
146         cameraManager.unregisterAvailabilityCallback(availabilityCallback)
147     }
148 
notifyCameraActivenull149     private fun notifyCameraActive(info: CameraProtectionInfo) {
150         listeners.forEach {
151             it.onApplyCameraProtection(info.cutoutProtectionPath, info.bounds)
152         }
153     }
154 
notifyCameraInactivenull155     private fun notifyCameraInactive() {
156         listeners.forEach { it.onHideCameraProtection() }
157     }
158 
159     /** Callbacks to tell a listener that a relevant camera turned on and off. */
160     interface CameraTransitionCallback {
onApplyCameraProtectionnull161         fun onApplyCameraProtection(protectionPath: Path, bounds: Rect)
162 
163         fun onHideCameraProtection()
164     }
165 
166     companion object Factory {
167         fun build(
168             context: Context,
169             executor: Executor,
170             cameraProtectionLoader: CameraProtectionLoader
171         ): CameraAvailabilityListener {
172             val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
173             val res = context.resources
174             val cameraProtectionInfoList = cameraProtectionLoader.loadCameraProtectionInfoList()
175             val excluded = res.getString(R.string.config_cameraProtectionExcludedPackages)
176 
177             return CameraAvailabilityListener(manager, cameraProtectionInfoList, excluded, executor)
178         }
179     }
180 
181     private data class OpenCameraInfo(
182         val logicalCameraId: String,
183         val packageId: String,
184     )
185 }
186