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