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