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.server.cts;
18 
19 import android.app.PolicyProto;
20 import android.service.notification.ConditionProto;
21 import android.service.notification.ManagedServicesProto;
22 import android.service.notification.NotificationRecordProto;
23 import android.service.notification.NotificationServiceDumpProto;
24 import android.service.notification.RankingHelperProto;
25 import android.service.notification.RankingHelperProto.RecordProto;
26 import android.service.notification.ZenMode;
27 import android.service.notification.ZenModeProto;
28 import android.service.notification.ZenRuleProto;
29 
30 import com.android.tradefed.device.ITestDevice;
31 
32 import java.util.List;
33 
34 /**
35  * Test to check that the notification service properly outputs its dump state.
36  *
37  * make -j32 CtsIncidentHostTestCases
38  * cts-tradefed run singleCommand cts-dev -d --module CtsIncidentHostTestCases
39  */
40 public class NotificationIncidentTest extends ProtoDumpTestCase {
41     // Constants from android.app.NotificationManager
42     private static final int IMPORTANCE_UNSPECIFIED = -1000;
43     private static final int IMPORTANCE_NONE = 0;
44     private static final int IMPORTANCE_MAX = 5;
45     private static final int VISIBILITY_NO_OVERRIDE = -1000;
46     // Constants from android.app.Notification
47     private static final int PRIORITY_MIN = -2;
48     private static final int PRIORITY_MAX = 2;
49     private static final int VISIBILITY_SECRET = -1;
50     private static final int VISIBILITY_PUBLIC = 1;
51     // These constants are those in PackageManager.
52     public static final String FEATURE_WATCH = "android.hardware.type.watch";
53 
54     private static final String DEVICE_SIDE_TEST_APK = "CtsNotificationIncidentTestApp.apk";
55     private static final String TEST_APP_TAG = "NotificationIncidentTestActivity";
56     private static final String TEST_APP_LOG = "Notification posted.";
57     private static final String TEST_ACTIVITY =
58             "com.android.server.cts.notifications/.NotificationIncidentTestActivity";
59     private static final int WAIT_MS = 1000;
60     private static final String DEVICE_SIDE_TEST_PKG = "com.android.server.cts.notifications";
61 
62     /**
63      * Tests that at least one notification is posted, and verify its properties are plausible.
64      */
testNotificationRecords()65     public void testNotificationRecords() throws Exception {
66         ITestDevice device = getDevice();
67         try {
68             installPackage(DEVICE_SIDE_TEST_APK, /* grantPermissions= */ true);
69             int retries = 3;
70             do {
71                 device.executeShellCommand("am start -n " + TEST_ACTIVITY);
72             } while (!checkLogcatForText(TEST_APP_TAG, TEST_APP_LOG, WAIT_MS) && retries-- > 0);
73 
74             final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
75                     "dumpsys notification --proto");
76 
77             assertTrue(dump.getRecordsCount() > 0);
78             boolean found = false;
79             for (NotificationRecordProto record : dump.getRecordsList()) {
80                 if (record.getKey().contains("android")) {
81                     found = true;
82                     assertTrue(record.getImportance() > IMPORTANCE_NONE);
83 
84                     // Ensure these fields exist, at least
85                     record.getFlags();
86                     record.getChannelId();
87                     record.getSound();
88                     record.getAudioAttributes();
89                     record.getCanVibrate();
90                     record.getCanShowLight();
91                     record.getGroupKey();
92                 }
93                 assertTrue(
94                         NotificationRecordProto.State.getDescriptor()
95                                 .getValues()
96                                 .contains(record.getState().getValueDescriptor()));
97             }
98 
99             assertTrue(found);
100         } finally {
101             if (device.getInstalledPackageNames().contains(DEVICE_SIDE_TEST_PKG)) {
102                 device.uninstallPackage(DEVICE_SIDE_TEST_PKG);
103             }
104         }
105     }
106 
107     /** Test valid values from the RankingHelper. */
testRankingConfig()108     public void testRankingConfig() throws Exception {
109         final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
110                 "dumpsys notification --proto");
111 
112         verifyRankingHelperProto(dump.getRankingConfig(), PRIVACY_NONE);
113     }
114 
verifyRankingHelperProto(RankingHelperProto rhProto, final int filterLevel)115     private static void verifyRankingHelperProto(RankingHelperProto rhProto, final int filterLevel) throws Exception {
116         for (RecordProto rp : rhProto.getRecordsList()) {
117             verifyRecordProto(rp);
118         }
119         for (RecordProto rp : rhProto.getRecordsRestoredWithoutUidList()) {
120             verifyRecordProto(rp);
121         }
122     }
123 
verifyRecordProto(RecordProto rp)124     private static void verifyRecordProto(RecordProto rp) throws Exception {
125         assertTrue(!rp.getPackage().isEmpty());
126         assertTrue(rp.getUid() == -10000 || rp.getUid() >= 0 || rp.getUid() == -1);
127         assertTrue("Record importance is an invalid value: " + rp.getImportance(),
128                 rp.getImportance() == IMPORTANCE_UNSPECIFIED ||
129                 (rp.getImportance() >= IMPORTANCE_NONE && rp.getImportance() <= IMPORTANCE_MAX));
130         assertTrue(rp.getPriority() >= PRIORITY_MIN && rp.getPriority() <= PRIORITY_MAX);
131         assertTrue("Record visibility is an invalid value: " + rp.getVisibility(),
132                 rp.getVisibility() == VISIBILITY_NO_OVERRIDE ||
133                 (rp.getVisibility() >= VISIBILITY_SECRET &&
134                  rp.getVisibility() <= VISIBILITY_PUBLIC));
135     }
136 
137     // Tests default state: zen mode is a valid/expected value
testZenMode()138     public void testZenMode() throws Exception {
139         final NotificationServiceDumpProto dump = getDump(NotificationServiceDumpProto.parser(),
140                 "dumpsys notification --proto");
141 
142         verifyZenModeProto(dump.getZen(), PRIVACY_NONE);
143     }
144 
verifyZenModeProto(ZenModeProto zenProto, final int filterLevel)145     private static void verifyZenModeProto(ZenModeProto zenProto, final int filterLevel) throws Exception {
146         assertTrue("Unexpected ZenMode value",
147                 ZenMode.getDescriptor().getValues().contains(zenProto.getZenMode().getValueDescriptor()));
148 
149         List<ZenRuleProto> zenRules = zenProto.getEnabledActiveConditionsList();
150         for (int i = 0; i < zenRules.size(); ++i) {
151             ZenRuleProto zr = zenRules.get(i);
152             ConditionProto cp = zr.getCondition();
153             if (filterLevel == PRIVACY_AUTO) {
154                 assertTrue(zr.getId().isEmpty());
155                 assertTrue(zr.getName().isEmpty());
156                 assertTrue(zr.getConditionId().isEmpty());
157 
158                 assertTrue(cp.getId().isEmpty());
159                 assertTrue(cp.getSummary().isEmpty());
160                 assertTrue(cp.getLine1().isEmpty());
161                 assertTrue(cp.getLine2().isEmpty());
162             } else if (i > 0) {
163                 // There will be at most one manual rule, the rest will be automatic. The fields
164                 // tested here are required for automatic rules.
165                 assertFalse(zr.getId().isEmpty());
166                 assertFalse(zr.getName().isEmpty());
167                 assertTrue(zr.getCreationTimeMs() > 0);
168                 assertFalse(zr.getConditionId().isEmpty());
169             }
170 
171             assertTrue(ConditionProto.State.getDescriptor().getValues()
172                     .contains(cp.getState().getValueDescriptor()));
173         }
174 
175         PolicyProto policy = zenProto.getPolicy();
176         for (PolicyProto.Category c : policy.getPriorityCategoriesList()) {
177             assertTrue(PolicyProto.Category.getDescriptor().getValues()
178                     .contains(c.getValueDescriptor()));
179         }
180         assertTrue(PolicyProto.Sender.getDescriptor().getValues()
181                 .contains(policy.getPriorityCallSender().getValueDescriptor()));
182         assertTrue(PolicyProto.Sender.getDescriptor().getValues()
183                 .contains(policy.getPriorityMessageSender().getValueDescriptor()));
184         for (PolicyProto.SuppressedVisualEffect sve : policy.getSuppressedVisualEffectsList()) {
185             assertTrue(PolicyProto.SuppressedVisualEffect.getDescriptor().getValues()
186                     .contains(sve.getValueDescriptor()));
187         }
188     }
189 
verifyNotificationServiceDumpProto(NotificationServiceDumpProto dump, final int filterLevel)190     static void verifyNotificationServiceDumpProto(NotificationServiceDumpProto dump, final int filterLevel) throws Exception {
191         for (NotificationRecordProto nr : dump.getRecordsList()) {
192             verifyNotificationRecordProto(nr, filterLevel);
193         }
194         verifyZenModeProto(dump.getZen(), filterLevel);
195         verifyManagedServicesProto(dump.getNotificationListeners(), filterLevel);
196         verifyManagedServicesProto(dump.getNotificationAssistants(), filterLevel);
197         verifyManagedServicesProto(dump.getConditionProviders(), filterLevel);
198         verifyRankingHelperProto(dump.getRankingConfig(), filterLevel);
199     }
200 
verifyManagedServicesProto(ManagedServicesProto ms, final int filterLevel)201     private static void verifyManagedServicesProto(ManagedServicesProto ms, final int filterLevel) throws Exception {
202         for (ManagedServicesProto.ServiceProto sp : ms.getApprovedList()) {
203             for (String n : sp.getNameList()) {
204                 assertFalse(n.isEmpty());
205             }
206             assertTrue(sp.getUserId() >= 0);
207         }
208     }
209 
verifyNotificationRecordProto(NotificationRecordProto record, final int filterLevel)210     private static void verifyNotificationRecordProto(NotificationRecordProto record, final int filterLevel) throws Exception {
211         // Ensure these fields exist, at least
212         record.getFlags();
213         record.getChannelId();
214         record.getSound();
215         record.getAudioAttributes();
216         record.getCanVibrate();
217         record.getCanShowLight();
218         record.getGroupKey();
219 
220         if (filterLevel == PRIVACY_AUTO) {
221             assertTrue(record.getChannelId().isEmpty());
222             assertTrue(record.getSound().isEmpty());
223             assertTrue(record.getGroupKey().isEmpty());
224         }
225 
226         assertTrue(NotificationRecordProto.State.getDescriptor().getValues()
227                 .contains(record.getState().getValueDescriptor()));
228     }
229 }
230