• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.KeyguardQuickAffordancePreviewConstants
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                 KeyguardQuickAffordancePreviewConstants.MESSAGE_ID_SLOT_SELECTED -> {
118                     message.data
119                         .getString(
120                             KeyguardQuickAffordancePreviewConstants.KEY_SLOT_ID,
121                         )
122                         ?.let { slotId -> renderer.onSlotSelected(slotId = slotId) }
123                 }
124                 else -> requestDestruction(this)
125             }
126 
127             return true
128         }
129 
130         override fun binderDied() {
131             requestDestruction(this)
132         }
133 
134         fun onDestroy(): IBinder? {
135             if (isDestroyedOrDestroying) {
136                 return null
137             }
138 
139             isDestroyedOrDestroying = true
140             val hostToken = renderer.hostToken
141             hostToken?.unlinkToDeath(this, 0)
142             scope.launch(mainDispatcher) { renderer.destroy() }
143             return hostToken
144         }
145     }
146 
147     companion object {
148         private const val TAG = "KeyguardRemotePreviewManager"
149         @VisibleForTesting const val KEY_PREVIEW_SURFACE_PACKAGE = "surface_package"
150         @VisibleForTesting const val KEY_PREVIEW_CALLBACK = "callback"
151     }
152 }
153