From 887ec6c32c897e52e1e7af7301e5b6a1f2b8dd26 Mon Sep 17 00:00:00 2001 From: Bonian Chen Date: Thu, 21 Apr 2022 11:13:48 +0800 Subject: [PATCH] [Settings] Code refactor for monitoring voice call status This is for monitoring the status of voice call when Lifecycle state STARTED or RESUMED. Bug: 229689535 Test: unit test Change-Id: Ifa3e0c5aa474a2539ad66338ea8c3564ea33744e --- .../network/helper/VoiceCallStatus.java | 102 ++++++++++++++++++ .../network/helper/VoiceCallStatusTest.java | 95 ++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 src/com/android/settings/network/helper/VoiceCallStatus.java create mode 100644 tests/unit/src/com/android/settings/network/helper/VoiceCallStatusTest.java diff --git a/src/com/android/settings/network/helper/VoiceCallStatus.java b/src/com/android/settings/network/helper/VoiceCallStatus.java new file mode 100644 index 00000000000..a5252cb193b --- /dev/null +++ b/src/com/android/settings/network/helper/VoiceCallStatus.java @@ -0,0 +1,102 @@ +/* + * 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.settings.network.helper; + +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyManager; + +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LiveData; + +import java.util.concurrent.Executor; +import java.util.function.Consumer; + +/** + * A {@link LiveData} as a mapping of voice call state reported from {@link TelephonyCallback}. + * Only got update when Lifecycle.State is considered as STARTED or RESUMED. + * + * {@code null} when status unknown. Other values are TelephonyManager#CALL_STATE_IDLE, + * TelephonyManager#CALL_STATE_RINGING and TelephonyManager#CALL_STATE_OFFHOOK. + */ +@VisibleForTesting +public class VoiceCallStatus extends LiveData { + private static final String TAG = "VoiceCallStatus"; + + @VisibleForTesting + protected CallStateProducer mCallStateProducer; + + @VisibleForTesting + protected LifecycleCallbackTelephonyAdapter mAdapter; + + @VisibleForTesting + protected Consumer mLiveDataUpdater = status -> setValue(status); + + /** + * Constructor + * @param lifecycle {@link Lifecycle} to monitor + * @param telephonyManager {@link TelephonyManager} to interact with + * @param executor {@link Executor} for receiving the notify from telephony framework. + */ + @VisibleForTesting + public VoiceCallStatus(@NonNull Lifecycle lifecycle, + @NonNull TelephonyManager telephonyManager, Executor executor) { + super(); + + mCallStateProducer = new CallStateProducer(this); + + mAdapter = new LifecycleCallbackTelephonyAdapter(lifecycle, + telephonyManager, mCallStateProducer, executor, mLiveDataUpdater) { + @Override + public void setCallbackActive(boolean isActive) { + super.setCallbackActive(isActive); + if (!isActive) { + /** + * Set to unknown status when no longer actively monitoring + * {@link TelephonyCallback}. + */ + mLiveDataUpdater.accept(null); + } + } + }; + } + + /** + * An implementation of TelephonyCallback. + * + * Status of voice call will be forward to {@link LifecycleCallbackTelephonyAdapter} + */ + @VisibleForTesting + protected static class CallStateProducer extends TelephonyCallback + implements TelephonyCallback.CallStateListener { + private final VoiceCallStatus mStatus; + + /** + * Constructor + * @param status {@link VoiceCallStatus} + */ + public CallStateProducer(VoiceCallStatus status) { + mStatus = status; + } + + @Override + public void onCallStateChanged(int state) { + mStatus.mAdapter.postResult(state); + } + } +} diff --git a/tests/unit/src/com/android/settings/network/helper/VoiceCallStatusTest.java b/tests/unit/src/com/android/settings/network/helper/VoiceCallStatusTest.java new file mode 100644 index 00000000000..3d7489275f9 --- /dev/null +++ b/tests/unit/src/com/android/settings/network/helper/VoiceCallStatusTest.java @@ -0,0 +1,95 @@ +/* + * 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.settings.network.helper; + +import static com.google.common.truth.Truth.assertThat; + +import android.telephony.TelephonyManager; + +import androidx.lifecycle.Lifecycle; +import androidx.lifecycle.LifecycleOwner; +import androidx.lifecycle.LifecycleRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.concurrent.atomic.AtomicReference; + +@RunWith(AndroidJUnit4.class) +public class VoiceCallStatusTest implements LifecycleOwner { + + private final LifecycleRegistry mRegistry = LifecycleRegistry.createUnsafe(this); + + @Mock + private TelephonyManager mTelMgr; + + private AtomicReference mStatusStorage; + private VoiceCallStatus mVoiceCallStatus; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + + mStatusStorage = new AtomicReference(); + mVoiceCallStatus = new VoiceCallStatus(getLifecycle(), mTelMgr, null) { + //ArchTaskExecutor.getMainThreadExecutor()) { + @Override + protected void setValue(Integer status) { + mStatusStorage.set(status); + } + }; + } + + public Lifecycle getLifecycle() { + return mRegistry; + } + + @Test + public void telephonyCallback_updateStatus_whenActive() { + mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); + + mVoiceCallStatus.mCallStateProducer.onCallStateChanged( + TelephonyManager.CALL_STATE_RINGING); + + assertThat(mStatusStorage.get()).isEqualTo(null); + + mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START); + + mVoiceCallStatus.mCallStateProducer.onCallStateChanged( + TelephonyManager.CALL_STATE_OFFHOOK); + + assertThat(mStatusStorage.get()).isEqualTo(TelephonyManager.CALL_STATE_OFFHOOK); + } + + @Test + public void telephonyCallback_updateStatusToNull_whenInActive() { + mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); + mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START); + + mVoiceCallStatus.mCallStateProducer.onCallStateChanged( + TelephonyManager.CALL_STATE_OFFHOOK); + + assertThat(mStatusStorage.get()).isEqualTo(TelephonyManager.CALL_STATE_OFFHOOK); + + mRegistry.handleLifecycleEvent(Lifecycle.Event.ON_STOP); + + assertThat(mStatusStorage.get()).isEqualTo(null); + } +}