1 /*
<lambda>null2  * 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 com.android.systemui.util.kotlin.logD
34 import javax.inject.Inject
35 import kotlinx.coroutines.CoroutineDispatcher
36 import kotlinx.coroutines.CoroutineScope
37 import kotlinx.coroutines.launch
38 
39 @SysUISingleton
40 class KeyguardRemotePreviewManager
41 @Inject
42 constructor(
43     private val previewRendererFactory: KeyguardPreviewRendererFactory,
44     @Application private val applicationScope: CoroutineScope,
45     @Main private val mainDispatcher: CoroutineDispatcher,
46     @Background private val backgroundHandler: Handler,
47 ) {
48     private val activePreviews: ArrayMap<Pair<IBinder?, Int>, PreviewLifecycleObserver> =
49         ArrayMap<Pair<IBinder?, Int>, PreviewLifecycleObserver>()
50 
51     fun preview(request: Bundle?): Bundle? {
52         if (request == null) {
53             return null
54         }
55 
56         var observer: PreviewLifecycleObserver? = null
57         return try {
58             val renderer = previewRendererFactory.create(request)
59 
60             observer =
61                 PreviewLifecycleObserver(
62                     applicationScope,
63                     mainDispatcher,
64                     renderer,
65                     ::destroyObserver,
66                 )
67 
68             logD(TAG) { "Created observer $observer" }
69 
70             // Destroy any previous renderer associated with this token.
71             activePreviews[renderer.id]?.let { destroyObserver(it) }
72             activePreviews[renderer.id] = observer
73             renderer.render()
74             renderer.hostToken?.linkToDeath(observer, 0)
75             val result = Bundle()
76             result.putParcelable(
77                 KEY_PREVIEW_SURFACE_PACKAGE,
78                 renderer.surfacePackage,
79             )
80             val messenger =
81                 Messenger(
82                     Handler(
83                         backgroundHandler.looper,
84                         observer,
85                     )
86                 )
87             // NOTE: The process on the other side can retain messenger indefinitely.
88             // (e.g. GC might not trigger and cleanup the reference)
89             val msg = Message.obtain()
90             msg.replyTo = messenger
91             result.putParcelable(KEY_PREVIEW_CALLBACK, msg)
92             result
93         } catch (e: Exception) {
94             Log.e(TAG, "Unable to generate preview", e)
95             observer?.let { destroyObserver(it) }
96             null
97         }
98     }
99 
100     private fun destroyObserver(observer: PreviewLifecycleObserver) {
101         observer.onDestroy()?.let { identifier ->
102             if (activePreviews[identifier] === observer) {
103                 activePreviews.remove(identifier)
104             }
105         }
106     }
107 
108     companion object {
109         internal const val TAG = "KeyguardRemotePreviewManager"
110         @VisibleForTesting const val KEY_PREVIEW_SURFACE_PACKAGE = "surface_package"
111         @VisibleForTesting const val KEY_PREVIEW_CALLBACK = "callback"
112     }
113 }
114 
115 /**
116  * Handles messages from the other process and handles cleanup.
117  *
118  * NOTE: The other process might hold on to reference of this class indefinitely. It's entirely
119  * possible that GC won't trigger and we'll leak this for all times even if [onDestroy] was called.
120  * This helps make sure no non-Singleton objects are retained beyond destruction to prevent leaks.
121  */
122 @VisibleForTesting(VisibleForTesting.PRIVATE)
123 class PreviewLifecycleObserver(
124     private val scope: CoroutineScope,
125     private val mainDispatcher: CoroutineDispatcher,
126     renderer: KeyguardPreviewRenderer,
127     onDestroy: (PreviewLifecycleObserver) -> Unit,
128 ) : Handler.Callback, IBinder.DeathRecipient {
129 
130     private var isDestroyedOrDestroying = false
131     // These two are null after destruction
132     @VisibleForTesting var renderer: KeyguardPreviewRenderer?
133     @VisibleForTesting var onDestroy: ((PreviewLifecycleObserver) -> Unit)?
134 
135     init {
136         this.renderer = renderer
137         this.onDestroy = onDestroy
138     }
139 
handleMessagenull140     override fun handleMessage(message: Message): Boolean {
141         if (isDestroyedOrDestroying) {
142             return true
143         }
144 
145         if (renderer == null || onDestroy == null) {
146             Log.wtf(TAG, "Renderer/onDestroy should not be null.")
147             return true
148         }
149 
150         when (message.what) {
151             KeyguardPreviewConstants.MESSAGE_ID_SLOT_SELECTED -> {
152                 message.data.getString(KeyguardPreviewConstants.KEY_SLOT_ID)?.let { slotId ->
153                     checkNotNull(renderer).onSlotSelected(slotId = slotId)
154                 }
155             }
156             KeyguardPreviewConstants.MESSAGE_ID_HIDE_SMART_SPACE -> {
157                 checkNotNull(renderer)
158                     .hideSmartspace(
159                         message.data.getBoolean(KeyguardPreviewConstants.KEY_HIDE_SMART_SPACE)
160                     )
161             }
162             else -> checkNotNull(onDestroy).invoke(this)
163         }
164 
165         return true
166     }
167 
binderDiednull168     override fun binderDied() {
169         onDestroy?.invoke(this)
170     }
171 
onDestroynull172     fun onDestroy(): Pair<IBinder?, Int>? {
173         if (isDestroyedOrDestroying) {
174             return null
175         }
176 
177         logD(TAG) { "Destroying $this" }
178 
179         isDestroyedOrDestroying = true
180         return renderer?.let { rendererToDestroy ->
181             this.renderer = null
182             this.onDestroy = null
183             val hostToken = rendererToDestroy.hostToken
184             hostToken?.unlinkToDeath(this, 0)
185             scope.launch(mainDispatcher) { rendererToDestroy.destroy() }
186             rendererToDestroy.id
187         }
188     }
189 
190     companion object {
191         private const val TAG = "KeyguardRemotePreviewManager"
192     }
193 }
194