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