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