RankingHelperTest.java revision 1864ff5dae054e86f527426ef9887440d3277bcd
1/* 2 * Copyright (C) 2014 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 */ 16package com.android.server.notification; 17 18import org.junit.Before; 19import org.junit.Test; 20import org.junit.runner.RunWith; 21 22import com.android.internal.util.FastXmlSerializer; 23 24import org.mockito.Mock; 25import org.mockito.MockitoAnnotations; 26import org.xmlpull.v1.XmlPullParser; 27import org.xmlpull.v1.XmlSerializer; 28 29import android.app.Notification; 30import android.content.Context; 31import android.app.NotificationChannel; 32import android.app.NotificationManager; 33import android.content.pm.ApplicationInfo; 34import android.content.pm.PackageManager; 35import android.net.Uri; 36import android.os.Build; 37import android.os.UserHandle; 38import android.service.notification.StatusBarNotification; 39import android.support.test.InstrumentationRegistry; 40import android.support.test.runner.AndroidJUnit4; 41import android.test.suitebuilder.annotation.SmallTest; 42import android.util.Xml; 43 44import java.io.BufferedInputStream; 45import java.io.BufferedOutputStream; 46import java.io.ByteArrayInputStream; 47import java.io.ByteArrayOutputStream; 48import java.util.ArrayList; 49 50import static org.junit.Assert.assertEquals; 51import static org.junit.Assert.assertFalse; 52import static org.junit.Assert.assertNotNull; 53import static org.junit.Assert.assertTrue; 54import static org.mockito.Matchers.anyInt; 55import static org.mockito.Matchers.anyString; 56import static org.mockito.Matchers.eq; 57import static org.mockito.Mockito.when; 58 59@SmallTest 60@RunWith(AndroidJUnit4.class) 61public class RankingHelperTest { 62 @Mock NotificationUsageStats mUsageStats; 63 @Mock RankingHandler handler; 64 @Mock PackageManager mPm; 65 66 private Notification mNotiGroupGSortA; 67 private Notification mNotiGroupGSortB; 68 private Notification mNotiNoGroup; 69 private Notification mNotiNoGroup2; 70 private Notification mNotiNoGroupSortA; 71 private NotificationRecord mRecordGroupGSortA; 72 private NotificationRecord mRecordGroupGSortB; 73 private NotificationRecord mRecordNoGroup; 74 private NotificationRecord mRecordNoGroup2; 75 private NotificationRecord mRecordNoGroupSortA; 76 private RankingHelper mHelper; 77 private final String pkg = "com.android.server.notification"; 78 private final int uid = 0; 79 private final String pkg2 = "pkg2"; 80 private final int uid2 = 1111111; 81 82 private Context getContext() { 83 return InstrumentationRegistry.getTargetContext(); 84 } 85 86 @Before 87 public void setUp() { 88 MockitoAnnotations.initMocks(this); 89 UserHandle user = UserHandle.ALL; 90 91 mHelper = new RankingHelper(getContext(), mPm, handler, mUsageStats, 92 new String[] {ImportanceExtractor.class.getName()}); 93 94 mNotiGroupGSortA = new Notification.Builder(getContext()) 95 .setContentTitle("A") 96 .setGroup("G") 97 .setSortKey("A") 98 .setWhen(1205) 99 .build(); 100 mRecordGroupGSortA = new NotificationRecord(getContext(), new StatusBarNotification( 101 "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortA, user), 102 getDefaultChannel()); 103 104 mNotiGroupGSortB = new Notification.Builder(getContext()) 105 .setContentTitle("B") 106 .setGroup("G") 107 .setSortKey("B") 108 .setWhen(1200) 109 .build(); 110 mRecordGroupGSortB = new NotificationRecord(getContext(), new StatusBarNotification( 111 "package", "package", 1, null, 0, 0, 0, mNotiGroupGSortB, user), 112 getDefaultChannel()); 113 114 mNotiNoGroup = new Notification.Builder(getContext()) 115 .setContentTitle("C") 116 .setWhen(1201) 117 .build(); 118 mRecordNoGroup = new NotificationRecord(getContext(), new StatusBarNotification( 119 "package", "package", 1, null, 0, 0, 0, mNotiNoGroup, user), 120 getDefaultChannel()); 121 122 mNotiNoGroup2 = new Notification.Builder(getContext()) 123 .setContentTitle("D") 124 .setWhen(1202) 125 .build(); 126 mRecordNoGroup2 = new NotificationRecord(getContext(), new StatusBarNotification( 127 "package", "package", 1, null, 0, 0, 0, mNotiNoGroup2, user), 128 getDefaultChannel()); 129 130 mNotiNoGroupSortA = new Notification.Builder(getContext()) 131 .setContentTitle("E") 132 .setWhen(1201) 133 .setSortKey("A") 134 .build(); 135 mRecordNoGroupSortA = new NotificationRecord(getContext(), new StatusBarNotification( 136 "package", "package", 1, null, 0, 0, 0, mNotiNoGroupSortA, user), 137 getDefaultChannel()); 138 139 final ApplicationInfo legacy = new ApplicationInfo(); 140 legacy.targetSdkVersion = Build.VERSION_CODES.N_MR1; 141 final ApplicationInfo upgrade = new ApplicationInfo(); 142 upgrade.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1; 143 try { 144 when(mPm.getApplicationInfo(eq(pkg), anyInt())).thenReturn(legacy); 145 when(mPm.getApplicationInfo(eq(pkg2), anyInt())).thenReturn(upgrade); 146 } catch (PackageManager.NameNotFoundException e) {} 147 } 148 149 private NotificationChannel getDefaultChannel() { 150 return new NotificationChannel(NotificationChannel.DEFAULT_CHANNEL_ID, "name", 151 NotificationManager.IMPORTANCE_LOW); 152 } 153 154 private ByteArrayOutputStream writeXmlAndPurge(String pkg, int uid, String... channelIds) 155 throws Exception { 156 XmlSerializer serializer = new FastXmlSerializer(); 157 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 158 serializer.setOutput(new BufferedOutputStream(baos), "utf-8"); 159 serializer.startDocument(null, true); 160 serializer.startTag(null, "ranking"); 161 mHelper.writeXml(serializer, false); 162 serializer.endTag(null, "ranking"); 163 serializer.endDocument(); 164 serializer.flush(); 165 166 for (String channelId : channelIds) { 167 mHelper.deleteNotificationChannel(pkg, uid, channelId); 168 } 169 return baos; 170 } 171 172 @Test 173 public void testFindAfterRankingWithASplitGroup() throws Exception { 174 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(3); 175 notificationList.add(mRecordGroupGSortA); 176 notificationList.add(mRecordGroupGSortB); 177 notificationList.add(mRecordNoGroup); 178 notificationList.add(mRecordNoGroupSortA); 179 mHelper.sort(notificationList); 180 assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortA) >= 0); 181 assertTrue(mHelper.indexOf(notificationList, mRecordGroupGSortB) >= 0); 182 assertTrue(mHelper.indexOf(notificationList, mRecordNoGroup) >= 0); 183 assertTrue(mHelper.indexOf(notificationList, mRecordNoGroupSortA) >= 0); 184 } 185 186 @Test 187 public void testSortShouldNotThrowWithPlainNotifications() throws Exception { 188 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2); 189 notificationList.add(mRecordNoGroup); 190 notificationList.add(mRecordNoGroup2); 191 mHelper.sort(notificationList); 192 } 193 194 @Test 195 public void testSortShouldNotThrowOneSorted() throws Exception { 196 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(2); 197 notificationList.add(mRecordNoGroup); 198 notificationList.add(mRecordNoGroupSortA); 199 mHelper.sort(notificationList); 200 } 201 202 @Test 203 public void testSortShouldNotThrowOneNotification() throws Exception { 204 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1); 205 notificationList.add(mRecordNoGroup); 206 mHelper.sort(notificationList); 207 } 208 209 @Test 210 public void testSortShouldNotThrowOneSortKey() throws Exception { 211 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(1); 212 notificationList.add(mRecordGroupGSortB); 213 mHelper.sort(notificationList); 214 } 215 216 @Test 217 public void testSortShouldNotThrowOnEmptyList() throws Exception { 218 ArrayList<NotificationRecord> notificationList = new ArrayList<NotificationRecord>(); 219 mHelper.sort(notificationList); 220 } 221 222 @Test 223 public void testChannelXml() throws Exception { 224 NotificationChannel channel1 = 225 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_HIGH); 226 NotificationChannel channel2 = 227 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW); 228 channel2.setRingtone(new Uri.Builder().scheme("test").build()); 229 channel2.setLights(true); 230 channel2.setBypassDnd(true); 231 channel2.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 232 233 mHelper.createNotificationChannel(pkg, uid, channel1); 234 mHelper.createNotificationChannel(pkg, uid, channel2); 235 236 ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), channel2.getId(), 237 NotificationChannel.DEFAULT_CHANNEL_ID); 238 239 mHelper.deleteNotificationChannel(pkg, uid, channel1.getId()); 240 mHelper.deleteNotificationChannel(pkg, uid, channel2.getId()); 241 mHelper.deleteNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID); 242 243 XmlPullParser parser = Xml.newPullParser(); 244 parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), 245 null); 246 parser.nextTag(); 247 mHelper.readXml(parser, false); 248 249 assertEquals(channel1, mHelper.getNotificationChannel(pkg, uid, channel1.getId())); 250 assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel2.getId())); 251 assertNotNull( 252 mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID)); 253 } 254 255 @Test 256 public void testChannelXml_defaultChannelLegacyApp_noUserSettings() throws Exception { 257 NotificationChannel channel1 = 258 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_DEFAULT); 259 260 mHelper.createNotificationChannel(pkg, uid, channel1); 261 262 ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), 263 NotificationChannel.DEFAULT_CHANNEL_ID); 264 265 XmlPullParser parser = Xml.newPullParser(); 266 parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), 267 null); 268 parser.nextTag(); 269 mHelper.readXml(parser, false); 270 271 final NotificationChannel updated = 272 mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID); 273 assertEquals(NotificationManager.IMPORTANCE_UNSPECIFIED, updated.getImportance()); 274 assertFalse(updated.canBypassDnd()); 275 assertEquals(NotificationManager.VISIBILITY_NO_OVERRIDE,updated.getLockscreenVisibility()); 276 assertEquals(0, updated.getUserLockedFields()); 277 } 278 279 @Test 280 public void testChannelXml_defaultChannelUpdatedApp() throws Exception { 281 final ApplicationInfo updated = new ApplicationInfo(); 282 updated.targetSdkVersion = Build.VERSION_CODES.N_MR1 + 1; 283 when(mPm.getApplicationInfo(anyString(), anyInt())).thenReturn(updated); 284 285 NotificationChannel channel1 = 286 new NotificationChannel("id1", "name1", NotificationManager.IMPORTANCE_MIN); 287 288 mHelper.createNotificationChannel(pkg, uid, channel1); 289 290 ByteArrayOutputStream baos = writeXmlAndPurge(pkg, uid, channel1.getId(), 291 NotificationChannel.DEFAULT_CHANNEL_ID); 292 293 XmlPullParser parser = Xml.newPullParser(); 294 parser.setInput(new BufferedInputStream(new ByteArrayInputStream(baos.toByteArray())), 295 null); 296 parser.nextTag(); 297 mHelper.readXml(parser, false); 298 299 assertEquals(NotificationManager.IMPORTANCE_LOW, mHelper.getNotificationChannel( 300 pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID).getImportance()); 301 } 302 303 @Test 304 public void testChannelXml_upgradeCreateDefaultChannel() throws Exception { 305 final String preupgradeXml = "<ranking version=\"1\">\n" 306 + "<package name=\"" + pkg + "\" importance=\"" + NotificationManager.IMPORTANCE_HIGH 307 + "\" priority=\"" + Notification.PRIORITY_MAX + "\" visibility=\"" 308 + Notification.VISIBILITY_SECRET + "\"" +" uid=\"" + uid + "\" />\n" 309 + "<package name=\"" + pkg2 + "\" uid=\"" + uid2 + "\" visibility=\"" 310 + Notification.VISIBILITY_PRIVATE + "\" />\n" 311 + "</ranking>"; 312 XmlPullParser parser = Xml.newPullParser(); 313 parser.setInput(new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), 314 null); 315 parser.nextTag(); 316 mHelper.readXml(parser, false); 317 318 final NotificationChannel updated1 = 319 mHelper.getNotificationChannel(pkg, uid, NotificationChannel.DEFAULT_CHANNEL_ID); 320 assertEquals(NotificationManager.IMPORTANCE_HIGH, updated1.getImportance()); 321 assertTrue(updated1.canBypassDnd()); 322 assertEquals(Notification.VISIBILITY_SECRET, updated1.getLockscreenVisibility()); 323 assertEquals(NotificationChannel.USER_LOCKED_IMPORTANCE 324 | NotificationChannel.USER_LOCKED_PRIORITY 325 | NotificationChannel.USER_LOCKED_VISIBILITY, updated1.getUserLockedFields()); 326 327 final NotificationChannel updated2 = 328 mHelper.getNotificationChannel(pkg2, uid2, NotificationChannel.DEFAULT_CHANNEL_ID); 329 // clamped 330 assertEquals(NotificationManager.IMPORTANCE_LOW, updated2.getImportance()); 331 assertFalse(updated2.canBypassDnd()); 332 assertEquals(Notification.VISIBILITY_PRIVATE, updated2.getLockscreenVisibility()); 333 assertEquals(NotificationChannel.USER_LOCKED_VISIBILITY, updated2.getUserLockedFields()); 334 } 335 336 @Test 337 public void testUpdate_userLockedImportance() throws Exception { 338 // all fields locked by user 339 final NotificationChannel channel = 340 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW); 341 channel.lockFields(NotificationChannel.USER_LOCKED_IMPORTANCE); 342 343 mHelper.createNotificationChannel(pkg, uid, channel); 344 345 // same id, try to update 346 final NotificationChannel channel2 = 347 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 348 349 mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2); 350 351 // no fields should be changed 352 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId())); 353 } 354 355 @Test 356 public void testUpdate_userLockedVisibility() throws Exception { 357 // all fields locked by user 358 final NotificationChannel channel = 359 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW); 360 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 361 channel.lockFields(NotificationChannel.USER_LOCKED_VISIBILITY); 362 363 mHelper.createNotificationChannel(pkg, uid, channel); 364 365 // same id, try to update 366 final NotificationChannel channel2 = 367 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 368 channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); 369 370 mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2); 371 372 // no fields should be changed 373 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId())); 374 } 375 376 @Test 377 public void testUpdate_userLockedVibration() throws Exception { 378 // all fields locked by user 379 final NotificationChannel channel = 380 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW); 381 channel.setLights(false); 382 channel.lockFields(NotificationChannel.USER_LOCKED_VIBRATION); 383 384 mHelper.createNotificationChannel(pkg, uid, channel); 385 386 // same id, try to update 387 final NotificationChannel channel2 = 388 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 389 channel2.setVibration(true); 390 391 mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2); 392 393 // no fields should be changed 394 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId())); 395 } 396 397 @Test 398 public void testUpdate_userLockedLights() throws Exception { 399 // all fields locked by user 400 final NotificationChannel channel = 401 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW); 402 channel.setLights(false); 403 channel.lockFields(NotificationChannel.USER_LOCKED_LIGHTS); 404 405 mHelper.createNotificationChannel(pkg, uid, channel); 406 407 // same id, try to update 408 final NotificationChannel channel2 = 409 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 410 channel2.setLights(true); 411 412 mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2); 413 414 // no fields should be changed 415 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId())); 416 } 417 418 @Test 419 public void testUpdate_userLockedPriority() throws Exception { 420 // all fields locked by user 421 final NotificationChannel channel = 422 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW); 423 channel.setBypassDnd(true); 424 channel.lockFields(NotificationChannel.USER_LOCKED_PRIORITY); 425 426 mHelper.createNotificationChannel(pkg, uid, channel); 427 428 // same id, try to update all fields 429 final NotificationChannel channel2 = 430 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 431 channel2.setBypassDnd(false); 432 433 mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2); 434 435 // no fields should be changed 436 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId())); 437 } 438 439 @Test 440 public void testUpdate_userLockedRingtone() throws Exception { 441 // all fields locked by user 442 final NotificationChannel channel = 443 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW); 444 channel.setRingtone(new Uri.Builder().scheme("test").build()); 445 channel.lockFields(NotificationChannel.USER_LOCKED_RINGTONE); 446 447 mHelper.createNotificationChannel(pkg, uid, channel); 448 449 // same id, try to update all fields 450 final NotificationChannel channel2 = 451 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 452 channel2.setRingtone(new Uri.Builder().scheme("test2").build()); 453 454 mHelper.updateNotificationChannelFromRanker(pkg, uid, channel2); 455 456 // no fields should be changed 457 assertEquals(channel, mHelper.getNotificationChannel(pkg, uid, channel.getId())); 458 } 459 460 @Test 461 public void testUpdate() throws Exception { 462 // no fields locked by user 463 final NotificationChannel channel = 464 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_LOW); 465 channel.setRingtone(new Uri.Builder().scheme("test").build()); 466 channel.setLights(true); 467 channel.setBypassDnd(true); 468 channel.setLockscreenVisibility(Notification.VISIBILITY_SECRET); 469 470 mHelper.createNotificationChannel(pkg, uid, channel); 471 472 // same id, try to update all fields 473 final NotificationChannel channel2 = 474 new NotificationChannel("id2", "name2", NotificationManager.IMPORTANCE_HIGH); 475 channel2.setRingtone(new Uri.Builder().scheme("test2").build()); 476 channel2.setLights(false); 477 channel2.setBypassDnd(false); 478 channel2.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); 479 480 mHelper.updateNotificationChannel(pkg, uid, channel2); 481 482 // all fields should be changed 483 assertEquals(channel2, mHelper.getNotificationChannel(pkg, uid, channel.getId())); 484 } 485 486 @Test 487 public void testGetChannelWithFallback() throws Exception { 488 NotificationChannel channel = 489 mHelper.getNotificationChannelWithFallback(pkg, uid, "garbage"); 490 assertEquals(NotificationChannel.DEFAULT_CHANNEL_ID, channel.getId()); 491 } 492} 493