1 /*
2  * Copyright (C) 2013 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.cts.verifier.notifications;
18 
19 import android.annotation.SuppressLint;
20 import android.app.Activity;
21 import android.app.Notification;
22 import android.provider.Settings.Secure;
23 import android.util.Log;
24 import android.view.View;
25 import android.view.ViewGroup;
26 
27 import com.android.cts.verifier.R;
28 
29 import org.json.JSONException;
30 import org.json.JSONObject;
31 
32 import java.util.ArrayList;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Set;
36 import java.util.UUID;
37 
38 import static com.android.cts.verifier.notifications.MockListener.*;
39 
40 public class NotificationListenerVerifierActivity extends InteractiveVerifierActivity
41         implements Runnable {
42     private static final String TAG = "NoListenerVerifier";
43 
44     private String mTag1;
45     private String mTag2;
46     private String mTag3;
47     private int mIcon1;
48     private int mIcon2;
49     private int mIcon3;
50     private int mId1;
51     private int mId2;
52     private int mId3;
53     private long mWhen1;
54     private long mWhen2;
55     private long mWhen3;
56     private int mFlag1;
57     private int mFlag2;
58     private int mFlag3;
59 
60     @Override
getTitleResource()61     int getTitleResource() {
62         return R.string.nls_test;
63     }
64 
65     @Override
getInstructionsResource()66     int getInstructionsResource() {
67         return R.string.nls_info;
68     }
69 
70     // Test Setup
71 
72     @Override
createTestItems()73     protected List<InteractiveTestCase> createTestItems() {
74         List<InteractiveTestCase> tests = new ArrayList<>(9);
75         tests.add(new IsEnabledTest());
76         tests.add(new ServiceStartedTest());
77         tests.add(new NotificationRecievedTest());
78         tests.add(new DataIntactTest());
79         tests.add(new DismissOneTest());
80         tests.add(new DismissAllTest());
81         tests.add(new IsDisabledTest());
82         tests.add(new ServiceStoppedTest());
83         tests.add(new NotificationNotReceivedTest());
84         return tests;
85     }
86 
87     @SuppressLint("NewApi")
sendNotifications()88     private void sendNotifications() {
89         mTag1 = UUID.randomUUID().toString();
90         mTag2 = UUID.randomUUID().toString();
91         mTag3 = UUID.randomUUID().toString();
92 
93         mNm.cancelAll();
94 
95         mWhen1 = System.currentTimeMillis() + 1;
96         mWhen2 = System.currentTimeMillis() + 2;
97         mWhen3 = System.currentTimeMillis() + 3;
98 
99         mIcon1 = R.drawable.ic_stat_alice;
100         mIcon2 = R.drawable.ic_stat_bob;
101         mIcon3 = R.drawable.ic_stat_charlie;
102 
103         mId1 = NOTIFICATION_ID + 1;
104         mId2 = NOTIFICATION_ID + 2;
105         mId3 = NOTIFICATION_ID + 3;
106 
107         mPackageString = "com.android.cts.verifier";
108 
109         Notification n1 = new Notification.Builder(mContext)
110                 .setContentTitle("ClearTest 1")
111                 .setContentText(mTag1.toString())
112                 .setPriority(Notification.PRIORITY_LOW)
113                 .setSmallIcon(mIcon1)
114                 .setWhen(mWhen1)
115                 .setDeleteIntent(makeIntent(1, mTag1))
116                 .setOnlyAlertOnce(true)
117                 .build();
118         mNm.notify(mTag1, mId1, n1);
119         mFlag1 = Notification.FLAG_ONLY_ALERT_ONCE;
120 
121         Notification n2 = new Notification.Builder(mContext)
122                 .setContentTitle("ClearTest 2")
123                 .setContentText(mTag2.toString())
124                 .setPriority(Notification.PRIORITY_HIGH)
125                 .setSmallIcon(mIcon2)
126                 .setWhen(mWhen2)
127                 .setDeleteIntent(makeIntent(2, mTag2))
128                 .setAutoCancel(true)
129                 .build();
130         mNm.notify(mTag2, mId2, n2);
131         mFlag2 = Notification.FLAG_AUTO_CANCEL;
132 
133         Notification n3 = new Notification.Builder(mContext)
134                 .setContentTitle("ClearTest 3")
135                 .setContentText(mTag3.toString())
136                 .setPriority(Notification.PRIORITY_LOW)
137                 .setSmallIcon(mIcon3)
138                 .setWhen(mWhen3)
139                 .setDeleteIntent(makeIntent(3, mTag3))
140                 .setAutoCancel(true)
141                 .setOnlyAlertOnce(true)
142                 .build();
143         mNm.notify(mTag3, mId3, n3);
144         mFlag3 = Notification.FLAG_ONLY_ALERT_ONCE | Notification.FLAG_AUTO_CANCEL;
145     }
146 
147     // Tests
148 
149     private class NotificationRecievedTest extends InteractiveTestCase {
150         @Override
inflate(ViewGroup parent)151         View inflate(ViewGroup parent) {
152             return createAutoItem(parent, R.string.nls_note_received);
153 
154         }
155 
156         @Override
setUp()157         void setUp() {
158             sendNotifications();
159             status = READY;
160             // wait for notifications to move through the system
161             delay();
162         }
163 
164         @Override
test()165         void test() {
166             MockListener.probeListenerPosted(mContext,
167                     new MockListener.StringListResultCatcher() {
168                         @Override
169                         public void accept(List<String> result) {
170                             if (result != null && result.size() > 0 && result.contains(mTag1)) {
171                                 status = PASS;
172                             } else {
173                                 logFail();
174                                 status = FAIL;
175                             }
176                             next();
177                         }
178                     });
179             delay();  // in case the catcher never returns
180         }
181     }
182 
183     private class DataIntactTest extends InteractiveTestCase {
184         @Override
inflate(ViewGroup parent)185         View inflate(ViewGroup parent) {
186             return createAutoItem(parent, R.string.nls_payload_intact);
187         }
188 
189         @Override
test()190         void test() {
191             MockListener.probeListenerPayloads(mContext,
192                     new MockListener.StringListResultCatcher() {
193                         @Override
194                         public void accept(List<String> result) {
195                             Set<String> found = new HashSet<String>();
196                             if (result == null || result.size() == 0) {
197                                 status = FAIL;
198                                 return;
199                             }
200                             boolean pass = true;
201                             for (String payloadData : result) {
202                                 try {
203                                     JSONObject payload = new JSONObject(payloadData);
204                                     pass &= checkEquals(mPackageString,
205                                             payload.getString(JSON_PACKAGE),
206                                             "data integrity test: notification package (%s, %s)");
207                                     String tag = payload.getString(JSON_TAG);
208                                     if (mTag1.equals(tag)) {
209                                         found.add(mTag1);
210                                         pass &= checkEquals(mIcon1, payload.getInt(JSON_ICON),
211                                                 "data integrity test: notification icon (%d, %d)");
212                                         pass &= checkFlagSet(mFlag1, payload.getInt(JSON_FLAGS),
213                                                 "data integrity test: notification flags (%d, %d)");
214                                         pass &= checkEquals(mId1, payload.getInt(JSON_ID),
215                                                 "data integrity test: notification ID (%d, %d)");
216                                         pass &= checkEquals(mWhen1, payload.getLong(JSON_WHEN),
217                                                 "data integrity test: notification when (%d, %d)");
218                                     } else if (mTag2.equals(tag)) {
219                                         found.add(mTag2);
220                                         pass &= checkEquals(mIcon2, payload.getInt(JSON_ICON),
221                                                 "data integrity test: notification icon (%d, %d)");
222                                         pass &= checkFlagSet(mFlag2, payload.getInt(JSON_FLAGS),
223                                                 "data integrity test: notification flags (%d, %d)");
224                                         pass &= checkEquals(mId2, payload.getInt(JSON_ID),
225                                                 "data integrity test: notification ID (%d, %d)");
226                                         pass &= checkEquals(mWhen2, payload.getLong(JSON_WHEN),
227                                                 "data integrity test: notification when (%d, %d)");
228                                     } else if (mTag3.equals(tag)) {
229                                         found.add(mTag3);
230                                         pass &= checkEquals(mIcon3, payload.getInt(JSON_ICON),
231                                                 "data integrity test: notification icon (%d, %d)");
232                                         pass &= checkFlagSet(mFlag3, payload.getInt(JSON_FLAGS),
233                                                 "data integrity test: notification flags (%d, %d)");
234                                         pass &= checkEquals(mId3, payload.getInt(JSON_ID),
235                                                 "data integrity test: notification ID (%d, %d)");
236                                         pass &= checkEquals(mWhen3, payload.getLong(JSON_WHEN),
237                                                 "data integrity test: notification when (%d, %d)");
238                                     } else {
239                                         pass = false;
240                                         logFail("unexpected notification tag: " + tag);
241                                     }
242                                 } catch (JSONException e) {
243                                     pass = false;
244                                     Log.e(TAG, "failed to unpack data from mocklistener", e);
245                                 }
246                             }
247 
248                             pass &= found.size() == 3;
249                             status = pass ? PASS : FAIL;
250                             next();
251                         }
252                     });
253             delay();  // in case the catcher never returns
254         }
255 
256         @Override
tearDown()257         void tearDown() {
258             mNm.cancelAll();
259             MockListener.resetListenerData(mContext);
260             delay();
261         }
262     }
263 
264     private class DismissOneTest extends InteractiveTestCase {
265         @Override
inflate(ViewGroup parent)266         View inflate(ViewGroup parent) {
267             return createAutoItem(parent, R.string.nls_clear_one);
268         }
269 
270         @Override
setUp()271         void setUp() {
272             sendNotifications();
273             status = READY;
274             delay();
275         }
276 
277         @Override
test()278         void test() {
279             if (status == READY) {
280                 MockListener.clearOne(mContext, mTag1, mId1);
281                 status = RETEST;
282             } else {
283                 MockListener.probeListenerRemoved(mContext,
284                         new MockListener.StringListResultCatcher() {
285                             @Override
286                             public void accept(List<String> result) {
287                                 if (result != null && result.size() != 0
288                                         && result.contains(mTag1)
289                                         && !result.contains(mTag2)
290                                         && !result.contains(mTag3)) {
291                                     status = PASS;
292                                 } else {
293                                     logFail();
294                                     status = FAIL;
295                                 }
296                                 next();
297                             }
298                         });
299             }
300             delay();
301         }
302 
303         @Override
tearDown()304         void tearDown() {
305             mNm.cancelAll();
306             MockListener.resetListenerData(mContext);
307             delay();
308         }
309     }
310 
311     private class DismissAllTest extends InteractiveTestCase {
312         @Override
inflate(ViewGroup parent)313         View inflate(ViewGroup parent) {
314             return createAutoItem(parent, R.string.nls_clear_all);
315         }
316 
317         @Override
setUp()318         void setUp() {
319             sendNotifications();
320             status = READY;
321             delay();
322         }
323 
324         @Override
test()325         void test() {
326             if (status == READY) {
327                 MockListener.clearAll(mContext);
328                 status = RETEST;
329             } else {
330                 MockListener.probeListenerRemoved(mContext,
331                         new MockListener.StringListResultCatcher() {
332                             @Override
333                             public void accept(List<String> result) {
334                                 if (result != null && result.size() != 0
335                                         && result.contains(mTag1)
336                                         && result.contains(mTag2)
337                                         && result.contains(mTag3)) {
338                                     status = PASS;
339                                 } else {
340                                     logFail();
341                                     status = FAIL;
342                                 }
343                                 next();
344                             }
345                         });
346             }
347             delay();  // in case the catcher never returns
348         }
349 
350         @Override
tearDown()351         void tearDown() {
352             mNm.cancelAll();
353             MockListener.resetListenerData(mContext);
354             delay();
355         }
356     }
357 
358     private class IsDisabledTest extends InteractiveTestCase {
359         @Override
inflate(ViewGroup parent)360         View inflate(ViewGroup parent) {
361             return createNlsSettingsItem(parent, R.string.nls_disable_service);
362         }
363 
364         @Override
autoStart()365         boolean autoStart() {
366             return true;
367         }
368 
369         @Override
test()370         void test() {
371             String listeners = Secure.getString(getContentResolver(),
372                     ENABLED_NOTIFICATION_LISTENERS);
373             if (listeners == null || !listeners.contains(LISTENER_PATH)) {
374                 status = PASS;
375             } else {
376                 status = WAIT_FOR_USER;
377             }
378             next();
379         }
380 
381         @Override
tearDown()382         void tearDown() {
383             MockListener.resetListenerData(mContext);
384             delay();
385         }
386     }
387 
388     private class ServiceStoppedTest extends InteractiveTestCase {
389         @Override
inflate(ViewGroup parent)390         View inflate(ViewGroup parent) {
391             return createAutoItem(parent, R.string.nls_service_stopped);
392         }
393 
394         @Override
test()395         void test() {
396             MockListener.probeListenerStatus(mContext,
397                     new MockListener.StatusCatcher() {
398                         @Override
399                         public void accept(int result) {
400                             if (result == Activity.RESULT_OK) {
401                                 logFail();
402                                 status = FAIL;
403                             } else {
404                                 status = PASS;
405                             }
406                             next();
407                         }
408                     });
409             delay();  // in case the catcher never returns
410         }
411 
412         @Override
tearDown()413         void tearDown() {
414             // wait for intent to move through the system
415             delay();
416         }
417     }
418 
419     private class NotificationNotReceivedTest extends InteractiveTestCase {
420         @Override
inflate(ViewGroup parent)421         View inflate(ViewGroup parent) {
422             return createAutoItem(parent, R.string.nls_note_missed);
423 
424         }
425 
426         @Override
setUp()427         void setUp() {
428             sendNotifications();
429             status = READY;
430             delay();
431         }
432 
433         @Override
test()434         void test() {
435             MockListener.probeListenerPosted(mContext,
436                     new MockListener.StringListResultCatcher() {
437                         @Override
438                         public void accept(List<String> result) {
439                             if (result == null || result.size() == 0) {
440                                 status = PASS;
441                             } else {
442                                 logFail();
443                                 status = FAIL;
444                             }
445                             next();
446                         }
447                     });
448             delay();  // in case the catcher never returns
449         }
450 
451         @Override
tearDown()452         void tearDown() {
453             mNm.cancelAll();
454             MockListener.resetListenerData(mContext);
455             delay();
456         }
457     }
458 }
459