1 /*
2  * Copyright (C) 2022 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 
18 package com.android.systemui.keyguard.ui.preview
19 
20 import android.os.Bundle
21 import android.os.Handler
22 import android.os.IBinder
23 import android.os.Message
24 import android.os.Messenger
25 import android.util.ArrayMap
26 import android.util.Log
27 import androidx.annotation.VisibleForTesting
28 import com.android.systemui.dagger.SysUISingleton
29 import com.android.systemui.dagger.qualifiers.Application
30 import com.android.systemui.dagger.qualifiers.Background
31 import com.android.systemui.dagger.qualifiers.Main
32 import com.android.systemui.shared.quickaffordance.shared.model.KeyguardPreviewConstants
33 import javax.inject.Inject
34 import kotlinx.coroutines.CoroutineDispatcher
35 import kotlinx.coroutines.CoroutineScope
36 import kotlinx.coroutines.launch
37 
38 @SysUISingleton
39 class KeyguardRemotePreviewManager
40 @Inject
41 constructor(
42     private val previewRendererFactory: KeyguardPreviewRendererFactory,
43     @Application private val applicationScope: CoroutineScope,
44     @Main private val mainDispatcher: CoroutineDispatcher,
45     @Background private val backgroundHandler: Handler,
46 ) {
47     private val activePreviews: ArrayMap<IBinder, PreviewLifecycleObserver> =
48         ArrayMap<IBinder, PreviewLifecycleObserver>()
49 
50     fun preview(request: Bundle?): Bundle? {
51         if (request == null) {
52             return null
53         }
54 
55         var observer: PreviewLifecycleObserver? = null
56         return try {
57             val renderer = previewRendererFactory.create(request)
58 
59             // Destroy any previous renderer associated with this token.
60             activePreviews[renderer.hostToken]?.let { destroyObserver(it) }
61             observer =
62                 PreviewLifecycleObserver(
63                     renderer,
64                     applicationScope,
65                     mainDispatcher,
66                     ::destroyObserver,
67                 )
68             activePreviews[renderer.hostToken] = observer
69             renderer.render()
70             renderer.hostToken?.linkToDeath(observer, 0)
71             val result = Bundle()
72             result.putParcelable(
73                 KEY_PREVIEW_SURFACE_PACKAGE,
74                 renderer.surfacePackage,
75             )
76             val messenger =
77                 Messenger(
78                     Handler(
79                         backgroundHandler.looper,
80                         observer,
81                     )
82                 )
83             val msg = Message.obtain()
84             msg.replyTo = messenger
85             result.putParcelable(KEY_PREVIEW_CALLBACK, msg)
86             result
87         } catch (e: Exception) {
88             Log.e(TAG, "Unable to generate preview", e)
89             observer?.let { destroyObserver(it) }
90             null
91         }
92     }
93 
94     private fun destroyObserver(observer: PreviewLifecycleObserver) {
95         observer.onDestroy()?.let { hostToken ->
96             if (activePreviews[hostToken] === observer) {
97                 activePreviews.remove(hostToken)
98             }
99         }
100     }
101 
102     private class PreviewLifecycleObserver(
103         private val renderer: KeyguardPreviewRenderer,
104         private val scope: CoroutineScope,
105         private val mainDispatcher: CoroutineDispatcher,
106         private val requestDestruction: (PreviewLifecycleObserver) -> Unit,
107     ) : Handler.Callback, IBinder.DeathRecipient {
108 
109         private var isDestroyedOrDestroying = false
110 
111         override fun handleMessage(message: Message): Boolean {
112             if (isDestroyedOrDestroying) {
113                 return true
114             }
115 
116             when (message.what) {
117                 KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> {
118                     message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId ->
119                         renderer.onSlotSelected(slotId = slotId)
120                     }
121                 }
122                 KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> {
123                     renderer.hideSmartspace(
124                         message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE)
125                     )
126                 }
127                 else -> requestDestruction(this)
128             }
129 
130             return true
131         }
132 
133         override fun binderDied() {
134             requestDestruction(this)
135         }
136 
137         fun onDestroy(): IBinder? {
138             if (isDestroyedOrDestroying) {
139                 return null
140             }
141 
142             isDestroyedOrDestroying = true
143             val hostToken = renderer.hostToken
144             hostToken?.unlinkToDeath(this, 0)
145             scope.launch(mainDispatcher) { renderer.destroy() }
146             return hostToken
147         }
148     }
149 
150     companion object {
151         private const val TAG = "KeyguardRemotePreviewManager"
152         @VisibleForTesting const val KEY_PREVIEW_SURFACE_PACKAGE = "surface_package"
153         @VisibleForTesting const val KEY_PREVIEW_CALLBACK = "callback"
154     }
155 }
156