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 */ 16 17 package com.android.cts.verifier.notifications; 18 19 import android.app.Notification; 20 import android.content.Intent; 21 import android.content.pm.PackageManager; 22 import android.util.Log; 23 import android.view.View; 24 import android.view.ViewGroup; 25 import com.android.cts.verifier.R; 26 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** 31 * Tests that the notification ranker honors user preferences about package priority. 32 * Users can, in Settings, specify a package as being high priority. This should 33 * result in the notificaitons from that package being ranked higher than those from 34 * other packages. 35 */ 36 public class PackagePriorityVerifierActivity 37 extends InteractiveVerifierActivity { 38 private static final String ACTION_POST = "com.android.cts.robot.ACTION_POST"; 39 private static final String ACTION_CANCEL = "com.android.cts.robot.ACTION_CANCEL"; 40 private static final String EXTRA_ID = "ID"; 41 private static final String EXTRA_NOTIFICATION = "NOTIFICATION"; 42 private static final String NOTIFICATION_BOT_PACKAGE = "com.android.cts.robot"; 43 private CharSequence mAppLabel; 44 45 @Override 46 int getTitleResource() { 47 return R.string.package_priority_test; 48 } 49 50 @Override 51 int getInstructionsResource() { 52 return R.string.package_priority_info; 53 } 54 55 // Test Setup 56 57 @Override 58 protected List<InteractiveTestCase> createTestItems() { 59 mAppLabel = getString(R.string.app_name); 60 List<InteractiveTestCase> tests = new ArrayList<>(17); 61 tests.add(new CheckForBot()); 62 tests.add(new IsEnabledTest()); 63 tests.add(new ServiceStartedTest()); 64 tests.add(new WaitForSetPriorityDefault()); 65 tests.add(new DefaultOrderTest()); 66 tests.add(new WaitForSetPriorityHigh()); 67 tests.add(new PackagePriorityOrderTest()); 68 return tests; 69 } 70 71 // Tests 72 73 /** Make sure the helper package is installed. */ 74 protected class CheckForBot extends InteractiveTestCase { 75 @Override 76 View inflate(ViewGroup parent) { 77 return createAutoItem(parent, R.string.package_priority_bot); 78 } 79 80 @Override 81 void test() { 82 PackageManager pm = mContext.getPackageManager(); 83 try { 84 pm.getPackageInfo(NOTIFICATION_BOT_PACKAGE, 0); 85 status = PASS; 86 } catch (PackageManager.NameNotFoundException e) { 87 status = FAIL; 88 logFail("You must install the CTS Robot helper, aka " + NOTIFICATION_BOT_PACKAGE); 89 } 90 next(); 91 } 92 } 93 94 /** Wait for the user to set the target package priority to default. */ 95 protected class WaitForSetPriorityDefault extends InteractiveTestCase { 96 @Override 97 View inflate(ViewGroup parent) { 98 return createRetryItem(parent, R.string.package_priority_default, mAppLabel); 99 } 100 101 @Override 102 void setUp() { 103 Log.i("WaitForSetPriorityDefault", "waiting for user"); 104 status = WAIT_FOR_USER; 105 } 106 107 @Override 108 void test() { 109 status = PASS; 110 next(); 111 } 112 113 @Override 114 void tearDown() { 115 MockListener.resetListenerData(mContext); 116 delay(); 117 } 118 } 119 120 /** Wait for the user to set the target package priority to high. */ 121 protected class WaitForSetPriorityHigh extends InteractiveTestCase { 122 @Override 123 View inflate(ViewGroup parent) { 124 return createRetryItem(parent, R.string.package_priority_high, mAppLabel); 125 } 126 127 @Override 128 void setUp() { 129 Log.i("WaitForSetPriorityHigh", "waiting for user"); 130 status = WAIT_FOR_USER; 131 } 132 133 @Override 134 void test() { 135 status = PASS; 136 next(); 137 } 138 139 @Override 140 void tearDown() { 141 MockListener.resetListenerData(mContext); 142 delay(); 143 } 144 } 145 146 /** 147 * With default priority, the notifcations should be reverse-ordered by time. 148 * A is before B, and therefor should B should rank before A. 149 */ 150 protected class DefaultOrderTest extends InteractiveTestCase { 151 @Override 152 View inflate(ViewGroup parent) { 153 return createAutoItem(parent, R.string.attention_default_order); 154 } 155 156 @Override 157 void setUp() { 158 sendNotifications(); 159 status = READY; 160 // wait for notifications to move through the system 161 delay(); 162 } 163 164 @Override 165 void test() { 166 MockListener.probeListenerOrder(mContext, 167 new MockListener.StringListResultCatcher() { 168 @Override 169 public void accept(List<String> orderedKeys) { 170 int rankA = indexOfPackageInKeys(orderedKeys, getPackageName()); 171 int rankB = indexOfPackageInKeys(orderedKeys, NOTIFICATION_BOT_PACKAGE); 172 if (rankB != -1 && rankB < rankA) { 173 status = PASS; 174 } else { 175 logFail("expected rankA (" + rankA + ") > rankB (" + rankB + ")"); 176 status = FAIL; 177 } 178 next(); 179 } 180 }); 181 delay(); // in case the catcher never returns 182 } 183 184 @Override 185 void tearDown() { 186 cancelNotifications(); 187 MockListener.resetListenerData(mContext); 188 delay(); 189 } 190 } 191 192 /** 193 * With higher package priority, A should rank above B. 194 */ 195 protected class PackagePriorityOrderTest extends InteractiveTestCase { 196 @Override 197 View inflate(ViewGroup parent) { 198 return createAutoItem(parent, R.string.package_priority_user_order); 199 } 200 201 @Override 202 void setUp() { 203 sendNotifications(); 204 status = READY; 205 // wait for notifications to move through the system 206 delay(); 207 } 208 209 @Override 210 void test() { 211 MockListener.probeListenerOrder(mContext, 212 new MockListener.StringListResultCatcher() { 213 @Override 214 public void accept(List<String> orderedKeys) { 215 int rankA = indexOfPackageInKeys(orderedKeys, getPackageName()); 216 int rankB = indexOfPackageInKeys(orderedKeys, NOTIFICATION_BOT_PACKAGE); 217 if (rankA != -1 && rankA < rankB) { 218 status = PASS; 219 } else { 220 logFail("expected rankA (" + rankA + ") < rankB (" + rankB + ")"); 221 status = FAIL; 222 } 223 next(); 224 } 225 }); 226 delay(); // in case the catcher never returns 227 } 228 229 @Override 230 void tearDown() { 231 cancelNotifications(); 232 MockListener.resetListenerData(mContext); 233 delay(); 234 } 235 } 236 // Utilities 237 238 private void sendNotifications() { 239 // post ours first, with an explicit time in the past to avoid any races. 240 Notification.Builder alice = new Notification.Builder(mContext) 241 .setContentTitle("alice title") 242 .setContentText("alice content") 243 .setSmallIcon(R.drawable.ic_stat_alice) 244 .setWhen(System.currentTimeMillis() - 10000L) 245 .setPriority(Notification.PRIORITY_DEFAULT); 246 mNm.notify(0, alice.build()); 247 248 // then post theirs, so it should be higher by default due to recency 249 Notification.Builder bob = new Notification.Builder(mContext) 250 .setContentTitle("bob title") 251 .setContentText("bob content") 252 .setSmallIcon(android.R.drawable.stat_notify_error) // must be global resource 253 .setWhen(System.currentTimeMillis()) 254 .setPriority(Notification.PRIORITY_DEFAULT); 255 Intent postIntent = new Intent(ACTION_POST); 256 postIntent.setPackage(NOTIFICATION_BOT_PACKAGE); 257 postIntent.putExtra(EXTRA_ID, 0); 258 postIntent.putExtra(EXTRA_NOTIFICATION, bob.build()); 259 postIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 260 sendBroadcast(postIntent); 261 } 262 263 private void cancelNotifications() { 264 //cancel ours 265 mNm.cancelAll(); 266 //cancel theirs 267 Intent cancelIntent = new Intent(ACTION_CANCEL); 268 cancelIntent.setPackage(NOTIFICATION_BOT_PACKAGE); 269 cancelIntent.putExtra(EXTRA_ID, 0); 270 cancelIntent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 271 sendBroadcast(cancelIntent); 272 } 273 274 /** Search a list of notification keys for a given packageName. */ 275 private int indexOfPackageInKeys(List<String> orderedKeys, String packageName) { 276 for (int i = 0; i < orderedKeys.size(); i++) { 277 if (orderedKeys.get(i).contains(packageName)) { 278 return i; 279 } 280 } 281 return -1; 282 } 283 } 284