1 /* 2 * Copyright (C) 2018 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 android.app.notification.legacy.cts; 18 19 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; 20 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 21 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; 22 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; 23 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; 24 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; 25 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_ON; 26 import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_STATUS_BAR; 27 28 import static junit.framework.Assert.assertEquals; 29 30 import android.app.ActivityManager; 31 import android.app.Instrumentation; 32 import android.app.Notification; 33 import android.app.NotificationChannel; 34 import android.app.NotificationManager; 35 import android.app.PendingIntent; 36 import android.app.UiAutomation; 37 import android.content.ComponentName; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.os.ParcelFileDescriptor; 41 import android.provider.Telephony.Threads; 42 import android.service.notification.NotificationListenerService; 43 import android.support.test.InstrumentationRegistry; 44 import android.support.test.runner.AndroidJUnit4; 45 import android.util.Log; 46 47 import junit.framework.Assert; 48 49 import org.junit.Before; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 53 import java.io.FileInputStream; 54 import java.io.IOException; 55 import java.io.InputStream; 56 57 /** 58 * Home for tests that need to verify behavior for apps that target old sdk versions. 59 */ 60 @RunWith(AndroidJUnit4.class) 61 public class LegacyNotificationManagerTest { 62 final String TAG = "LegacyNoManTest"; 63 64 final String NOTIFICATION_CHANNEL_ID = "LegacyNotificationManagerTest"; 65 private NotificationManager mNotificationManager; 66 private ActivityManager mActivityManager; 67 private Context mContext; 68 private MockNotificationListener mListener; 69 70 @Before 71 public void setUp() throws Exception { 72 mContext = InstrumentationRegistry.getContext(); 73 toggleListenerAccess(MockNotificationListener.getId(), 74 InstrumentationRegistry.getInstrumentation(), false); 75 mNotificationManager = (NotificationManager) mContext.getSystemService( 76 Context.NOTIFICATION_SERVICE); 77 mNotificationManager.createNotificationChannel(new NotificationChannel( 78 NOTIFICATION_CHANNEL_ID, "name", NotificationManager.IMPORTANCE_DEFAULT)); 79 mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE); 80 } 81 82 @Test 83 public void testPrePCannotToggleAlarmsAndMediaTest() throws Exception { 84 if (mActivityManager.isLowRamDevice()) { 85 return; 86 } 87 toggleNotificationPolicyAccess(mContext.getPackageName(), 88 InstrumentationRegistry.getInstrumentation(), true); 89 90 // Pre-P cannot toggle alarms and media 91 NotificationManager.Policy origPolicy = mNotificationManager.getNotificationPolicy(); 92 int alarmBit = origPolicy.priorityCategories & NotificationManager.Policy 93 .PRIORITY_CATEGORY_ALARMS; 94 int mediaBit = origPolicy.priorityCategories & NotificationManager.Policy 95 .PRIORITY_CATEGORY_MEDIA; 96 int systemBit = origPolicy.priorityCategories & NotificationManager.Policy 97 .PRIORITY_CATEGORY_SYSTEM; 98 99 // attempt to toggle off alarms, media, system: 100 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy(0, 0, 0)); 101 NotificationManager.Policy policy = mNotificationManager.getNotificationPolicy(); 102 assertEquals(alarmBit, policy.priorityCategories 103 & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS); 104 assertEquals(mediaBit, policy.priorityCategories 105 & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA); 106 assertEquals(systemBit, policy.priorityCategories 107 & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM); 108 109 // attempt to toggle on alarms, media, system: 110 mNotificationManager.setNotificationPolicy(new NotificationManager.Policy( 111 NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS 112 | NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA, 0, 0)); 113 policy = mNotificationManager.getNotificationPolicy(); 114 assertEquals(alarmBit, policy.priorityCategories 115 & NotificationManager.Policy.PRIORITY_CATEGORY_ALARMS); 116 assertEquals(mediaBit, policy.priorityCategories 117 & NotificationManager.Policy.PRIORITY_CATEGORY_MEDIA); 118 assertEquals(systemBit, policy.priorityCategories 119 & NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM); 120 121 toggleNotificationPolicyAccess(mContext.getPackageName(), 122 InstrumentationRegistry.getInstrumentation(), false); 123 } 124 125 @Test 126 public void testSetNotificationPolicy_preP_setOldFields() throws Exception { 127 if (mActivityManager.isLowRamDevice()) { 128 return; 129 } 130 toggleNotificationPolicyAccess(mContext.getPackageName(), 131 InstrumentationRegistry.getInstrumentation(), true); 132 133 NotificationManager.Policy userPolicy = mNotificationManager.getNotificationPolicy(); 134 135 NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, 136 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF); 137 mNotificationManager.setNotificationPolicy(appPolicy); 138 139 int expected = userPolicy.suppressedVisualEffects 140 | SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_SCREEN_OFF 141 | SUPPRESSED_EFFECT_PEEK | SUPPRESSED_EFFECT_LIGHTS 142 | SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; 143 144 assertEquals(expected, 145 mNotificationManager.getNotificationPolicy().suppressedVisualEffects); 146 } 147 148 @Test 149 public void testSetNotificationPolicy_preP_setNewFields() throws Exception { 150 if (mActivityManager.isLowRamDevice()) { 151 return; 152 } 153 toggleNotificationPolicyAccess(mContext.getPackageName(), 154 InstrumentationRegistry.getInstrumentation(), true); 155 156 NotificationManager.Policy userPolicy = mNotificationManager.getNotificationPolicy(); 157 158 NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, 159 SUPPRESSED_EFFECT_NOTIFICATION_LIST); 160 mNotificationManager.setNotificationPolicy(appPolicy); 161 162 int expected = userPolicy.suppressedVisualEffects; 163 expected &= ~ SUPPRESSED_EFFECT_SCREEN_OFF & ~ SUPPRESSED_EFFECT_SCREEN_ON; 164 assertEquals(expected, 165 mNotificationManager.getNotificationPolicy().suppressedVisualEffects); 166 167 toggleNotificationPolicyAccess(mContext.getPackageName(), 168 InstrumentationRegistry.getInstrumentation(), false); 169 } 170 171 @Test 172 public void testSuspendPackage() throws Exception { 173 toggleListenerAccess(MockNotificationListener.getId(), 174 InstrumentationRegistry.getInstrumentation(), true); 175 Thread.sleep(500); // wait for listener to be allowed 176 177 mListener = MockNotificationListener.getInstance(); 178 Assert.assertNotNull(mListener); 179 180 sendNotification(1, R.drawable.icon_black); 181 Thread.sleep(500); // wait for notification listener to receive notification 182 assertEquals(1, mListener.mPosted.size()); 183 mListener.resetData(); 184 185 // suspend package, listener receives onRemoved 186 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 187 true); 188 Thread.sleep(500); // wait for notification listener to get response 189 assertEquals(1, mListener.mRemoved.size()); 190 191 // unsuspend package, listener receives onPosted 192 suspendPackage(mContext.getPackageName(), InstrumentationRegistry.getInstrumentation(), 193 false); 194 Thread.sleep(500); // wait for notification listener to get response 195 assertEquals(1, mListener.mPosted.size()); 196 197 toggleListenerAccess(MockNotificationListener.getId(), 198 InstrumentationRegistry.getInstrumentation(), false); 199 200 mListener.resetData(); 201 } 202 203 @Test 204 public void testSetNotificationPolicy_preP_setOldNewFields() throws Exception { 205 if (mActivityManager.isLowRamDevice()) { 206 return; 207 } 208 toggleNotificationPolicyAccess(mContext.getPackageName(), 209 InstrumentationRegistry.getInstrumentation(), true); 210 211 NotificationManager.Policy userPolicy = mNotificationManager.getNotificationPolicy(); 212 213 NotificationManager.Policy appPolicy = new NotificationManager.Policy(0, 0, 0, 214 SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_STATUS_BAR); 215 mNotificationManager.setNotificationPolicy(appPolicy); 216 217 int expected = userPolicy.suppressedVisualEffects 218 | SUPPRESSED_EFFECT_SCREEN_ON | SUPPRESSED_EFFECT_PEEK; 219 expected &= ~ SUPPRESSED_EFFECT_SCREEN_OFF; 220 assertEquals(expected, 221 mNotificationManager.getNotificationPolicy().suppressedVisualEffects); 222 223 toggleNotificationPolicyAccess(mContext.getPackageName(), 224 InstrumentationRegistry.getInstrumentation(), false); 225 } 226 227 private void sendNotification(final int id, final int icon) throws Exception { 228 sendNotification(id, null, icon); 229 } 230 231 private void sendNotification(final int id, String groupKey, final int icon) throws Exception { 232 final Intent intent = new Intent(Intent.ACTION_MAIN, Threads.CONTENT_URI); 233 234 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP 235 | Intent.FLAG_ACTIVITY_CLEAR_TOP); 236 intent.setAction(Intent.ACTION_MAIN); 237 238 final PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0); 239 final Notification notification = 240 new Notification.Builder(mContext, NOTIFICATION_CHANNEL_ID) 241 .setSmallIcon(icon) 242 .setWhen(System.currentTimeMillis()) 243 .setContentTitle("notify#" + id) 244 .setContentText("This is #" + id + "notification ") 245 .setContentIntent(pendingIntent) 246 .setGroup(groupKey) 247 .build(); 248 mNotificationManager.notify(id, notification); 249 } 250 251 private void toggleNotificationPolicyAccess(String packageName, 252 Instrumentation instrumentation, boolean on) throws IOException { 253 254 String command = " cmd notification " + (on ? "allow_dnd " : "disallow_dnd ") + packageName; 255 256 runCommand(command, instrumentation); 257 258 NotificationManager nm = mContext.getSystemService(NotificationManager.class); 259 Assert.assertEquals("Notification Policy Access Grant is " + 260 nm.isNotificationPolicyAccessGranted() + " not " + on, on, 261 nm.isNotificationPolicyAccessGranted()); 262 } 263 264 private void suspendPackage(String packageName, 265 Instrumentation instrumentation, boolean suspend) throws IOException { 266 String command = " cmd notification " + (suspend ? "suspend_package " 267 : "unsuspend_package ") + packageName; 268 269 runCommand(command, instrumentation); 270 } 271 272 private void toggleListenerAccess(String componentName, Instrumentation instrumentation, 273 boolean on) throws IOException { 274 275 String command = " cmd notification " + (on ? "allow_listener " : "disallow_listener ") 276 + componentName; 277 278 runCommand(command, instrumentation); 279 280 final NotificationManager nm = mContext.getSystemService(NotificationManager.class); 281 final ComponentName listenerComponent = MockNotificationListener.getComponentName(); 282 Assert.assertTrue(listenerComponent + " has not been granted access", 283 nm.isNotificationListenerAccessGranted(listenerComponent) == on); 284 } 285 286 private void runCommand(String command, Instrumentation instrumentation) throws IOException { 287 UiAutomation uiAutomation = instrumentation.getUiAutomation(); 288 // Execute command 289 try (ParcelFileDescriptor fd = uiAutomation.executeShellCommand(command)) { 290 Assert.assertNotNull("Failed to execute shell command: " + command, fd); 291 // Wait for the command to finish by reading until EOF 292 try (InputStream in = new FileInputStream(fd.getFileDescriptor())) { 293 byte[] buffer = new byte[4096]; 294 while (in.read(buffer) > 0) {} 295 } catch (IOException e) { 296 throw new IOException("Could not read stdout of command:" + command, e); 297 } 298 } finally { 299 uiAutomation.destroy(); 300 } 301 } 302 } 303