diff options
author | 2024-01-19 14:44:52 +0800 | |
---|---|---|
committer | 2024-01-19 13:53:21 +0000 | |
commit | f7bfbc3490bf66fd8c4dce7cad7de0fd7c6cca70 (patch) | |
tree | c20bee6c0f5d717ee5c03f7e59569dd235e80cdd | |
parent | 4846e39bce4c3955c53ba9256e84705eebe218de (diff) |
New PermissionsChangedFlow
Also also clean up LiveDataTestUtil.
Bug: 321163306
Test: unit test
Change-Id: Id2d77f53569619654036e782285cab8931fd5bfc
3 files changed, 127 insertions, 50 deletions
diff --git a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/LiveDataTestUtil.kt b/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/LiveDataTestUtil.kt deleted file mode 100644 index dddda5511c7b..000000000000 --- a/packages/SettingsLib/Spa/testutils/src/com/android/settingslib/spa/testutils/LiveDataTestUtil.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.settingslib.spa.testutils - -import androidx.lifecycle.LiveData -import androidx.lifecycle.Observer -import java.util.concurrent.CountDownLatch -import java.util.concurrent.TimeUnit -import java.util.concurrent.TimeoutException - -fun <T> LiveData<T>.getOrAwaitValue( - timeout: Long = 1, - timeUnit: TimeUnit = TimeUnit.SECONDS, - afterObserve: () -> Unit = {}, -): T? { - var data: T? = null - val latch = CountDownLatch(1) - val observer = Observer<T> { newData -> - data = newData - latch.countDown() - } - this.observeForever(observer) - - afterObserve() - - try { - // Don't wait indefinitely if the LiveData is not set. - if (!latch.await(timeout, timeUnit)) { - throw TimeoutException("LiveData value was never set.") - } - } finally { - this.removeObserver(observer) - } - - return data -} diff --git a/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PermissionsChangedFlow.kt b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PermissionsChangedFlow.kt new file mode 100644 index 000000000000..367244aa2cfe --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/src/com/android/settingslib/spaprivileged/model/app/PermissionsChangedFlow.kt @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spaprivileged.model.app + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import com.android.settingslib.spaprivileged.framework.common.asUser +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOn + +/** + * Creates an instance of a cold Flow for permissions changed callback of given [app]. + * + * An initial element will be always sent. + */ +fun Context.permissionsChangedFlow(app: ApplicationInfo) = callbackFlow { + val userPackageManager = asUser(app.userHandle).packageManager + + val onPermissionsChangedListener = PackageManager.OnPermissionsChangedListener { uid -> + if (uid == app.uid) trySend(Unit) + } + userPackageManager.addOnPermissionsChangeListener(onPermissionsChangedListener) + trySend(Unit) + + awaitClose { + userPackageManager.removeOnPermissionsChangeListener(onPermissionsChangedListener) + } +}.conflate().flowOn(Dispatchers.Default) diff --git a/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/PermissionsChangedFlowTest.kt b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/PermissionsChangedFlowTest.kt new file mode 100644 index 000000000000..31522c1209f7 --- /dev/null +++ b/packages/SettingsLib/SpaPrivileged/tests/src/com/android/settingslib/spaprivileged/model/app/PermissionsChangedFlowTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.spaprivileged.model.app + +import android.content.Context +import android.content.pm.ApplicationInfo +import android.content.pm.PackageManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull +import com.android.settingslib.spa.testutils.toListWithTimeout +import com.android.settingslib.spaprivileged.framework.common.asUser +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.async +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy + +@RunWith(AndroidJUnit4::class) +class PermissionsChangedFlowTest { + + private var onPermissionsChangedListener: PackageManager.OnPermissionsChangedListener? = null + + private val mockPackageManager = mock<PackageManager> { + on { addOnPermissionsChangeListener(any()) } doAnswer { + onPermissionsChangedListener = + it.arguments[0] as PackageManager.OnPermissionsChangedListener + } + } + + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + on { asUser(APP.userHandle) } doReturn mock + on { packageManager } doReturn mockPackageManager + } + + @Test + fun permissionsChangedFlow_sendInitialValueTrue() = runBlocking { + val flow = context.permissionsChangedFlow(APP) + + assertThat(flow.firstWithTimeoutOrNull()).isNotNull() + } + + @Test + fun permissionsChangedFlow_collectChanged_getTwo() = runBlocking { + val listDeferred = async { + context.permissionsChangedFlow(APP).toListWithTimeout() + } + delay(100) + + onPermissionsChangedListener?.onPermissionsChanged(APP.uid) + + assertThat(listDeferred.await()).hasSize(2) + } + + private companion object { + val APP = ApplicationInfo().apply { + packageName = "package.name" + uid = 10000 + } + } +} |