Add Unit Test for NotificationMgr
-Make NotificationMgr constructor package private
-Cover UT cases for all supported notifications.
Bug: 242451629
Test: atest NotificationMgrTest
Change-Id: I49395b7ddeb2a933fc92287c251de43e659ce819
diff --git a/src/com/android/phone/NotificationMgr.java b/src/com/android/phone/NotificationMgr.java
index f2641a1..b9d1f53 100644
--- a/src/com/android/phone/NotificationMgr.java
+++ b/src/com/android/phone/NotificationMgr.java
@@ -57,6 +57,7 @@
import android.util.SparseArray;
import android.widget.Toast;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import com.android.internal.telephony.TelephonyCapabilities;
@@ -163,7 +164,8 @@
* Private constructor (this is a singleton).
* @see #init(PhoneGlobals)
*/
- private NotificationMgr(PhoneGlobals app) {
+ @VisibleForTesting
+ /* package */ NotificationMgr(PhoneGlobals app) {
mApp = app;
mContext = app;
mStatusBarManager =
diff --git a/tests/src/com/android/phone/NotificationMgrTest.java b/tests/src/com/android/phone/NotificationMgrTest.java
new file mode 100644
index 0000000..8a63e54
--- /dev/null
+++ b/tests/src/com/android/phone/NotificationMgrTest.java
@@ -0,0 +1,344 @@
+/*
+ * 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.phone;
+
+import static com.android.phone.NotificationMgr.DATA_ROAMING_NOTIFICATION;
+import static com.android.phone.NotificationMgr.LIMITED_SIM_FUNCTION_NOTIFICATION;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.StatusBarManager;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+import android.content.res.Resources;
+import android.os.Build;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.telecom.TelecomManager;
+import android.telephony.ServiceState;
+import android.telephony.SubscriptionInfo;
+import android.telephony.SubscriptionManager;
+import android.telephony.TelephonyManager;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.internal.telephony.Phone;
+import com.android.internal.telephony.PhoneConstants;
+import com.android.internal.telephony.PhoneFactory;
+import com.android.internal.telephony.util.NotificationChannelController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.reflect.Field;
+
+/**
+ * Unit Test for NotificationMgr
+ */
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationMgrTest {
+
+ private static final int TEST_SUB_ID = 1;
+ private static final long SERIAL_NUMBER_OF_USER = 1234567L;
+ private static final String TEST_LABEL_CF = "test_call_forwarding";
+ private static final String TEST_SUB_INFO_DISPLAY_NAME = "display_name";
+ private static final String TEST_PACKAGE_NAME = "com.android.phone";
+ private static final String TEST_SELECTED_NETWORK_OPERATOR_NAME = "TheOperator";
+ private static final String MOBILE_NETWORK_SELECTION_PACKAGE = "com.android.phone";
+ private static final String MOBILE_NETWORK_SELECTION_CLASS = ".testClass";
+ private static final String CARRIER_NAME = "CoolCarrier";
+
+ @Mock PhoneGlobals mApp;
+ @Mock StatusBarManager mStatusBarManager;
+ @Mock UserManager mUserManager;
+ @Mock SubscriptionManager mSubscriptionManager;
+ @Mock TelecomManager mTelecomManager;
+ @Mock TelephonyManager mTelephonyManager;
+ @Mock Phone mPhone;
+ @Mock SharedPreferences mSharedPreferences;
+ @Mock NotificationManager mNotificationManager;
+ @Mock SubscriptionInfo mSubscriptionInfo;
+ @Mock Resources mResources;
+ @Mock ServiceState mServiceState;
+
+ private Phone[] mPhones;
+ private NotificationMgr mNotificationMgr;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mPhones = new Phone[]{mPhone};
+ replaceInstance(PhoneFactory.class, "sPhones", null, mPhones);
+ when(mPhone.getPhoneType()).thenReturn(PhoneConstants.PHONE_TYPE_GSM);
+ when(mApp.getSharedPreferences(anyString(), anyInt())).thenReturn(mSharedPreferences);
+
+ when(mApp.getPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ when(mApp.getSystemService(Context.STATUS_BAR_SERVICE)).thenReturn(mStatusBarManager);
+ when(mApp.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
+ when(mApp.getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE)).thenReturn(
+ mSubscriptionManager);
+ when(mApp.getSystemServiceName(TelecomManager.class)).thenReturn(Context.TELECOM_SERVICE);
+ when(mApp.getSystemService(TelecomManager.class)).thenReturn(mTelecomManager);
+ when(mApp.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
+
+ when(mApp.createPackageContextAsUser(any(), eq(0), any())).thenReturn(mApp);
+ when(mApp.getSystemService(Context.NOTIFICATION_SERVICE)).thenReturn(mNotificationManager);
+ when(mUserManager.getSerialNumbersOfUsers(true)).thenReturn(
+ new long[]{SERIAL_NUMBER_OF_USER});
+ when(mUserManager.getUserForSerialNumber(eq(SERIAL_NUMBER_OF_USER))).thenReturn(
+ UserHandle.SYSTEM);
+ when(mApp.getResources()).thenReturn(mResources);
+ when(mResources.getString(R.string.labelCF)).thenReturn(TEST_LABEL_CF);
+ ApplicationInfo appWithSdkS = buildApplicationInfo(Build.VERSION_CODES.S);
+ when(mApp.getApplicationInfo()).thenReturn(appWithSdkS);
+ when(mTelephonyManager.createForSubscriptionId(anyInt())).thenReturn(mTelephonyManager);
+ when(mTelephonyManager.getServiceState()).thenReturn(mServiceState);
+
+ mNotificationMgr = new NotificationMgr(mApp);
+ }
+
+ @Test
+ public void testUpdateCfi_visible_noActiveSubscription_notificationNeverSent()
+ throws Exception {
+ // Given no active subscription available
+ when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(null);
+
+ // When updateCfi method is called
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/true, /*isFresh=*/false);
+
+ // Then the notification should never be sent
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateCfi_visible_hasActiveSub_singleSIM_notificationSent()
+ throws Exception {
+ when(mTelephonyManager.getPhoneCount()).thenReturn(1);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(
+ mSubscriptionInfo);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/true, /*isFresh=*/false);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD);
+ }
+
+ @Test
+ public void testUpdateCfi_visible_hasActiveSub_multiSIM_notificationSentWithoutDisplayName()
+ throws Exception {
+ when(mTelephonyManager.getPhoneCount()).thenReturn(2);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(
+ mSubscriptionInfo);
+ when(mSubscriptionInfo.getDisplayName()).thenReturn(null);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/true, /*isFresh=*/false);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD);
+ }
+
+ @Test
+ public void testUpdateCfi_visible_hasActiveSub_multiSIM_notificationSentWithDisplayName()
+ throws Exception {
+ when(mTelephonyManager.getPhoneCount()).thenReturn(2);
+ when(mSubscriptionManager.getActiveSubscriptionInfo(eq(TEST_SUB_ID))).thenReturn(
+ mSubscriptionInfo);
+ when(mSubscriptionInfo.getDisplayName()).thenReturn(TEST_SUB_INFO_DISPLAY_NAME);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/true, /*isFresh=*/false);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_CALL_FORWARD);
+ }
+
+ @Test
+ public void testUpdateCfi_invisible_hasUnmanagedProfile_notificationCanceled()
+ throws Exception {
+ when(mUserManager.isManagedProfile(anyInt())).thenReturn(false);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/false, /*isFresh=*/false);
+
+ verify(mNotificationManager).cancel(any(), anyInt());
+ }
+
+ @Test
+ public void testUpdateCfi_invisible_allProfilesAreManaged_notificationNeverCanceled()
+ throws Exception {
+ when(mUserManager.isManagedProfile(anyInt())).thenReturn(true);
+
+ mNotificationMgr.updateCfi(TEST_SUB_ID, /*visible=*/false, /*isFresh=*/false);
+
+ verify(mNotificationManager, never()).cancel(any(), anyInt());
+ }
+
+ @Test
+ public void testShowDataRoamingNotification_roamingOn() throws Exception {
+ mNotificationMgr.showDataRoamingNotification(TEST_SUB_ID, /*roamingOn=*/true);
+
+ verifyNotificationSentWithChannelId(
+ NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS);
+ }
+
+ @Test
+ public void testShowDataRoamingNotification_roamingOff() throws Exception {
+ mNotificationMgr.showDataRoamingNotification(TEST_SUB_ID, /*roamingOn=*/false);
+
+ verifyNotificationSentWithChannelId(
+ NotificationChannelController.CHANNEL_ID_MOBILE_DATA_STATUS);
+ }
+
+ @Test
+ public void testHideDataRoamingNotification() {
+ mNotificationMgr.hideDataRoamingNotification();
+
+ verify(mNotificationManager).cancel(any(), eq(DATA_ROAMING_NOTIFICATION));
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_justOutOfService_notificationNeverSent()
+ throws Exception {
+ prepareResourcesForNetworkSelection();
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ try {
+ Thread.sleep(2000);
+ } catch (InterruptedException ignored) {
+ }
+
+ verify(mNotificationManager, never()).notify(any(), anyInt(), any());
+ }
+
+ @Test
+ public void testUpdateNetworkSelection_outOfServiceForEnoughTime_notificationSent()
+ throws Exception {
+ prepareResourcesForNetworkSelection();
+
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+ // TODO: use effective TestLooper time eclipse instead of sleeping
+ try {
+ Thread.sleep(10000);
+ } catch (InterruptedException ignored) {
+ }
+ mNotificationMgr.updateNetworkSelection(ServiceState.STATE_OUT_OF_SERVICE, TEST_SUB_ID);
+
+ verifyNotificationSentWithChannelId(NotificationChannelController.CHANNEL_ID_ALERT);
+ }
+
+ @Test
+ public void testShowLimitedSimFunctionWarningNotification_forTheFirstTime_notificationSent()
+ throws Exception {
+ when(mResources.getText(R.string.limited_sim_function_notification_message)).thenReturn(
+ CARRIER_NAME);
+ when(mResources.getText(
+ R.string.limited_sim_function_with_phone_num_notification_message)).thenReturn(
+ "123");
+
+ mNotificationMgr.showLimitedSimFunctionWarningNotification(TEST_SUB_ID, CARRIER_NAME);
+
+ verifyNotificationSentWithChannelId(
+ NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY);
+ }
+
+ @Test
+ public void testShowLimitedSimFunctionWarningNotification_consecutiveCall_notificationSentOnce()
+ throws Exception {
+ when(mResources.getText(R.string.limited_sim_function_notification_message)).thenReturn(
+ CARRIER_NAME);
+ when(mResources.getText(
+ R.string.limited_sim_function_with_phone_num_notification_message)).thenReturn(
+ "123");
+
+ // Call the method TWICE with the same subscription
+ mNotificationMgr.showLimitedSimFunctionWarningNotification(TEST_SUB_ID, CARRIER_NAME);
+ mNotificationMgr.showLimitedSimFunctionWarningNotification(TEST_SUB_ID, CARRIER_NAME);
+
+ // Verify the notification is only sent ONCE
+ verifyNotificationSentWithChannelId(
+ NotificationChannelController.CHANNEL_ID_SIM_HIGH_PRIORITY);
+ }
+
+ @Test
+ public void testDismissLimitedSimFunctionWarningNotification_noShowCalledBefore_noCancelSent()
+ throws Exception {
+ // showLimitedSimFunctionWarningNotification was never called before
+
+ mNotificationMgr.dismissLimitedSimFunctionWarningNotification(TEST_SUB_ID);
+
+ verify(mNotificationManager, never()).cancel(any(), anyInt());
+ }
+
+ @Test
+ public void testDismissLimitedSimFunctionWarningNotification_showCalledBefore_cancelSent()
+ throws Exception {
+ when(mResources.getText(R.string.limited_sim_function_notification_message)).thenReturn(
+ CARRIER_NAME);
+ when(mResources.getText(
+ R.string.limited_sim_function_with_phone_num_notification_message)).thenReturn(
+ "123");
+ mNotificationMgr.showLimitedSimFunctionWarningNotification(TEST_SUB_ID, CARRIER_NAME);
+
+ mNotificationMgr.dismissLimitedSimFunctionWarningNotification(TEST_SUB_ID);
+
+ verify(mNotificationManager).cancel(any(), eq(LIMITED_SIM_FUNCTION_NOTIFICATION));
+ }
+
+ private ApplicationInfo buildApplicationInfo(int targetSdkVersion) {
+ ApplicationInfo applicationInfo = new ApplicationInfo();
+ applicationInfo.targetSdkVersion = targetSdkVersion;
+ return applicationInfo;
+ }
+
+ private void verifyNotificationSentWithChannelId(String expectedNotificationChannelId) {
+ ArgumentCaptor<Notification> notificationArgumentCaptor = ArgumentCaptor.forClass(
+ Notification.class);
+ verify(mNotificationManager).notify(any(), anyInt(), notificationArgumentCaptor.capture());
+ Notification capturedNotification = notificationArgumentCaptor.getAllValues().get(0);
+ assertThat(capturedNotification.getChannelId()).isEqualTo(expectedNotificationChannelId);
+ }
+
+ private void prepareResourcesForNetworkSelection() {
+ when(mSharedPreferences.getString(Phone.NETWORK_SELECTION_NAME_KEY + TEST_SUB_ID,
+ "")).thenReturn(TEST_SELECTED_NETWORK_OPERATOR_NAME);
+ when(mResources.getBoolean(
+ com.android.internal.R.bool.skip_restoring_network_selection)).thenReturn(false);
+ when(mServiceState.getState()).thenReturn(ServiceState.STATE_OUT_OF_SERVICE);
+ when(mApp.getString(R.string.mobile_network_settings_package)).thenReturn(
+ MOBILE_NETWORK_SELECTION_PACKAGE);
+ when(mApp.getString(R.string.mobile_network_settings_class)).thenReturn(
+ MOBILE_NETWORK_SELECTION_CLASS);
+ }
+
+ private static void replaceInstance(final Class c,
+ final String instanceName, final Object obj, final Object newValue) throws Exception {
+ Field field = c.getDeclaredField(instanceName);
+ field.setAccessible(true);
+ field.set(obj, newValue);
+ }
+}