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