| /* |
| * Copyright (C) 2017 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.notification; |
| |
| import static android.app.Notification.VISIBILITY_PRIVATE; |
| import static android.app.NotificationChannel.DEFAULT_CHANNEL_ID; |
| import static android.app.NotificationManager.IMPORTANCE_DEFAULT; |
| import static android.app.NotificationManager.IMPORTANCE_MIN; |
| import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE; |
| import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS; |
| import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS; |
| import static junit.framework.Assert.assertFalse; |
| import static junit.framework.Assert.assertTrue; |
| import static org.junit.Assert.assertEquals; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.spy; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| |
| import android.app.Notification; |
| import android.app.NotificationChannel; |
| import android.app.NotificationManager; |
| import android.app.admin.DevicePolicyManager; |
| import android.content.ComponentName; |
| import android.content.Context; |
| import android.content.pm.UserInfo; |
| import android.os.UserManager; |
| import android.provider.Settings; |
| |
| import com.android.internal.widget.LockPatternUtils; |
| import com.android.settings.RestrictedListPreference; |
| import com.android.settings.testutils.SettingsRobolectricTestRunner; |
| import com.android.settings.testutils.shadow.ShadowRestrictionUtils; |
| import com.android.settingslib.RestrictedLockUtils; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Answers; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.robolectric.annotation.Config; |
| import org.robolectric.shadows.ShadowApplication; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| @RunWith(SettingsRobolectricTestRunner.class) |
| @Config(shadows = ShadowRestrictionUtils.class) |
| public class VisibilityPreferenceControllerTest { |
| |
| private Context mContext; |
| @Mock |
| private NotificationBackend mBackend; |
| @Mock |
| private NotificationManager mNm; |
| @Mock |
| private LockPatternUtils mLockUtils; |
| @Mock |
| private UserManager mUm; |
| @Mock |
| private DevicePolicyManager mDm; |
| @Mock(answer = Answers.RETURNS_DEEP_STUBS) |
| |
| private VisibilityPreferenceController mController; |
| |
| @Before |
| public void setUp() { |
| MockitoAnnotations.initMocks(this); |
| ShadowApplication shadowApplication = ShadowApplication.getInstance(); |
| shadowApplication.setSystemService(Context.NOTIFICATION_SERVICE, mNm); |
| shadowApplication.setSystemService(Context.USER_SERVICE, mUm); |
| shadowApplication.setSystemService(Context.DEVICE_POLICY_SERVICE, mDm); |
| mContext = shadowApplication.getApplicationContext(); |
| mController = spy(new VisibilityPreferenceController(mContext, mLockUtils, mBackend)); |
| |
| // by default the lockscreen is secure |
| when(mLockUtils.isSecure(anyInt())).thenReturn(true); |
| // and notifications are visible in redacted form |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1); |
| // and not restricted |
| ShadowRestrictionUtils.setRestricted(false); |
| // with no managed profile |
| UserInfo userInfo = new UserInfo(); |
| when(mUm.getUserInfo(anyInt())).thenReturn(userInfo); |
| } |
| |
| @Test |
| public void testNoCrashIfNoOnResume() { |
| mController.isAvailable(); |
| mController.updateState(mock(RestrictedListPreference.class)); |
| mController.onPreferenceChange(mock(RestrictedListPreference.class), true); |
| } |
| |
| @Test |
| public void testIsAvailable_notSecure() { |
| when(mLockUtils.isSecure(anyInt())).thenReturn(false); |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT); |
| mController.onResume(appRow, channel, null, null); |
| assertFalse(mController.isAvailable()); |
| } |
| |
| @Test |
| public void testIsAvailable_notIfNotImportant() { |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = new NotificationChannel("", "", IMPORTANCE_MIN); |
| mController.onResume(appRow, channel, null, null); |
| assertFalse(mController.isAvailable()); |
| } |
| |
| @Test |
| public void testIsAvailable() { |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = |
| new NotificationChannel(DEFAULT_CHANNEL_ID, "", IMPORTANCE_DEFAULT); |
| mController.onResume(appRow, channel, null, null); |
| assertTrue(mController.isAvailable()); |
| |
| channel = new NotificationChannel("", "", IMPORTANCE_DEFAULT); |
| mController.onResume(appRow, channel, null, null); |
| assertTrue(mController.isAvailable()); |
| } |
| |
| @Test |
| public void testUpdateState_disabledByAdmin_disableSecure() { |
| ShadowRestrictionUtils.setRestricted(true); |
| UserInfo userInfo = new UserInfo(2, "user 2", UserInfo.FLAG_MANAGED_PROFILE); |
| when(mUm.getUserInfo(anyInt())).thenReturn(userInfo); |
| List<ComponentName> components = new ArrayList<>(); |
| components.add(new ComponentName("", "")); |
| when(mDm.getActiveAdminsAsUser(anyInt())).thenReturn(components); |
| when(mDm.getKeyguardDisabledFeatures(any(), anyInt())) |
| .thenReturn(KEYGUARD_DISABLE_SECURE_NOTIFICATIONS); |
| |
| NotificationChannel channel = mock(NotificationChannel.class); |
| when(channel.getId()).thenReturn("something"); |
| mController.onResume(new NotificationBackend.AppRow(), channel, null, mock( |
| RestrictedLockUtils.EnforcedAdmin.class)); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| verify(pref, times(2)).addRestrictedItem(any()); |
| } |
| |
| @Test |
| public void testUpdateState_disabledByAdmin_disableUnredacted() { |
| ShadowRestrictionUtils.setRestricted(true); |
| UserInfo userInfo = new UserInfo(2, "user 2", UserInfo.FLAG_MANAGED_PROFILE); |
| when(mUm.getUserInfo(anyInt())).thenReturn(userInfo); |
| List<ComponentName> components = new ArrayList<>(); |
| components.add(new ComponentName("", "")); |
| when(mDm.getActiveAdminsAsUser(anyInt())).thenReturn(components); |
| when(mDm.getKeyguardDisabledFeatures(any(), anyInt())) |
| .thenReturn(KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS); |
| |
| NotificationChannel channel = mock(NotificationChannel.class); |
| when(channel.getId()).thenReturn("something"); |
| mController.onResume(new NotificationBackend.AppRow(), channel, null, mock( |
| RestrictedLockUtils.EnforcedAdmin.class)); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| verify(pref, times(1)).addRestrictedItem(any()); |
| } |
| |
| @Test |
| public void testUpdateState_noLockScreenNotificationsGlobally() { |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0); |
| |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = mock(NotificationChannel.class); |
| mController.onResume(appRow, channel, null, null); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| ArgumentCaptor<CharSequence[]> argumentCaptor = |
| ArgumentCaptor.forClass(CharSequence[].class); |
| verify(pref, times(1)).setEntryValues(argumentCaptor.capture()); |
| assertFalse(toStringList(argumentCaptor.getValue()) |
| .contains(String.valueOf(VISIBILITY_NO_OVERRIDE))); |
| assertFalse(toStringList(argumentCaptor.getValue()) |
| .contains(String.valueOf(VISIBILITY_PRIVATE))); |
| } |
| |
| @Test |
| public void testUpdateState_noLockScreenNotificationsGloballyInProfile() { |
| final int primaryUserId = 2; |
| final UserInfo primaryUserInfo = new UserInfo(primaryUserId, "user 2", 0); |
| when(mUm.getProfileParent(anyInt())).thenReturn(primaryUserInfo); |
| |
| Settings.Secure.putIntForUser(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, primaryUserId); |
| |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = mock(NotificationChannel.class); |
| mController.onResume(appRow, channel, null, null); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| ArgumentCaptor<CharSequence[]> argumentCaptor = |
| ArgumentCaptor.forClass(CharSequence[].class); |
| verify(pref, times(1)).setEntryValues(argumentCaptor.capture()); |
| assertFalse(toStringList(argumentCaptor.getValue()) |
| .contains(String.valueOf(VISIBILITY_NO_OVERRIDE))); |
| assertFalse(toStringList(argumentCaptor.getValue()) |
| .contains(String.valueOf(VISIBILITY_PRIVATE))); |
| } |
| |
| @Test |
| public void testUpdateState_noPrivateLockScreenNotificationsGlobally() { |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1); |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); |
| |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = mock(NotificationChannel.class); |
| mController.onResume(appRow, channel, null, null); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| ArgumentCaptor<CharSequence[]> argumentCaptor = |
| ArgumentCaptor.forClass(CharSequence[].class); |
| verify(pref, times(1)).setEntryValues(argumentCaptor.capture()); |
| assertEquals(2, toStringList(argumentCaptor.getValue()).size()); |
| assertFalse(toStringList(argumentCaptor.getValue()) |
| .contains(String.valueOf(VISIBILITY_NO_OVERRIDE))); |
| } |
| |
| @Test |
| public void testUpdateState_noGlobalRestriction() { |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = mock(NotificationChannel.class); |
| mController.onResume(appRow, channel, null, null); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| ArgumentCaptor<CharSequence[]> argumentCaptor = |
| ArgumentCaptor.forClass(CharSequence[].class); |
| verify(pref, times(1)).setEntryValues(argumentCaptor.capture()); |
| List<String> values = toStringList(argumentCaptor.getValue()); |
| assertEquals(3, values.size()); |
| assertTrue(values.contains(String.valueOf(VISIBILITY_NO_OVERRIDE))); |
| assertTrue(values.contains(String.valueOf(VISIBILITY_PRIVATE))); |
| assertTrue(values.contains(String.valueOf(Notification.VISIBILITY_SECRET))); |
| } |
| |
| private static List<String> toStringList(CharSequence[] charSequences) { |
| List<String> result = new ArrayList<>(); |
| for (CharSequence charSequence : charSequences) { |
| result.add(charSequence.toString()); |
| } |
| return result; |
| } |
| |
| @Test |
| public void testUpdateState_noChannelOverride() { |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); |
| |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = mock(NotificationChannel.class); |
| when(channel.getLockscreenVisibility()).thenReturn(VISIBILITY_NO_OVERRIDE); |
| mController.onResume(appRow, channel, null, null); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class); |
| verify(pref, times(1)).setValue(argumentCaptor.capture()); |
| |
| assertEquals(String.valueOf(VISIBILITY_PRIVATE), argumentCaptor.getValue()); |
| } |
| |
| @Test |
| public void testUpdateState_channelOverride() { |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); |
| |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = mock(NotificationChannel.class); |
| when(channel.getLockscreenVisibility()).thenReturn(Notification.VISIBILITY_SECRET); |
| mController.onResume(appRow, channel, null, null); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class); |
| verify(pref, times(1)).setValue(argumentCaptor.capture()); |
| |
| assertEquals(String.valueOf(Notification.VISIBILITY_SECRET), argumentCaptor.getValue()); |
| } |
| |
| @Test |
| public void testOnPreferenceChange_noOverride() { |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); |
| |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = new NotificationChannel("", "", 4); |
| channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); |
| mController.onResume(appRow, channel, null, null); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| mController.onPreferenceChange(pref, String.valueOf(VISIBILITY_PRIVATE)); |
| |
| assertEquals(VISIBILITY_NO_OVERRIDE, channel.getLockscreenVisibility()); |
| verify(mBackend, times(1)).updateChannel(any(), anyInt(), any()); |
| } |
| |
| @Test |
| public void testOnPreferenceChange_override() { |
| Settings.Secure.putInt(mContext.getContentResolver(), |
| Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0); |
| |
| NotificationBackend.AppRow appRow = new NotificationBackend.AppRow(); |
| NotificationChannel channel = new NotificationChannel("", "", 4); |
| channel.setLockscreenVisibility(VISIBILITY_NO_OVERRIDE); |
| mController.onResume(appRow, channel, null, null); |
| |
| RestrictedListPreference pref = mock(RestrictedListPreference.class); |
| mController.updateState(pref); |
| |
| mController.onPreferenceChange(pref, String.valueOf(Notification.VISIBILITY_SECRET)); |
| |
| assertEquals(Notification.VISIBILITY_SECRET, channel.getLockscreenVisibility()); |
| verify(mBackend, times(1)).updateChannel(any(), anyInt(), any()); |
| } |
| } |