1 /*
<lambda>null2  * Copyright 2023 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 androidx.datastore.testapp.twoWayIpc
18 
19 import android.content.Intent
20 import android.os.Handler
21 import android.os.IBinder
22 import android.os.Looper
23 import android.os.Message
24 import android.os.Messenger
25 import androidx.lifecycle.LifecycleService
26 import androidx.lifecycle.lifecycleScope
27 import kotlinx.coroutines.CoroutineScope
28 import kotlinx.coroutines.Dispatchers
29 import kotlinx.coroutines.Job
30 import kotlinx.coroutines.cancelAndJoin
31 import kotlinx.coroutines.launch
32 
33 /**
34  * Another service of the same type, that runs in another separate process.
35  *
36  * @see TwoWayIpcService
37  */
38 class TwoWayIpcService2 : TwoWayIpcService()
39 
40 /**
41  * An Android [android.app.Service] implementation that can create and maintain multiple
42  * [TwoWayIpcSubject] instances.
43  *
44  * It properly scopes those subjects and destroys their scopes when the Service is destroyed,
45  * allowing tests to properly maintain resources.
46  *
47  * @see androidx.datastore.testapp.multiprocess.MultiProcessTestRule
48  */
49 open class TwoWayIpcService : LifecycleService() {
50     private val subjects = mutableListOf<TwoWayIpcSubject>()
51     private val jobForSubjects = Job()
52     private val scopeForSubjects = CoroutineScope(jobForSubjects + Dispatchers.IO)
53     private val messenger: Messenger =
54         Messenger(
55             Handler(Looper.getMainLooper()) { incoming ->
56                 // make a copy to prevent recycling
57                 when (incoming.what) {
58                     MSG_CREATE_SUBJECT -> {
59                         val subject = TwoWayIpcSubject(scopeForSubjects).also { subjects.add(it) }
60 
61                         @Suppress("DEPRECATION")
62                         val messenger = incoming.data.getParcelable<Messenger>("messenger")
63                         checkNotNull(messenger) { "missing messenger" }
64                         subject.bus.setOutgoingMessenger(messenger)
65                         val response =
66                             Message.obtain().also {
67                                 it.data.putParcelable("messenger", subject.bus.incomingMessenger)
68                             }
69                         incoming.replyTo.send(response)
70                     }
71                     MSG_DESTROY_SUBJECTS -> {
72                         val incomingCopy = Message.obtain().also { it.copyFrom(incoming) }
73                         lifecycleScope.launch {
74                             IpcLogger.log("destroying subjects")
75                             try {
76                                 jobForSubjects.cancelAndJoin()
77                                 IpcLogger.log("destroyed subjects")
78                             } finally {
79                                 incomingCopy.replyTo.send(
80                                     Message.obtain().also { it.data.putBoolean("closed", true) }
81                                 )
82                             }
83                         }
84                     }
85                     else -> error("unknown message type ${incoming.what}")
86                 }
87                 true
88             }
89         )
90 
91     override fun onBind(intent: Intent): IBinder? {
92         super.onBind(intent)
93         return messenger.binder
94     }
95 
96     companion object {
97         const val MSG_CREATE_SUBJECT = 500
98         const val MSG_DESTROY_SUBJECTS = 501
99     }
100 }
101