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