diff --git a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java index 245ac8357ed..7addb597372 100644 --- a/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/AbstractMobileNetworkSettings.java @@ -18,7 +18,6 @@ package com.android.settings.network.telephony; import android.os.SystemClock; import android.text.TextUtils; -import android.util.Log; import androidx.preference.Preference; import androidx.preference.PreferenceScreen; @@ -66,8 +65,7 @@ abstract class AbstractMobileNetworkSettings extends RestrictedDashboardFragment TelephonyStatusControlSession setTelephonyAvailabilityStatus( Collection listOfPrefControllers) { - return (new TelephonyStatusControlSession.Builder(listOfPrefControllers)) - .build(); + return new TelephonyStatusControlSession(listOfPrefControllers, getLifecycle()); } @Override diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java deleted file mode 100644 index 3716f1f864e..00000000000 --- a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2020 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.settings.network.telephony; - -import android.util.Log; - -import com.android.settings.core.BasePreferenceController; -import com.android.settingslib.core.AbstractPreferenceController; -import com.android.settingslib.utils.ThreadUtils; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; - -/** - * Session for controlling the status of TelephonyPreferenceController(s). - * - * Within this session, result of {@link BasePreferenceController#availabilityStatus()} - * would be under control. - */ -public class TelephonyStatusControlSession implements AutoCloseable { - - private static final String LOG_TAG = "TelephonyStatusControlSS"; - - private Collection mControllers; - private Collection> mResult = new ArrayList<>(); - - /** - * Buider of session - */ - public static class Builder { - private Collection mControllers; - - /** - * Constructor - * - * @param controllers is a collection of {@link AbstractPreferenceController} - * which would have {@link BasePreferenceController#availabilityStatus()} - * under control within this session. - */ - public Builder(Collection controllers) { - mControllers = controllers; - } - - /** - * Method to build this session. - * @return {@link TelephonyStatusControlSession} session been setup. - */ - public TelephonyStatusControlSession build() { - return new TelephonyStatusControlSession(mControllers); - } - } - - private TelephonyStatusControlSession(Collection controllers) { - mControllers = controllers; - controllers.forEach(prefCtrl -> mResult - .add(ThreadUtils.postOnBackgroundThread(() -> setupAvailabilityStatus(prefCtrl)))); - - } - - /** - * Close the session. - * - * No longer control the status. - */ - public void close() { - //check the background thread is finished then unset the status of availability. - - for (Future result : mResult) { - try { - result.get(); - } catch (ExecutionException | InterruptedException exception) { - Log.e(LOG_TAG, "setup availability status failed!", exception); - } - } - unsetAvailabilityStatus(mControllers); - } - - private Boolean setupAvailabilityStatus(AbstractPreferenceController controller) { - try { - if (controller instanceof TelephonyAvailabilityHandler) { - int status = ((BasePreferenceController) controller) - .getAvailabilityStatus(); - ((TelephonyAvailabilityHandler) controller).setAvailabilityStatus(status); - } - return true; - } catch (Exception exception) { - Log.e(LOG_TAG, "Setup availability status failed!", exception); - return false; - } - } - - private void unsetAvailabilityStatus( - Collection controllerLists) { - controllerLists.stream() - .filter(controller -> controller instanceof TelephonyAvailabilityHandler) - .map(TelephonyAvailabilityHandler.class::cast) - .forEach(controller -> { - controller.unsetAvailabilityStatus(); - }); - } -} diff --git a/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt new file mode 100644 index 00000000000..0e63c8cce4e --- /dev/null +++ b/src/com/android/settings/network/telephony/TelephonyStatusControlSession.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2023 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.settings.network.telephony + +import android.util.Log +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.coroutineScope +import com.android.settings.core.BasePreferenceController +import com.android.settingslib.core.AbstractPreferenceController +import com.google.common.collect.Sets +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import kotlinx.coroutines.yield + +/** + * Session for controlling the status of TelephonyPreferenceController(s). + * + * Within this session, result of [BasePreferenceController.getAvailabilityStatus] + * would be under control. + */ +class TelephonyStatusControlSession( + private val controllers: Collection, + lifecycle: Lifecycle, +) : AutoCloseable { + private var job: Job? = null + private val controllerSet = Sets.newConcurrentHashSet() + + init { + job = lifecycle.coroutineScope.launch(Dispatchers.Default) { + for (controller in controllers) { + launch { + setupAvailabilityStatus(controller) + } + } + } + } + + /** + * Close the session. + * + * No longer control the status. + */ + override fun close() { + job?.cancel() + unsetAvailabilityStatus() + } + + private suspend fun setupAvailabilityStatus(controller: AbstractPreferenceController): Boolean = + try { + if (controller is TelephonyAvailabilityHandler) { + val status = (controller as BasePreferenceController).availabilityStatus + yield() // prompt cancellation guarantee + if (controllerSet.add(controller)) { + controller.setAvailabilityStatus(status) + } + } + true + } catch (exception: Exception) { + Log.e(LOG_TAG, "Setup availability status failed!", exception) + false + } + + private fun unsetAvailabilityStatus() { + for (controller in controllerSet) { + controller.unsetAvailabilityStatus() + } + } + + companion object { + private const val LOG_TAG = "TelephonyStatusControlSS" + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt new file mode 100644 index 00000000000..7e6a91b8c65 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyStatusControlSessionTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 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.settings.network.telephony + +import android.content.Context +import androidx.lifecycle.testing.TestLifecycleOwner +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.core.BasePreferenceController +import com.android.settingslib.spa.testutils.waitUntil +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@RunWith(AndroidJUnit4::class) +class TelephonyStatusControlSessionTest { + private val context: Context = ApplicationProvider.getApplicationContext() + + @Test + fun init() = runTest { + val controller = TestController(context) + + val session = TelephonyStatusControlSession( + controllers = listOf(controller), + lifecycle = TestLifecycleOwner().lifecycle, + ) + + waitUntil { controller.availabilityStatus == STATUS } + session.close() + } + + @Test + fun close() = runTest { + val controller = TestController(context) + + val session = TelephonyStatusControlSession( + controllers = listOf(controller), + lifecycle = TestLifecycleOwner().lifecycle, + ) + session.close() + + assertThat(controller.availabilityStatus).isNull() + } + + private companion object { + const val KEY = "key" + const val STATUS = BasePreferenceController.AVAILABLE + } + + private class TestController(context: Context) : BasePreferenceController(context, KEY), + TelephonyAvailabilityHandler { + + var availabilityStatus: Int? = null + override fun getAvailabilityStatus(): Int = STATUS + + override fun setAvailabilityStatus(status: Int) { + availabilityStatus = status + } + + override fun unsetAvailabilityStatus() { + availabilityStatus = null + } + } +}