1 /* 2 * Copyright (C) 2024 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 package com.android.car.carlauncher.datasources 18 19 import android.content.ComponentName 20 import android.content.Context 21 import android.content.Intent 22 import android.content.ServiceConnection 23 import android.content.pm.PackageManager 24 import android.content.res.Resources 25 import android.os.Bundle 26 import android.os.Handler 27 import android.os.IBinder 28 import android.os.Looper 29 import android.os.Message 30 import android.os.Messenger 31 import android.os.RemoteException 32 import android.util.Log 33 import com.android.car.carlauncher.R 34 import com.android.car.carlauncher.datasources.ControlCenterMirroringDataSource.MirroringPackageData 35 import java.net.URISyntaxException 36 import kotlinx.coroutines.CoroutineDispatcher 37 import kotlinx.coroutines.Dispatchers 38 import kotlinx.coroutines.cancel 39 import kotlinx.coroutines.channels.ProducerScope 40 import kotlinx.coroutines.channels.awaitClose 41 import kotlinx.coroutines.flow.Flow 42 import kotlinx.coroutines.flow.callbackFlow 43 import kotlinx.coroutines.flow.conflate 44 import kotlinx.coroutines.flow.flowOn 45 46 /** 47 * DataSource interface tells if there is an active Mirroring-Session. 48 */ 49 interface ControlCenterMirroringDataSource { 50 51 /** 52 * @return Flow of [MirroringPackageData] which sends the active Mirroring packageName and 53 * redirect launchIntent which launches the application in the 54 * MirroringActivity 55 */ getAppMirroringSessionnull56 fun getAppMirroringSession(): Flow<MirroringPackageData> 57 58 data class MirroringPackageData( 59 val packageName: String, 60 val launchIntent: Intent 61 ) { 62 companion object { 63 // signifies active mirroring session 64 val NO_MIRRORING = MirroringPackageData("", Intent()) 65 } 66 } 67 } 68 69 /** 70 * Impl of [ControlCenterMirroringDataSource] to surface all the control center mirroring session 71 * All the operations in this class are non blocking. 72 * 73 * The Application using this Datasource is expected to define the following configs 74 * in its resources. The implementation uses [resources] to fetch these configs. 75 * These configs should be bound application's lifecycle and are not expected to change with 76 * Activity's lifecycle events. 77 * 78 * __config_msg_mirroring_service_pkg_name__: String value stating the package name of the 79 * mirroring service. 80 * 81 * __config_msg_mirroring_service_class_name__: String value stating the class name of the 82 * mirroring service. 83 * 84 * __config_msg_register_mirroring_pkg_code__: Integer unique key to register the service. 85 * 86 * __config_msg_unregister_mirroring_pkg_code__: Integer unique key to unregister the service. 87 * 88 * __config_msg_send_mirroring_pkg_code__: Integer unique key to send mirroring packet across the 89 * service. 90 * 91 * __config_msg_mirroring_pkg_name_key__: String unique key to send packageName of the active 92 * mirroring session. 93 * 94 * __config_msg_mirroring_redirect_uri_key__: String unique key to send the redirect uri of the 95 * mirroring activity. 96 * 97 * @property [resources] Application resources, not bound to activity's configuration changes. 98 * @property [bindService] Function to register service. 99 * Should be provided by an Android Component owning the [Context]. 100 * @property [unBindService] Function to unregister the broadcast receiver. 101 * Should be provided by the Android Component owning the [Context]. 102 * @property [packageManager] Used to resolve the bounded Service. 103 * @property [bgDispatcher] Executes all the operations on this background coroutine dispatcher. 104 * 105 */ 106 class ControlCenterMirroringDataSourceImpl( 107 private val resources: Resources, 108 private val bindService: (Intent, MirroringServiceConnection, flags: Int) -> Unit, 109 private val unBindService: (MirroringServiceConnection) -> Unit, 110 private val packageManager: PackageManager, 111 private val bgDispatcher: CoroutineDispatcher = Dispatchers.IO 112 ) : ControlCenterMirroringDataSource { 113 114 /** 115 * @return Flow of [MirroringPackageData] reporting current active mirroring session. 116 * 117 * Note: The producer sends an [MirroringPackageData.NO_MIRRORING] initially. 118 * This immediately tells the collector that there are no changes as of now with packages. 119 * 120 * When the scope in which this flow is collected is closed/canceled 121 * [unBindService] is triggered. 122 */ getAppMirroringSessionnull123 override fun getAppMirroringSession(): Flow<MirroringPackageData> { 124 return callbackFlow { 125 // Send empty mirroring packet to signify that no mirroring is ongoing 126 trySend(MirroringPackageData.NO_MIRRORING) 127 val looper = Looper.getMainLooper() 128 val clientMessenger = getReceiverMessenger(looper, this) 129 val serviceConnection = getMirroringConnectionService(clientMessenger, this) 130 registerReceiver(serviceConnection, this) 131 132 awaitClose { 133 unregisterReceiver(serviceConnection) 134 } 135 }.flowOn(bgDispatcher).conflate() 136 } 137 getReceiverMessengernull138 private fun getReceiverMessenger( 139 looper: Looper, 140 producerScope: ProducerScope<MirroringPackageData> 141 ): Messenger { 142 return Messenger(object : Handler(looper) { 143 private val senderMirroringPkgCode = 144 resources.getInteger(R.integer.config_msg_send_mirroring_pkg_code) 145 private val mirroringPkgNameKey = 146 resources.getString(R.string.config_msg_mirroring_pkg_name_key) 147 private val mirroringRedirectUriKey = 148 resources.getString(R.string.config_msg_mirroring_redirect_uri_key) 149 150 override fun handleMessage(msg: Message) { 151 if (msg.what != senderMirroringPkgCode) { 152 super.handleMessage(msg) 153 return 154 } 155 val bundle = msg.obj as Bundle 156 val mirroringPackageName = bundle.getString(mirroringPkgNameKey) 157 if (mirroringPackageName.isNullOrEmpty()) { 158 producerScope.trySend(MirroringPackageData.NO_MIRRORING) 159 return 160 } 161 try { 162 val mirroringIntentRedirect = Intent.parseUri( 163 bundle.getString(mirroringRedirectUriKey), 164 Intent.URI_INTENT_SCHEME 165 ) 166 producerScope.trySend( 167 MirroringPackageData( 168 mirroringPackageName, 169 mirroringIntentRedirect 170 ) 171 ) 172 } catch (e: URISyntaxException) { 173 Log.d(TAG, "Error parsing mirroring redirect intent $e") 174 } 175 } 176 }) 177 } 178 179 abstract class MirroringServiceConnection : ServiceConnection { 180 var mServiceMessenger: Messenger? = null 181 var mClientMessenger: Messenger? = null 182 } 183 getMirroringConnectionServicenull184 private fun getMirroringConnectionService( 185 clientMessenger: Messenger, 186 producerScope: ProducerScope<MirroringPackageData> 187 ): MirroringServiceConnection { 188 return object : MirroringServiceConnection() { 189 init { 190 mClientMessenger = clientMessenger 191 } 192 193 override fun onServiceConnected(name: ComponentName, service: IBinder) { 194 mServiceMessenger = Messenger(service) 195 val msg: Message = Message.obtain( 196 null, 197 resources.getInteger(R.integer.config_msg_register_mirroring_pkg_code) 198 ) 199 msg.replyTo = mClientMessenger 200 try { 201 mServiceMessenger?.send(msg) 202 } catch (e: RemoteException) { 203 Log.d(TAG, "Exception sending message to mirroring service: $e") 204 } 205 } 206 207 override fun onServiceDisconnected(name: ComponentName) { 208 producerScope.cancel("Mirroring Service disconnected") 209 } 210 } 211 } 212 registerReceivernull213 private fun registerReceiver( 214 mirroringConnectionService: MirroringServiceConnection, 215 producerScope: ProducerScope<MirroringPackageData> 216 ) { 217 try { 218 val intent = Intent() 219 intent.component = ComponentName( 220 resources.getString(R.string.config_msg_mirroring_service_pkg_name), 221 resources.getString(R.string.config_msg_mirroring_service_class_name) 222 ) 223 if (packageManager.resolveService(intent, 0) != null) { 224 bindService( 225 intent, 226 mirroringConnectionService, 227 Context.BIND_AUTO_CREATE or Context.BIND_IMPORTANT 228 ) 229 } 230 } catch (e: SecurityException) { 231 Log.e(TAG, "Error binding to mirroring service: $e") 232 producerScope.close(e) 233 } 234 } 235 unregisterReceivernull236 private fun unregisterReceiver(mirroringConnectionService: MirroringServiceConnection) { 237 val msg = Message.obtain( 238 null, 239 resources.getInteger(R.integer.config_msg_unregister_mirroring_pkg_code) 240 ) 241 msg.replyTo = mirroringConnectionService.mClientMessenger 242 try { 243 mirroringConnectionService.mServiceMessenger?.send(msg) 244 } catch (e: RemoteException) { 245 Log.d(TAG, "Exception unregistering mirroring service $e") 246 } 247 if (mirroringConnectionService.mServiceMessenger != null) { 248 unBindService(mirroringConnectionService) 249 } 250 } 251 252 companion object { 253 val TAG: String = ControlCenterMirroringDataSourceImpl::class.java.simpleName 254 } 255 } 256