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