• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 static android.app.Notification.VISIBILITY_PRIVATE;
20 import static android.app.Notification.VISIBILITY_PUBLIC;
21 import static android.app.Notification.VISIBILITY_SECRET;
22 import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
23 import static android.app.NotificationManager.VISIBILITY_NO_OVERRIDE;
24 import static android.provider.Settings.EXTRA_APP_PACKAGE;
25 import static android.provider.Settings.EXTRA_CHANNEL_ID;
26 
27 import android.annotation.SuppressLint;
28 import android.app.KeyguardManager;
29 import android.app.Notification;
30 import android.app.NotificationChannel;
31 import android.content.Intent;
32 import android.os.Bundle;
33 import android.provider.Settings;
34 import android.util.Log;
35 import android.view.View;
36 import android.view.ViewGroup;
37 
38 import androidx.annotation.StringRes;
39 
40 import com.android.cts.verifier.R;
41 import com.android.cts.verifier.features.FeatureUtil;
42 
43 import java.util.ArrayList;
44 import java.util.List;
45 import java.util.UUID;
46 
47 /**
48  * A verifier test which validates the lockscreen behaviors of notifications under various settings
49  */
50 public class NotificationPrivacyVerifierActivity extends InteractiveVerifierActivity
51         implements Runnable {
52     static final String TAG = "NotifPrivacyVerifier";
53     private static final String NOTIFICATION_CHANNEL_ID = TAG;
54 
55     @Override
onCreate(Bundle savedState)56     protected void onCreate(Bundle savedState) {
57         super.onCreate(savedState);
58     }
59 
60     @Override
getTitleResource()61     protected int getTitleResource() {
62         return R.string.notif_privacy_test;
63     }
64 
65     @Override
getInstructionsResource()66     protected int getInstructionsResource() {
67         return R.string.notif_privacy_info;
68     }
69 
getChannelVisibility()70     private int getChannelVisibility() {
71         NotificationChannel channel = mNm.getNotificationChannel(NOTIFICATION_CHANNEL_ID);
72         int visibility = channel.getLockscreenVisibility();
73         if (visibility == VISIBILITY_NO_OVERRIDE) {
74             visibility = getGlobalVisibility();
75         }
76         if (visibility != VISIBILITY_SECRET
77                 && visibility != VISIBILITY_PRIVATE
78                 && visibility != VISIBILITY_PUBLIC) {
79             throw new RuntimeException("Unexpected visibility: " + visibility);
80         }
81         return visibility;
82     }
83 
getGlobalVisibility()84     private int getGlobalVisibility() {
85         if (!getLockscreenNotificationsEnabled()) {
86             return VISIBILITY_SECRET;
87         } else if (!getLockscreenAllowPrivateNotifications()) {
88             return VISIBILITY_PRIVATE;
89         }
90         return VISIBILITY_PUBLIC;
91     }
92 
getLockscreenNotificationsEnabled()93     private boolean getLockscreenNotificationsEnabled() {
94         return Settings.Secure.getInt(mContext.getContentResolver(),
95                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0) != 0;
96     }
97 
getLockscreenAllowPrivateNotifications()98     private boolean getLockscreenAllowPrivateNotifications() {
99         return Settings.Secure.getInt(mContext.getContentResolver(),
100                 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0) != 0;
101     }
102 
103 
104     // Test Setup
105 
106     @Override
createTestItems()107     protected List<InteractiveTestCase> createTestItems() {
108         List<InteractiveTestCase> tests = new ArrayList<>();
109 
110         // FIRST: enable lock screen
111         tests.add(new SetScreenLockEnabledStep());
112 
113         // for watches, no notifications should appear on the secure lock screen
114         if (!FeatureUtil.isWatch(this)) {
115             // THEN: set redaction settings
116             tests.add(new SetGlobalVisibilityPublicStep());
117             tests.add(new SetChannelLockscreenVisibilityPrivateStep());
118             // NOW TESTING: redacted by channel
119             tests.add(new NotificationWhenLockedShowsRedactedTest());
120             // TODO: Test: notification CAN be dismissed on lockscreen
121             tests.add(new NotificationWhenOccludedShowsRedactedTest());
122 
123             tests.add(new SetChannelLockscreenVisibilityPublicStep());
124             // NOW TESTING: not redacted at all
125             tests.add(new SecureActionOnLockScreenTest());
126             tests.add(new NotificationWhenLockedShowsPrivateTest());
127             // TODO: Test: notification can NOT be dismissed on lockscreen
128             tests.add(new NotificationWhenOccludedShowsPrivateTest());
129 
130             tests.add(new SetGlobalVisibilityPrivateStep());
131             // NOW TESTING: redacted globally
132             tests.add(new NotificationWhenLockedShowsRedactedTest());
133             // TODO: Test: notification CAN be dismissed on lockscreen
134             tests.add(new NotificationWhenOccludedShowsRedactedTest());
135 
136             tests.add(new SetGlobalVisibilitySecretStep());
137         }
138 
139         // NOW TESTING: notifications do not appear
140         tests.add(new NotificationWhenLockedIsHiddenTest());
141         if (!FeatureUtil.isWatch(this)) {
142             tests.add(new NotificationWhenOccludedIsHiddenTest());
143         }
144 
145         // FINALLY: restore device state
146         tests.add(new SetScreenLockDisabledStep());
147         return tests;
148     }
149 
createChannels()150     private void createChannels() {
151         NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
152                 NOTIFICATION_CHANNEL_ID, IMPORTANCE_DEFAULT);
153         mNm.createNotificationChannel(channel);
154     }
155 
deleteChannels()156     private void deleteChannels() {
157         mNm.deleteNotificationChannel(NOTIFICATION_CHANNEL_ID);
158     }
159 
160     @SuppressLint("NewApi")
sendNotification()161     private void sendNotification() {
162         String tag = UUID.randomUUID().toString();
163         long when = System.currentTimeMillis();
164         Log.d(TAG, "Sending: tag=" + tag + " when=" + when);
165 
166         mPackageString = "com.android.cts.verifier";
167 
168         Notification publicVersion = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
169                 .setContentTitle(getString(R.string.np_public_version_text))
170                 .setSmallIcon(R.drawable.ic_stat_alice)
171                 .setWhen(when)
172                 .build();
173         Notification privateVersion = new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID)
174                 .setContentTitle(getString(R.string.np_private_version_text))
175                 .setSmallIcon(R.drawable.ic_stat_alice)
176                 .setWhen(when)
177                 .setPublicVersion(publicVersion)
178                 .build();
179         mNm.notify(tag, NOTIFICATION_ID, privateVersion);
180     }
181 
182     /**
183      * Asks the user to set the lockscreen visibility of the channel to the given value
184      */
185     private abstract class SetChannelLockscreenVisibilityBaseStep extends InteractiveTestCase {
186         @StringRes
187         private final int mInstructionRes;
188         private final int mExpectVisibility;
189         private View mView;
190 
SetChannelLockscreenVisibilityBaseStep(@tringRes int instructionRes, int expectVisibility)191         SetChannelLockscreenVisibilityBaseStep(@StringRes int instructionRes,
192                 int expectVisibility) {
193             mInstructionRes = instructionRes;
194             mExpectVisibility = expectVisibility;
195         }
196 
197         @Override
inflate(ViewGroup parent)198         protected View inflate(ViewGroup parent) {
199             mView = createUserItem(parent, R.string.np_start_channel_settings, mInstructionRes);
200             setButtonsEnabled(mView, false);
201             return mView;
202         }
203 
204         @Override
setUp()205         protected void setUp() {
206             createChannels();
207             status = READY;
208             setButtonsEnabled(mView, true);
209             next();
210         }
211 
212         @Override
autoStart()213         boolean autoStart() {
214             return true;
215         }
216 
217         @Override
test()218         protected void test() {
219             if (getChannelVisibility() == mExpectVisibility) {
220                 status = PASS;
221             } else {
222                 // user hasn't jumped to settings yet
223                 status = WAIT_FOR_USER;
224             }
225 
226             next();
227         }
228 
tearDown()229         protected void tearDown() {
230             deleteChannels();
231             next();
232         }
233 
234         @Override
getIntent()235         protected Intent getIntent() {
236             return new Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
237                     .putExtra(EXTRA_APP_PACKAGE, mContext.getPackageName())
238                     .putExtra(EXTRA_CHANNEL_ID, NOTIFICATION_CHANNEL_ID);
239         }
240     }
241 
242     private class SetChannelLockscreenVisibilityPrivateStep extends
243             SetChannelLockscreenVisibilityBaseStep {
SetChannelLockscreenVisibilityPrivateStep()244         SetChannelLockscreenVisibilityPrivateStep() {
245             super(R.string.nls_visibility, VISIBILITY_PRIVATE);
246         }
247     }
248 
249     private class SetChannelLockscreenVisibilityPublicStep extends
250             SetChannelLockscreenVisibilityBaseStep {
SetChannelLockscreenVisibilityPublicStep()251         SetChannelLockscreenVisibilityPublicStep() {
252             super(R.string.nls_restore_visibility, VISIBILITY_PUBLIC);
253         }
254     }
255 
256     private abstract class SetScreenLockBaseStep extends InteractiveTestCase {
257         @StringRes
258         private final int mInstructionRes;
259         private final boolean mExpectSecure;
260         private View mView;
261 
SetScreenLockBaseStep(int instructionRes, boolean expectSecure)262         private SetScreenLockBaseStep(int instructionRes, boolean expectSecure) {
263             mInstructionRes = instructionRes;
264             mExpectSecure = expectSecure;
265         }
266 
267         @Override
inflate(ViewGroup parent)268         protected View inflate(ViewGroup parent) {
269             mView = createUserItem(parent, R.string.np_start_security_settings, mInstructionRes);
270             setButtonsEnabled(mView, false);
271             return mView;
272         }
273 
274         @Override
setUp()275         protected void setUp() {
276             status = READY;
277             setButtonsEnabled(mView, true);
278             next();
279         }
280 
281         @Override
autoStart()282         boolean autoStart() {
283             return true;
284         }
285 
286         @Override
test()287         protected void test() {
288             KeyguardManager km = getSystemService(KeyguardManager.class);
289             if (km.isDeviceSecure() == mExpectSecure) {
290                 status = PASS;
291             } else {
292                 status = WAIT_FOR_USER;
293             }
294 
295             next();
296         }
297 
298         @Override
getIntent()299         protected Intent getIntent() {
300             return new Intent(Settings.ACTION_SECURITY_SETTINGS)
301                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
302         }
303     }
304 
305     private class SetScreenLockEnabledStep extends SetScreenLockBaseStep {
SetScreenLockEnabledStep()306         private SetScreenLockEnabledStep() {
307             super(R.string.add_screen_lock, true /* secure */);
308         }
309     }
310 
311     private class SetScreenLockDisabledStep extends SetScreenLockBaseStep {
SetScreenLockDisabledStep()312         private SetScreenLockDisabledStep() {
313             super(R.string.remove_screen_lock, false /* secure */);
314         }
315     }
316 
317     private abstract class SetGlobalVisibilityBaseStep extends InteractiveTestCase {
318         @StringRes
319         private final int mInstructionRes;
320         private final int mExpectVisibility;
321         private View mView;
322 
SetGlobalVisibilityBaseStep(int instructionRes, int expectVisibility)323         private SetGlobalVisibilityBaseStep(int instructionRes, int expectVisibility) {
324             mInstructionRes = instructionRes;
325             mExpectVisibility = expectVisibility;
326         }
327 
328         @Override
inflate(ViewGroup parent)329         protected View inflate(ViewGroup parent) {
330             mView = createUserItem(parent, R.string.np_start_notif_settings, mInstructionRes);
331             setButtonsEnabled(mView, false);
332             return mView;
333         }
334 
335         @Override
setUp()336         protected void setUp() {
337             status = READY;
338             setButtonsEnabled(mView, true);
339             next();
340         }
341 
342         @Override
autoStart()343         boolean autoStart() {
344             return true;
345         }
346 
347         @Override
test()348         protected void test() {
349             KeyguardManager km = getSystemService(KeyguardManager.class);
350             if (!km.isDeviceSecure()) {
351                 // if lockscreen itself not set, this setting won't be available.
352                 status = FAIL;
353             } else if (getGlobalVisibility() == mExpectVisibility) {
354                 status = PASS;
355             } else {
356                 status = WAIT_FOR_USER;
357             }
358 
359             next();
360         }
361 
362         @Override
getIntent()363         protected Intent getIntent() {
364             return new Intent(Settings.ACTION_NOTIFICATION_SETTINGS)
365                     .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
366         }
367     }
368 
369     private class SetGlobalVisibilityPublicStep extends SetGlobalVisibilityBaseStep {
SetGlobalVisibilityPublicStep()370         private SetGlobalVisibilityPublicStep() {
371             super(R.string.set_global_visibility_public, VISIBILITY_PUBLIC);
372         }
373     }
374 
375     private class SetGlobalVisibilityPrivateStep extends SetGlobalVisibilityBaseStep {
SetGlobalVisibilityPrivateStep()376         private SetGlobalVisibilityPrivateStep() {
377             super(R.string.set_global_visibility_private, VISIBILITY_PRIVATE);
378         }
379     }
380 
381     private class SetGlobalVisibilitySecretStep extends SetGlobalVisibilityBaseStep {
SetGlobalVisibilitySecretStep()382         private SetGlobalVisibilitySecretStep() {
383             super(R.string.set_global_visibility_secret, VISIBILITY_SECRET);
384         }
385     }
386 
387     private class SecureActionOnLockScreenTest extends InteractiveTestCase {
388         private View mView;
389 
390         @Override
setUp()391         protected void setUp() {
392             createChannels();
393             ActionTriggeredReceiver.sendNotification(mContext, true);
394             setButtonsEnabled(mView, true);
395             status = READY;
396             next();
397         }
398 
399         @Override
tearDown()400         protected void tearDown() {
401             mNm.cancelAll();
402             deleteChannels();
403             delay();
404         }
405 
406         @Override
inflate(ViewGroup parent)407         protected View inflate(ViewGroup parent) {
408             mView = createPassFailItem(parent, R.string.secure_action_lockscreen);
409             setButtonsEnabled(mView, false);
410             return mView;
411         }
412 
413         @Override
autoStart()414         boolean autoStart() {
415             return true;
416         }
417 
418         @Override
test()419         protected void test() {
420             status = WAIT_FOR_USER;
421             next();
422         }
423     }
424 
425 
426     private abstract class NotificationPrivacyBaseTest extends InteractiveTestCase {
427         private View mView;
428         @StringRes
429         private final int mInstructionRes;
430 
NotificationPrivacyBaseTest(@tringRes int instructionRes)431         NotificationPrivacyBaseTest(@StringRes int instructionRes) {
432             mInstructionRes = instructionRes;
433         }
434 
435         @Override
setUp()436         protected void setUp() {
437             createChannels();
438             sendNotification();
439             setButtonsEnabled(mView, true);
440             status = READY;
441             next();
442         }
443 
444         @Override
tearDown()445         protected void tearDown() {
446             mNm.cancelAll();
447             deleteChannels();
448             delay();
449         }
450 
451         @Override
inflate(ViewGroup parent)452         protected View inflate(ViewGroup parent) {
453             mView = createPassFailItem(parent, mInstructionRes);
454             setButtonsEnabled(mView, false);
455             return mView;
456         }
457 
458         @Override
autoStart()459         boolean autoStart() {
460             return true;
461         }
462 
463         @Override
test()464         protected void test() {
465             status = WAIT_FOR_USER;
466             next();
467         }
468     }
469 
470     private class NotificationWhenLockedShowsRedactedTest extends NotificationPrivacyBaseTest {
NotificationWhenLockedShowsRedactedTest()471         NotificationWhenLockedShowsRedactedTest() {
472             super(R.string.np_when_locked_see_redacted);
473         }
474     }
475 
476     private class NotificationWhenLockedShowsPrivateTest extends NotificationPrivacyBaseTest {
NotificationWhenLockedShowsPrivateTest()477         NotificationWhenLockedShowsPrivateTest() {
478             super(R.string.np_when_locked_see_private);
479         }
480     }
481 
482     private class NotificationWhenLockedIsHiddenTest extends NotificationPrivacyBaseTest {
NotificationWhenLockedIsHiddenTest()483         NotificationWhenLockedIsHiddenTest() {
484             super(R.string.np_when_locked_hidden);
485         }
486     }
487 
488     private abstract class NotificationWhenOccludedBaseTest extends InteractiveTestCase {
489         private View mView;
490         @StringRes
491         private final int mInstructionRes;
492 
NotificationWhenOccludedBaseTest(@tringRes int instructionRes)493         NotificationWhenOccludedBaseTest(@StringRes int instructionRes) {
494             mInstructionRes = instructionRes;
495         }
496 
497         @Override
setUp()498         protected void setUp() {
499             createChannels();
500             sendNotification();
501             setButtonsEnabled(mView, true);
502             status = READY;
503             next();
504         }
505 
506         @Override
tearDown()507         protected void tearDown() {
508             mNm.cancelAll();
509             deleteChannels();
510             delay();
511         }
512 
513         @Override
inflate(ViewGroup parent)514         protected View inflate(ViewGroup parent) {
515             mView = createUserAndPassFailItem(
516                     parent, R.string.np_start_occluding, R.string.np_occluding_instructions);
517             setButtonsEnabled(mView, false);
518             return mView;
519         }
520 
521         @Override
autoStart()522         boolean autoStart() {
523             return true;
524         }
525 
526         @Override
test()527         protected void test() {
528             status = WAIT_FOR_USER;
529             next();
530         }
531 
532         @Override
getIntent()533         protected Intent getIntent() {
534             return ShowWhenLockedActivity.makeActivityIntent(
535                     getApplicationContext(), getString(mInstructionRes));
536         }
537     }
538 
539     private class NotificationWhenOccludedShowsRedactedTest extends
540             NotificationWhenOccludedBaseTest {
NotificationWhenOccludedShowsRedactedTest()541         NotificationWhenOccludedShowsRedactedTest() {
542             super(R.string.np_occluding_see_redacted);
543         }
544     }
545 
546     private class NotificationWhenOccludedShowsPrivateTest extends
547             NotificationWhenOccludedBaseTest {
NotificationWhenOccludedShowsPrivateTest()548         NotificationWhenOccludedShowsPrivateTest() {
549             super(R.string.np_occluding_see_private);
550         }
551     }
552 
553     private class NotificationWhenOccludedIsHiddenTest extends NotificationWhenOccludedBaseTest {
NotificationWhenOccludedIsHiddenTest()554         NotificationWhenOccludedIsHiddenTest() {
555             super(R.string.np_occluding_hidden);
556         }
557     }
558 
559 }
560