1 /*
2  * Copyright (C) 2019 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 android.app;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import android.app.NotificationHistory.HistoricalNotification;
22 import android.graphics.drawable.Icon;
23 import android.os.Parcel;
24 
25 import androidx.test.InstrumentationRegistry;
26 import androidx.test.runner.AndroidJUnit4;
27 
28 import org.junit.Test;
29 import org.junit.runner.RunWith;
30 
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Set;
36 
37 @RunWith(AndroidJUnit4.class)
38 public class NotificationHistoryTest {
39 
getHistoricalNotification(int index)40     private HistoricalNotification getHistoricalNotification(int index) {
41         return getHistoricalNotification("package" + index, index);
42     }
43 
getHistoricalNotification(String packageName, int index)44     private HistoricalNotification getHistoricalNotification(String packageName, int index) {
45         String expectedChannelName = "channelName" + index;
46         String expectedChannelId = "channelId" + index;
47         int expectedUid = 1123456 + index;
48         int expectedUserId = 11 + index;
49         long expectedPostTime = 987654321 + index;
50         String expectedTitle = "title" + index;
51         String expectedText = "text" + index;
52         Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(),
53                 index);
54         String conversationId = null;
55         if (index % 2 == 0) {
56             conversationId = "convo" + index;
57         }
58 
59         return new HistoricalNotification.Builder()
60                 .setPackage(packageName)
61                 .setChannelName(expectedChannelName)
62                 .setChannelId(expectedChannelId)
63                 .setUid(expectedUid)
64                 .setUserId(expectedUserId)
65                 .setPostedTimeMs(expectedPostTime)
66                 .setTitle(expectedTitle)
67                 .setText(expectedText)
68                 .setIcon(expectedIcon)
69                 .setConversationId(conversationId)
70                 .build();
71     }
72 
73     @Test
testHistoricalNotificationBuilder()74     public void testHistoricalNotificationBuilder() {
75         String expectedPackage = "package";
76         String expectedChannelName = "channelName";
77         String expectedChannelId = "channelId";
78         int expectedUid = 1123456;
79         int expectedUserId = 11;
80         long expectedPostTime = 987654321;
81         String expectedTitle = "title";
82         String expectedText = "text";
83         Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(),
84                 android.R.drawable.btn_star);
85         String expectedConversationId = "convo";
86 
87         HistoricalNotification n = new HistoricalNotification.Builder()
88                 .setPackage(expectedPackage)
89                 .setChannelName(expectedChannelName)
90                 .setChannelId(expectedChannelId)
91                 .setUid(expectedUid)
92                 .setUserId(expectedUserId)
93                 .setPostedTimeMs(expectedPostTime)
94                 .setTitle(expectedTitle)
95                 .setText(expectedText)
96                 .setIcon(expectedIcon)
97                 .setConversationId(expectedConversationId)
98                 .build();
99 
100         assertThat(n.getPackage()).isEqualTo(expectedPackage);
101         assertThat(n.getChannelName()).isEqualTo(expectedChannelName);
102         assertThat(n.getChannelId()).isEqualTo(expectedChannelId);
103         assertThat(n.getUid()).isEqualTo(expectedUid);
104         assertThat(n.getUserId()).isEqualTo(expectedUserId);
105         assertThat(n.getPostedTimeMs()).isEqualTo(expectedPostTime);
106         assertThat(n.getTitle()).isEqualTo(expectedTitle);
107         assertThat(n.getText()).isEqualTo(expectedText);
108         assertThat(expectedIcon.sameAs(n.getIcon())).isTrue();
109         assertThat(n.getConversationId()).isEqualTo(expectedConversationId);
110     }
111 
112     @Test
testAddNotificationToWrite()113     public void testAddNotificationToWrite() {
114         NotificationHistory history = new NotificationHistory();
115         HistoricalNotification n = getHistoricalNotification(0);
116         HistoricalNotification n2 = getHistoricalNotification(1);
117 
118         history.addNotificationToWrite(n2);
119         history.addNotificationToWrite(n);
120 
121         assertThat(history.getNotificationsToWrite().size()).isEqualTo(2);
122         assertThat(history.getNotificationsToWrite().get(0)).isSameInstanceAs(n2);
123         assertThat(history.getNotificationsToWrite().get(1)).isSameInstanceAs(n);
124         assertThat(history.getHistoryCount()).isEqualTo(2);
125     }
126 
127     @Test
testAddNotificationsToWrite()128     public void testAddNotificationsToWrite() {
129         NotificationHistory history = new NotificationHistory();
130         HistoricalNotification n = getHistoricalNotification(3);
131         HistoricalNotification n2 = getHistoricalNotification(1);
132         HistoricalNotification n5 = getHistoricalNotification(0);
133         history.addNotificationToWrite(n2);
134         history.addNotificationToWrite(n);
135         history.addNotificationToWrite(n5);
136 
137         NotificationHistory secondHistory = new NotificationHistory();
138         HistoricalNotification n3 = getHistoricalNotification(4);
139         HistoricalNotification n4 = getHistoricalNotification(2);
140         secondHistory.addNotificationToWrite(n4);
141         secondHistory.addNotificationToWrite(n3);
142 
143         history.addNotificationsToWrite(secondHistory);
144 
145         assertThat(history.getNotificationsToWrite().size()).isEqualTo(5);
146         assertThat(history.getNotificationsToWrite().get(0)).isSameInstanceAs(n3);
147         assertThat(history.getNotificationsToWrite().get(1)).isSameInstanceAs(n);
148         assertThat(history.getNotificationsToWrite().get(2)).isSameInstanceAs(n4);
149         assertThat(history.getNotificationsToWrite().get(3)).isSameInstanceAs(n2);
150         assertThat(history.getNotificationsToWrite().get(4)).isSameInstanceAs(n5);
151         assertThat(history.getHistoryCount()).isEqualTo(5);
152 
153         assertThat(history.getPooledStringsToWrite()).asList().contains(n2.getChannelName());
154         assertThat(history.getPooledStringsToWrite()).asList().contains(n4.getPackage());
155     }
156 
157     @Test
testPoolStringsFromNotifications()158     public void testPoolStringsFromNotifications() {
159         NotificationHistory history = new NotificationHistory();
160 
161         List<String> expectedStrings = new ArrayList<>();
162         for (int i = 1; i <= 10; i++) {
163             HistoricalNotification n = getHistoricalNotification(i);
164             expectedStrings.add(n.getPackage());
165             expectedStrings.add(n.getChannelName());
166             expectedStrings.add(n.getChannelId());
167             if (n.getConversationId() != null) {
168                 expectedStrings.add(n.getConversationId());
169             }
170             history.addNotificationToWrite(n);
171         }
172 
173         history.poolStringsFromNotifications();
174 
175         assertThat(history.getPooledStringsToWrite().length).isEqualTo(expectedStrings.size());
176         String previous = null;
177         for (String actual : history.getPooledStringsToWrite()) {
178             assertThat(expectedStrings).contains(actual);
179 
180             if (previous != null) {
181                 assertThat(actual).isGreaterThan(previous);
182             }
183             previous = actual;
184         }
185     }
186 
187     @Test
testAddPooledStrings()188     public void testAddPooledStrings() {
189         NotificationHistory history = new NotificationHistory();
190 
191         List<String> expectedStrings = new ArrayList<>();
192         for (int i = 1; i <= 10; i++) {
193             HistoricalNotification n = getHistoricalNotification(i);
194             expectedStrings.add(n.getPackage());
195             expectedStrings.add(n.getChannelName());
196             expectedStrings.add(n.getChannelId());
197             if (n.getConversationId() != null) {
198                 expectedStrings.add(n.getConversationId());
199             }
200             history.addNotificationToWrite(n);
201         }
202 
203         history.addPooledStrings(expectedStrings);
204 
205         String[] actualStrings = history.getPooledStringsToWrite();
206         assertThat(actualStrings.length).isEqualTo(expectedStrings.size());
207         String previous = null;
208         for (String actual : actualStrings) {
209             assertThat(expectedStrings).contains(actual);
210 
211             if (previous != null) {
212                 assertThat(actual).isGreaterThan(previous);
213             }
214             previous = actual;
215         }
216     }
217 
218     @Test
testRemoveNotificationsFromWrite()219     public void testRemoveNotificationsFromWrite() {
220         NotificationHistory history = new NotificationHistory();
221 
222         List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>();
223         List<String> postRemoveExpectedStrings = new ArrayList<>();
224         for (int i = 1; i <= 10; i++) {
225             HistoricalNotification n =
226                     getHistoricalNotification((i % 2 == 0) ? "pkgEven" : "pkgOdd", i);
227 
228             if (i % 2 == 0) {
229                 postRemoveExpectedStrings.add(n.getPackage());
230                 postRemoveExpectedStrings.add(n.getChannelName());
231                 postRemoveExpectedStrings.add(n.getChannelId());
232                 if (n.getConversationId() != null) {
233                     postRemoveExpectedStrings.add(n.getConversationId());
234                 }
235                 postRemoveExpectedEntries.add(n);
236             }
237 
238             history.addNotificationToWrite(n);
239         }
240 
241         history.poolStringsFromNotifications();
242 
243         assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
244         // 2 package names and 10 * 2 unique channel names and ids and 5 conversation ids
245         assertThat(history.getPooledStringsToWrite().length).isEqualTo(27);
246 
247         history.removeNotificationsFromWrite("pkgOdd");
248 
249 
250         // 1 package names and 5 * 2 unique channel names and ids and 5 conversation ids
251         assertThat(history.getPooledStringsToWrite().length).isEqualTo(16);
252         assertThat(history.getNotificationsToWrite())
253                 .containsExactlyElementsIn(postRemoveExpectedEntries);
254     }
255 
256     @Test
testRemoveNotificationFromWrite()257     public void testRemoveNotificationFromWrite() {
258         NotificationHistory history = new NotificationHistory();
259 
260         List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>();
261         List<String> postRemoveExpectedStrings = new ArrayList<>();
262         for (int i = 1; i <= 10; i++) {
263             HistoricalNotification n = getHistoricalNotification("pkg", i);
264 
265             if (987654323 != n.getPostedTimeMs()) {
266                 postRemoveExpectedStrings.add(n.getPackage());
267                 postRemoveExpectedStrings.add(n.getChannelName());
268                 postRemoveExpectedStrings.add(n.getChannelId());
269                 if (n.getConversationId() != null) {
270                     postRemoveExpectedStrings.add(n.getConversationId());
271                 }
272                 postRemoveExpectedEntries.add(n);
273             }
274 
275             history.addNotificationToWrite(n);
276         }
277 
278         history.poolStringsFromNotifications();
279 
280         assertThat(history.getNotificationsToWrite().size()).isEqualTo(10);
281         // 1 package name and 20 unique channel names and ids and 5 conversation ids
282         assertThat(history.getPooledStringsToWrite().length).isEqualTo(26);
283 
284         history.removeNotificationFromWrite("pkg", 987654323);
285 
286 
287         // 1 package names and 9 * 2 unique channel names and ids and 4 conversation ids
288         assertThat(history.getPooledStringsToWrite().length).isEqualTo(23);
289         assertThat(history.getNotificationsToWrite())
290                 .containsExactlyElementsIn(postRemoveExpectedEntries);
291     }
292 
293     @Test
testRemoveConversationNotificationFromWrite()294     public void testRemoveConversationNotificationFromWrite() {
295         NotificationHistory history = new NotificationHistory();
296 
297         List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>();
298         List<String> postRemoveExpectedStrings = new ArrayList<>();
299         for (int i = 1; i <= 10; i++) {
300             HistoricalNotification n = getHistoricalNotification("pkg", i);
301 
302             if (i != 2 && i != 4) {
303                 postRemoveExpectedStrings.add(n.getPackage());
304                 postRemoveExpectedStrings.add(n.getChannelName());
305                 postRemoveExpectedStrings.add(n.getChannelId());
306                 if (n.getConversationId() != null) {
307                     postRemoveExpectedStrings.add(n.getConversationId());
308                 }
309                 postRemoveExpectedEntries.add(n);
310             }
311 
312             history.addNotificationToWrite(n);
313         }
314         // add second notification with the same conversation id that will be removed
315         history.addNotificationToWrite(getHistoricalNotification("pkg", 2));
316 
317         history.poolStringsFromNotifications();
318 
319         assertThat(history.getNotificationsToWrite().size()).isEqualTo(11);
320         // 1 package name and 20 unique channel names and ids and 5 conversation ids
321         assertThat(history.getPooledStringsToWrite().length).isEqualTo(26);
322 
323         history.removeConversationsFromWrite("pkg", Set.of("convo2", "convo4"));
324 
325         // 1 package names and 8 * 2 unique channel names and ids and 3 conversation ids
326         assertThat(history.getPooledStringsToWrite().length).isEqualTo(20);
327         assertThat(history.getNotificationsToWrite())
328                 .containsExactlyElementsIn(postRemoveExpectedEntries);
329     }
330 
331     @Test
testRemoveChannelFromWrite()332     public void testRemoveChannelFromWrite() {
333         NotificationHistory history = new NotificationHistory();
334 
335         List<HistoricalNotification> postRemoveExpectedEntries = new ArrayList<>();
336         Set<String> postRemoveExpectedStrings = new HashSet<>();
337         for (int i = 1; i <= 10; i++) {
338             HistoricalNotification n = getHistoricalNotification("pkg", i);
339 
340             // Remove channel numbers 5 and 6
341             if (i != 5 && i != 6) {
342                 postRemoveExpectedStrings.add(n.getPackage());
343                 postRemoveExpectedStrings.add(n.getChannelName());
344                 postRemoveExpectedStrings.add(n.getChannelId());
345                 if (n.getConversationId() != null) {
346                     postRemoveExpectedStrings.add(n.getConversationId());
347                 }
348                 postRemoveExpectedEntries.add(n);
349             }
350 
351             history.addNotificationToWrite(n);
352         }
353         // add second notification with the same channel id that will also be removed
354         history.addNotificationToWrite(getHistoricalNotification("pkg", 6));
355 
356         history.poolStringsFromNotifications();
357 
358         assertThat(history.getNotificationsToWrite().size()).isEqualTo(11);
359         // 1 package name and 20 unique channel names and ids and 5 conversation ids
360         assertThat(history.getPooledStringsToWrite().length).isEqualTo(26);
361 
362         history.removeChannelFromWrite("pkg", "channelId5");
363         history.removeChannelFromWrite("pkg", "channelId6");
364 
365         // 1 package names and 8 * 2 unique channel names and ids and 4 conversation ids
366         assertThat(history.getPooledStringsToWrite().length).isEqualTo(21);
367         assertThat(Arrays.asList(history.getPooledStringsToWrite()))
368                 .containsExactlyElementsIn(postRemoveExpectedStrings);
369         assertThat(history.getNotificationsToWrite())
370                 .containsExactlyElementsIn(postRemoveExpectedEntries);
371     }
372 
373     @Test
testParceling()374     public void testParceling() {
375         NotificationHistory history = new NotificationHistory();
376 
377         List<HistoricalNotification> expectedEntries = new ArrayList<>();
378         for (int i = 10; i >= 1; i--) {
379             HistoricalNotification n = getHistoricalNotification(i);
380             expectedEntries.add(n);
381             history.addNotificationToWrite(n);
382         }
383         history.poolStringsFromNotifications();
384 
385         Parcel parcel = Parcel.obtain();
386         history.writeToParcel(parcel, 0);
387         parcel.setDataPosition(0);
388         NotificationHistory parceledHistory = NotificationHistory.CREATOR.createFromParcel(parcel);
389 
390         assertThat(parceledHistory.getHistoryCount()).isEqualTo(expectedEntries.size());
391 
392         for (int i = 0; i < expectedEntries.size(); i++) {
393             assertThat(parceledHistory.hasNextNotification()).isTrue();
394 
395             HistoricalNotification postParcelNotification = parceledHistory.getNextNotification();
396             assertThat(postParcelNotification).isEqualTo(expectedEntries.get(i));
397         }
398         assertThat(parceledHistory.hasNextNotification()).isFalse();
399         assertThat(parceledHistory.getNextNotification()).isNull();
400     }
401 }
402