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 com.android.server.notification; 18 19 import static junit.framework.Assert.assertEquals; 20 import static junit.framework.Assert.assertSame; 21 22 import static org.hamcrest.Matchers.instanceOf; 23 import static org.junit.Assert.assertThat; 24 import static org.mockito.Mockito.eq; 25 import static org.mockito.Mockito.spy; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 // this is a lazy way to do in/out/err but we're not particularly interested in the output 30 import static java.io.FileDescriptor.err; 31 import static java.io.FileDescriptor.in; 32 import static java.io.FileDescriptor.out; 33 34 import android.app.INotificationManager; 35 import android.app.Notification; 36 import android.graphics.Bitmap; 37 import android.graphics.Canvas; 38 import android.graphics.Color; 39 import android.graphics.drawable.GradientDrawable; 40 import android.graphics.drawable.Icon; 41 import android.os.Binder; 42 import android.os.Handler; 43 import android.os.ResultReceiver; 44 import android.os.ShellCallback; 45 import android.os.UserHandle; 46 import android.test.suitebuilder.annotation.SmallTest; 47 import android.testing.AndroidTestingRunner; 48 import android.testing.TestableContext; 49 import android.testing.TestableLooper; 50 import android.testing.TestableLooper.RunWithLooper; 51 52 import com.android.server.UiServiceTestCase; 53 54 import org.junit.Before; 55 import org.junit.Test; 56 import org.junit.runner.RunWith; 57 import org.mockito.ArgumentCaptor; 58 import org.mockito.Mock; 59 import org.mockito.MockitoAnnotations; 60 61 import java.util.ArrayList; 62 import java.util.List; 63 64 @SmallTest 65 @RunWith(AndroidTestingRunner.class) 66 @RunWithLooper 67 public class NotificationShellCmdTest extends UiServiceTestCase { 68 private final Binder mBinder = new Binder(); 69 private final ShellCallback mCallback = new ShellCallback(); 70 private final TestableContext mTestableContext = spy(getContext()); 71 @Mock 72 NotificationManagerService mMockService; 73 @Mock 74 INotificationManager mMockBinderService; 75 private TestableLooper mTestableLooper; 76 private ResultReceiver mResultReceiver; 77 78 @Before setUp()79 public void setUp() throws Exception { 80 MockitoAnnotations.initMocks(this); 81 82 mTestableLooper = TestableLooper.get(this); 83 mResultReceiver = new ResultReceiver(new Handler(mTestableLooper.getLooper())); 84 85 when(mMockService.getContext()).thenReturn(mTestableContext); 86 when(mMockService.getBinderService()).thenReturn(mMockBinderService); 87 } 88 createTestBitmap()89 private Bitmap createTestBitmap() { 90 final Bitmap bits = Bitmap.createBitmap(512, 512, Bitmap.Config.ARGB_8888); 91 final Canvas canvas = new Canvas(bits); 92 final GradientDrawable grad = new GradientDrawable(GradientDrawable.Orientation.TL_BR, 93 new int[]{Color.RED, Color.YELLOW, Color.GREEN, 94 Color.CYAN, Color.BLUE, Color.MAGENTA}); 95 grad.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); 96 grad.draw(canvas); 97 return bits; 98 } 99 doCmd(String... args)100 private void doCmd(String... args) { 101 System.out.println("Running command: " + String.join(" ", args)); 102 new TestNotificationShellCmd(mMockService) 103 .exec(mBinder, in, out, err, args, mCallback, mResultReceiver); 104 } 105 106 @Test testNoArgs()107 public void testNoArgs() throws Exception { 108 doCmd(); 109 } 110 111 @Test testHelp()112 public void testHelp() throws Exception { 113 doCmd("--help"); 114 } 115 captureNotification(String aTag)116 Notification captureNotification(String aTag) throws Exception { 117 ArgumentCaptor<Notification> notificationCaptor = 118 ArgumentCaptor.forClass(Notification.class); 119 verify(mMockBinderService).enqueueNotificationWithTag( 120 eq(getContext().getPackageName()), 121 eq(getContext().getPackageName()), 122 eq(aTag), 123 eq(NotificationShellCmd.NOTIFICATION_ID), 124 notificationCaptor.capture(), 125 eq(UserHandle.getCallingUserId())); 126 return notificationCaptor.getValue(); 127 } 128 129 @Test testBasic()130 public void testBasic() throws Exception { 131 final String aTag = "aTag"; 132 final String aText = "someText"; 133 final String aTitle = "theTitle"; 134 doCmd("notify", 135 "--title", aTitle, 136 aTag, aText); 137 final Notification captured = captureNotification(aTag); 138 assertEquals(aText, captured.extras.getString(Notification.EXTRA_TEXT)); 139 assertEquals(aTitle, captured.extras.getString(Notification.EXTRA_TITLE)); 140 } 141 142 @Test testIcon()143 public void testIcon() throws Exception { 144 final String aTag = "aTag"; 145 final String aText = "someText"; 146 doCmd("notify", "--icon", "@android:drawable/stat_sys_adb", aTag, aText); 147 final Notification captured = captureNotification(aTag); 148 final Icon icon = captured.getSmallIcon(); 149 assertEquals("android", icon.getResPackage()); 150 assertEquals(com.android.internal.R.drawable.stat_sys_adb, icon.getResId()); 151 } 152 153 @Test testBigText()154 public void testBigText() throws Exception { 155 final String aTag = "aTag"; 156 final String aText = "someText"; 157 final String bigText = "someBigText"; 158 doCmd("notify", 159 "--style", "bigtext", 160 "--big-text", bigText, 161 aTag, aText); 162 final Notification captured = captureNotification(aTag); 163 assertSame(captured.getNotificationStyle(), Notification.BigTextStyle.class); 164 assertEquals(aText, captured.extras.getString(Notification.EXTRA_TEXT)); 165 assertEquals(bigText, captured.extras.getString(Notification.EXTRA_BIG_TEXT)); 166 } 167 168 @Test testBigPicture()169 public void testBigPicture() throws Exception { 170 final String aTag = "aTag"; 171 final String aText = "someText"; 172 final String bigPicture = "@android:drawable/default_wallpaper"; 173 doCmd("notify", 174 "--style", "bigpicture", 175 "--picture", bigPicture, 176 aTag, aText); 177 final Notification captured = captureNotification(aTag); 178 assertSame(captured.getNotificationStyle(), Notification.BigPictureStyle.class); 179 final Object pic = captured.extras.get(Notification.EXTRA_PICTURE); 180 assertThat(pic, instanceOf(Bitmap.class)); 181 } 182 183 @Test testInbox()184 public void testInbox() throws Exception { 185 final int n = 25; 186 final String aTag = "inboxTag"; 187 final String aText = "inboxText"; 188 ArrayList<String> args = new ArrayList<>(); 189 args.add("notify"); 190 args.add("--style"); 191 args.add("inbox"); 192 final int startOfLineArgs = args.size(); 193 for (int i = 0; i < n; i++) { 194 args.add("--line"); 195 args.add(String.format("Line %02d", i)); 196 } 197 args.add(aTag); 198 args.add(aText); 199 200 doCmd(args.toArray(new String[0])); 201 final Notification captured = captureNotification(aTag); 202 assertSame(captured.getNotificationStyle(), Notification.InboxStyle.class); 203 final Notification.Builder builder = 204 Notification.Builder.recoverBuilder(mContext, captured); 205 final ArrayList<CharSequence> lines = 206 ((Notification.InboxStyle) (builder.getStyle())).getLines(); 207 for (int i = 0; i < n; i++) { 208 assertEquals(lines.get(i), args.get(1 + 2 * i + startOfLineArgs)); 209 } 210 } 211 212 static final String[] PEOPLE = { 213 "Alice", 214 "Bob", 215 "Charlotte" 216 }; 217 static final String[] MESSAGES = { 218 "Shall I compare thee to a summer's day?", 219 "Thou art more lovely and more temperate:", 220 "Rough winds do shake the darling buds of May,", 221 "And summer's lease hath all too short a date;", 222 "Sometime too hot the eye of heaven shines,", 223 "And often is his gold complexion dimm'd;", 224 "And every fair from fair sometime declines,", 225 "By chance or nature's changing course untrimm'd;", 226 "But thy eternal summer shall not fade,", 227 "Nor lose possession of that fair thou ow'st;", 228 "Nor shall death brag thou wander'st in his shade,", 229 "When in eternal lines to time thou grow'st:", 230 " So long as men can breathe or eyes can see,", 231 " So long lives this, and this gives life to thee.", 232 }; 233 234 @Test testMessaging()235 public void testMessaging() throws Exception { 236 final String aTag = "messagingTag"; 237 final String aText = "messagingText"; 238 ArrayList<String> args = new ArrayList<>(); 239 args.add("notify"); 240 args.add("--style"); 241 args.add("messaging"); 242 args.add("--conversation"); 243 args.add("Sonnet 18"); 244 final int startOfLineArgs = args.size(); 245 for (int i = 0; i < MESSAGES.length; i++) { 246 args.add("--message"); 247 args.add(String.format("%s:%s", 248 PEOPLE[i % PEOPLE.length], 249 MESSAGES[i % MESSAGES.length])); 250 } 251 args.add(aTag); 252 args.add(aText); 253 254 doCmd(args.toArray(new String[0])); 255 final Notification captured = captureNotification(aTag); 256 assertSame(Notification.MessagingStyle.class, captured.getNotificationStyle()); 257 final Notification.Builder builder = 258 Notification.Builder.recoverBuilder(mContext, captured); 259 final Notification.MessagingStyle messagingStyle = 260 (Notification.MessagingStyle) (builder.getStyle()); 261 262 assertEquals("Sonnet 18", messagingStyle.getConversationTitle()); 263 final List<Notification.MessagingStyle.Message> messages = messagingStyle.getMessages(); 264 for (int i = 0; i < messages.size(); i++) { 265 final Notification.MessagingStyle.Message m = messages.get(i); 266 assertEquals(MESSAGES[i], m.getText()); 267 assertEquals(PEOPLE[i % PEOPLE.length], m.getSenderPerson().getName()); 268 } 269 270 } 271 272 /** 273 * Version of NotificationShellCmd that allows this atest to work properly despite coming in 274 * from the wrong uid. 275 */ 276 private final class TestNotificationShellCmd extends NotificationShellCmd { TestNotificationShellCmd(NotificationManagerService service)277 TestNotificationShellCmd(NotificationManagerService service) { 278 super(service); 279 } 280 281 @Override checkShellCommandPermission(int callingUid)282 protected boolean checkShellCommandPermission(int callingUid) { 283 return true; 284 } 285 } 286 } 287