1 /* 2 * 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.os.Bundle 20 import android.os.Parcelable 21 import kotlin.properties.ReadWriteProperty 22 import kotlin.reflect.KProperty 23 import kotlinx.coroutines.CoroutineScope 24 25 /** 26 * A class that represents a test subject for DataStore multi-process tests. Each test subject is 27 * given a [datastoreScope] as well as a [data] so they can keep state. 28 * 29 * Subjects execute [IpcAction]s which contain the actual test logic. 30 */ 31 internal class TwoWayIpcSubject(val datastoreScope: CoroutineScope) { 32 val bus: TwoWayIpcBus = 33 TwoWayIpcBus(executionScope = datastoreScope, handler = this::handleIncomingAction) 34 val data = CompositeServiceSubjectModel() 35 handleIncomingActionnull36 private suspend fun handleIncomingAction(bundle: Bundle?): Bundle { 37 @Suppress("DEPRECATION") val ipcAction = bundle?.getParcelable<IpcAction<*>>(KEY_ACTION) 38 checkNotNull(ipcAction) { "no ipc action in bundle" } 39 IpcLogger.log("executing action: ${ipcAction::class.java}") 40 41 val response = ipcAction.invokeInRemoteProcess(this) 42 IpcLogger.log("executed action: ${ipcAction::class.java}") 43 return Bundle().also { it.putParcelable(KEY_RESPONSE, response) } 44 } 45 invokeInRemoteProcessnull46 suspend fun <T : Parcelable> invokeInRemoteProcess(action: IpcAction<T>): T { 47 val response = bus.sendMessage(Bundle().also { it.putParcelable(KEY_ACTION, action) }) 48 checkNotNull(response) { "No response received for $action" } 49 @Suppress("DEPRECATION") 50 return response.getParcelable(KEY_RESPONSE) 51 ?: error("didn't get a response from remote process") 52 } 53 54 companion object { 55 private const val KEY_ACTION = "ipc_action" 56 private const val KEY_RESPONSE = "ipc_response" 57 } 58 } 59 60 /** 61 * A property delegate to stash values into the [CompositeServiceSubjectModel] of a 62 * [TwoWayIpcSubject]. 63 */ 64 internal class SubjectReadWriteProperty<T>(private val key: CompositeServiceSubjectModel.Key<T>) : 65 ReadWriteProperty<TwoWayIpcSubject, T> { getValuenull66 override fun getValue(thisRef: TwoWayIpcSubject, property: KProperty<*>): T { 67 return thisRef.data[key] 68 } 69 setValuenull70 override fun setValue(thisRef: TwoWayIpcSubject, property: KProperty<*>, value: T) { 71 thisRef.data[key] = value 72 } 73 } 74