1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.keyguard; 18 19 import static org.mockito.ArgumentMatchers.any; 20 import static org.mockito.ArgumentMatchers.anyString; 21 import static org.mockito.ArgumentMatchers.eq; 22 import static org.mockito.Mockito.clearInvocations; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.reset; 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 30 import android.app.AlarmManager; 31 import android.content.ContentResolver; 32 import android.media.MediaMetadata; 33 import android.media.session.PlaybackState; 34 import android.net.Uri; 35 import android.os.Handler; 36 import android.provider.Settings; 37 import android.testing.AndroidTestingRunner; 38 import android.testing.TestableLooper; 39 import android.testing.TestableLooper.RunWithLooper; 40 41 import androidx.slice.Slice; 42 import androidx.slice.SliceItem; 43 import androidx.slice.SliceProvider; 44 import androidx.slice.SliceSpecs; 45 import androidx.slice.builders.ListBuilder; 46 import androidx.slice.core.SliceQuery; 47 import androidx.test.filters.SmallTest; 48 49 import com.android.keyguard.KeyguardUpdateMonitor; 50 import com.android.systemui.SystemUIInitializerImpl; 51 import com.android.systemui.SysuiTestCase; 52 import com.android.systemui.plugins.statusbar.StatusBarStateController; 53 import com.android.systemui.settings.UserTracker; 54 import com.android.systemui.statusbar.NotificationMediaManager; 55 import com.android.systemui.statusbar.StatusBarState; 56 import com.android.systemui.statusbar.phone.DozeParameters; 57 import com.android.systemui.statusbar.phone.KeyguardBypassController; 58 import com.android.systemui.statusbar.policy.NextAlarmController; 59 import com.android.systemui.statusbar.policy.ZenModeController; 60 import com.android.systemui.util.wakelock.SettableWakeLock; 61 62 import org.junit.After; 63 import org.junit.Assert; 64 import org.junit.Before; 65 import org.junit.Test; 66 import org.junit.runner.RunWith; 67 import org.mockito.Mock; 68 import org.mockito.MockitoAnnotations; 69 70 import java.util.Arrays; 71 import java.util.HashSet; 72 import java.util.concurrent.TimeUnit; 73 74 @SmallTest 75 @RunWith(AndroidTestingRunner.class) 76 @RunWithLooper 77 public class KeyguardSliceProviderTest extends SysuiTestCase { 78 79 @Mock 80 private ContentResolver mContentResolver; 81 @Mock 82 private AlarmManager mAlarmManager; 83 @Mock 84 private NotificationMediaManager mNotificationMediaManager; 85 @Mock 86 private StatusBarStateController mStatusBarStateController; 87 @Mock 88 private KeyguardBypassController mKeyguardBypassController; 89 @Mock 90 private ZenModeController mZenModeController; 91 @Mock 92 private SettableWakeLock mMediaWakeLock; 93 @Mock 94 private DozeParameters mDozeParameters; 95 @Mock 96 private NextAlarmController mNextAlarmController; 97 @Mock 98 private KeyguardUpdateMonitor mKeyguardUpdateMonitor; 99 @Mock 100 private UserTracker mUserTracker; 101 private TestableKeyguardSliceProvider mProvider; 102 private boolean mIsZenMode; 103 104 @Before setup()105 public void setup() { 106 MockitoAnnotations.initMocks(this); 107 mIsZenMode = false; 108 mProvider = new TestableKeyguardSliceProvider(); 109 mProvider.setContextAvailableCallback(context -> new SystemUIInitializerImpl(mContext)); 110 mProvider.attachInfo(getContext(), null); 111 reset(mContentResolver); 112 SliceProvider.setSpecs(new HashSet<>(Arrays.asList(SliceSpecs.LIST))); 113 when(mUserTracker.getUserId()).thenReturn(100); 114 } 115 116 @After tearDown()117 public void tearDown() { 118 mProvider.onDestroy(); 119 } 120 121 @Test registersClockUpdate()122 public void registersClockUpdate() { 123 Assert.assertTrue("registerClockUpdate should have been called during initialization.", 124 mProvider.isRegistered()); 125 } 126 127 @Test returnsValidSlice()128 public void returnsValidSlice() { 129 Slice slice = mProvider.onBindSlice(mProvider.getUri()); 130 SliceItem text = SliceQuery.find(slice, android.app.slice.SliceItem.FORMAT_TEXT, 131 android.app.slice.Slice.HINT_TITLE, 132 null /* nonHints */); 133 Assert.assertNotNull("Slice must provide a title.", text); 134 } 135 136 @Test onBindSlice_readsMedia_withoutBypass()137 public void onBindSlice_readsMedia_withoutBypass() { 138 MediaMetadata metadata = mock(MediaMetadata.class); 139 when(metadata.getText(any())).thenReturn("metadata"); 140 mProvider.onDozingChanged(true); 141 mProvider.onPrimaryMetadataOrStateChanged(metadata, PlaybackState.STATE_PLAYING); 142 mProvider.onBindSlice(mProvider.getUri()); 143 verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE)); 144 verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_ARTIST)); 145 verify(mNotificationMediaManager).getMediaIcon(); 146 } 147 148 @Test onBindSlice_readsMedia_withBypass_notDozing()149 public void onBindSlice_readsMedia_withBypass_notDozing() { 150 MediaMetadata metadata = mock(MediaMetadata.class); 151 when(metadata.getText(any())).thenReturn("metadata"); 152 when(mKeyguardBypassController.getBypassEnabled()).thenReturn(true); 153 when(mDozeParameters.getAlwaysOn()).thenReturn(true); 154 mProvider.onPrimaryMetadataOrStateChanged(metadata, PlaybackState.STATE_PLAYING); 155 mProvider.onBindSlice(mProvider.getUri()); 156 verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_TITLE)); 157 verify(metadata).getText(eq(MediaMetadata.METADATA_KEY_ARTIST)); 158 verify(mNotificationMediaManager).getMediaIcon(); 159 } 160 161 @Test cleansDateFormat()162 public void cleansDateFormat() { 163 mProvider.mKeyguardUpdateMonitorCallback.onTimeZoneChanged(null); 164 TestableLooper.get(this).processAllMessages(); 165 Assert.assertEquals("Date format should have been cleaned.", 1 /* expected */, 166 mProvider.mCleanDateFormatInvokations); 167 } 168 169 @Test updatesClock()170 public void updatesClock() { 171 clearInvocations(mContentResolver); 172 mProvider.mKeyguardUpdateMonitorCallback.onTimeChanged(); 173 TestableLooper.get(this).processAllMessages(); 174 verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); 175 } 176 177 @Test schedulesAlarm12hBefore()178 public void schedulesAlarm12hBefore() { 179 long in16Hours = System.currentTimeMillis() + TimeUnit.HOURS.toHours(16); 180 AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours, null); 181 mProvider.onNextAlarmChanged(alarmClockInfo); 182 183 long twelveHours = TimeUnit.HOURS.toMillis(KeyguardSliceProvider.ALARM_VISIBILITY_HOURS); 184 long triggerAt = in16Hours - twelveHours; 185 verify(mAlarmManager).setExact(eq(AlarmManager.RTC), eq(triggerAt), anyString(), any(), 186 any()); 187 } 188 189 @Test updatingNextAlarmInvalidatesSlice()190 public void updatingNextAlarmInvalidatesSlice() { 191 long in16Hours = System.currentTimeMillis() + TimeUnit.HOURS.toHours(8); 192 AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(in16Hours, null); 193 mProvider.onNextAlarmChanged(alarmClockInfo); 194 195 verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); 196 } 197 198 @Test onZenChanged_updatesSlice()199 public void onZenChanged_updatesSlice() { 200 mProvider.onZenChanged(Settings.Global.ZEN_MODE_ALARMS); 201 verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); 202 } 203 204 @Test addZenMode_addedToSlice()205 public void addZenMode_addedToSlice() { 206 ListBuilder listBuilder = spy(new ListBuilder(getContext(), mProvider.getUri(), 207 ListBuilder.INFINITY)); 208 mProvider.addZenModeLocked(listBuilder); 209 verify(listBuilder, never()).addRow(any(ListBuilder.RowBuilder.class)); 210 211 mIsZenMode = true; 212 mProvider.addZenModeLocked(listBuilder); 213 verify(listBuilder).addRow(any(ListBuilder.RowBuilder.class)); 214 } 215 216 @Test onMetadataChanged_updatesSlice()217 public void onMetadataChanged_updatesSlice() { 218 mProvider.onStateChanged(StatusBarState.KEYGUARD); 219 mProvider.onDozingChanged(true); 220 reset(mContentResolver); 221 mProvider.onPrimaryMetadataOrStateChanged(mock(MediaMetadata.class), 222 PlaybackState.STATE_PLAYING); 223 TestableLooper.get(this).processAllMessages(); 224 verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); 225 226 // Hides after waking up 227 reset(mContentResolver); 228 mProvider.onDozingChanged(false); 229 TestableLooper.get(this).processAllMessages(); 230 verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); 231 } 232 233 @Test onDozingChanged_updatesSliceIfMedia()234 public void onDozingChanged_updatesSliceIfMedia() { 235 mProvider.onStateChanged(StatusBarState.KEYGUARD); 236 mProvider.onPrimaryMetadataOrStateChanged(mock(MediaMetadata.class), 237 PlaybackState.STATE_PLAYING); 238 reset(mContentResolver); 239 TestableLooper.get(this).processAllMessages(); 240 // Show media when dozing 241 mProvider.onDozingChanged(true); 242 verify(mContentResolver).notifyChange(eq(mProvider.getUri()), eq(null)); 243 244 // Do not notify again if nothing changed 245 reset(mContentResolver); 246 mProvider.onDozingChanged(true); 247 verify(mContentResolver, never()).notifyChange(eq(mProvider.getUri()), eq(null)); 248 } 249 250 @Test onDestroy_noCrash()251 public void onDestroy_noCrash() { 252 mProvider.onDestroy(); 253 } 254 255 @Test onDestroy_unregisterListeners()256 public void onDestroy_unregisterListeners() { 257 mProvider.registerClockUpdate(); 258 mProvider.onDestroy(); 259 verify(mMediaWakeLock).setAcquired(eq(false)); 260 verify(mAlarmManager).cancel(any(AlarmManager.OnAlarmListener.class)); 261 verify(mKeyguardUpdateMonitor).removeCallback(any()); 262 } 263 264 private class TestableKeyguardSliceProvider extends KeyguardSliceProvider { 265 int mCleanDateFormatInvokations; 266 private int mCounter; 267 TestableKeyguardSliceProvider()268 TestableKeyguardSliceProvider() { 269 super(); 270 271 mAlarmManager = KeyguardSliceProviderTest.this.mAlarmManager; 272 mContentResolver = KeyguardSliceProviderTest.this.mContentResolver; 273 mZenModeController = KeyguardSliceProviderTest.this.mZenModeController; 274 mDozeParameters = KeyguardSliceProviderTest.this.mDozeParameters; 275 mNextAlarmController = KeyguardSliceProviderTest.this.mNextAlarmController; 276 mStatusBarStateController = KeyguardSliceProviderTest.this.mStatusBarStateController; 277 mKeyguardBypassController = KeyguardSliceProviderTest.this.mKeyguardBypassController; 278 mMediaManager = KeyguardSliceProviderTest.this.mNotificationMediaManager; 279 mKeyguardUpdateMonitor = KeyguardSliceProviderTest.this.mKeyguardUpdateMonitor; 280 mUserTracker = KeyguardSliceProviderTest.this.mUserTracker; 281 mBgHandler = 282 new Handler(TestableLooper.get(KeyguardSliceProviderTest.this).getLooper()); 283 } 284 285 @Override onCreateSliceProvider()286 public boolean onCreateSliceProvider() { 287 boolean result = super.onCreateSliceProvider(); 288 mMediaWakeLock = KeyguardSliceProviderTest.this.mMediaWakeLock; 289 return result; 290 } 291 getUri()292 Uri getUri() { 293 return mSliceUri; 294 } 295 296 @Override isDndOn()297 protected boolean isDndOn() { 298 return mIsZenMode; 299 } 300 301 @Override cleanDateFormatLocked()302 void cleanDateFormatLocked() { 303 super.cleanDateFormatLocked(); 304 mCleanDateFormatInvokations++; 305 } 306 307 @Override getFormattedDateLocked()308 protected String getFormattedDateLocked() { 309 return super.getFormattedDateLocked() + mCounter++; 310 } 311 } 312 313 } 314