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 new NotificationShellCmd(mMockService) 102 .exec(mBinder, in, out, err, args, mCallback, mResultReceiver); 103 } 104 105 @Test testNoArgs()106 public void testNoArgs() throws Exception { 107 doCmd(); 108 } 109 110 @Test testHelp()111 public void testHelp() throws Exception { 112 doCmd("--help"); 113 } 114 captureNotification(String aTag)115 Notification captureNotification(String aTag) throws Exception { 116 ArgumentCaptor<Notification> notificationCaptor = 117 ArgumentCaptor.forClass(Notification.class); 118 verify(mMockBinderService).enqueueNotificationWithTag( 119 eq(NotificationShellCmd.NOTIFICATION_PACKAGE), 120 eq("android"), 121 eq(aTag), 122 eq(NotificationShellCmd.NOTIFICATION_ID), 123 notificationCaptor.capture(), 124 eq(UserHandle.getCallingUserId())); 125 return notificationCaptor.getValue(); 126 } 127 128 @Test testBasic()129 public void testBasic() throws Exception { 130 final String aTag = "aTag"; 131 final String aText = "someText"; 132 final String aTitle = "theTitle"; 133 doCmd("notify", 134 "--title", aTitle, 135 aTag, aText); 136 final Notification captured = captureNotification(aTag); 137 assertEquals(aText, captured.extras.getString(Notification.EXTRA_TEXT)); 138 assertEquals(aTitle, captured.extras.getString(Notification.EXTRA_TITLE)); 139 } 140 141 @Test testIcon()142 public void testIcon() throws Exception { 143 final String aTag = "aTag"; 144 final String aText = "someText"; 145 doCmd("notify", "--icon", "@android:drawable/stat_sys_adb", aTag, aText); 146 final Notification captured = captureNotification(aTag); 147 final Icon icon = captured.getSmallIcon(); 148 assertEquals("android", icon.getResPackage()); 149 assertEquals(com.android.internal.R.drawable.stat_sys_adb, icon.getResId()); 150 } 151 152 @Test testBigText()153 public void testBigText() throws Exception { 154 final String aTag = "aTag"; 155 final String aText = "someText"; 156 final String bigText = "someBigText"; 157 doCmd("notify", 158 "--style", "bigtext", 159 "--big-text", bigText, 160 aTag, aText); 161 final Notification captured = captureNotification(aTag); 162 assertSame(captured.getNotificationStyle(), Notification.BigTextStyle.class); 163 assertEquals(aText, captured.extras.getString(Notification.EXTRA_TEXT)); 164 assertEquals(bigText, captured.extras.getString(Notification.EXTRA_BIG_TEXT)); 165 } 166 167 @Test testBigPicture()168 public void testBigPicture() throws Exception { 169 final String aTag = "aTag"; 170 final String aText = "someText"; 171 final String bigPicture = "@android:drawable/default_wallpaper"; 172 doCmd("notify", 173 "--style", "bigpicture", 174 "--picture", bigPicture, 175 aTag, aText); 176 final Notification captured = captureNotification(aTag); 177 assertSame(captured.getNotificationStyle(), Notification.BigPictureStyle.class); 178 final Object pic = captured.extras.get(Notification.EXTRA_PICTURE); 179 assertThat(pic, instanceOf(Bitmap.class)); 180 } 181 182 @Test testInbox()183 public void testInbox() throws Exception { 184 final int n = 25; 185 final String aTag = "inboxTag"; 186 final String aText = "inboxText"; 187 ArrayList<String> args = new ArrayList<>(); 188 args.add("notify"); 189 args.add("--style"); 190 args.add("inbox"); 191 final int startOfLineArgs = args.size(); 192 for (int i = 0; i < n; i++) { 193 args.add("--line"); 194 args.add(String.format("Line %02d", i)); 195 } 196 args.add(aTag); 197 args.add(aText); 198 199 doCmd(args.toArray(new String[0])); 200 final Notification captured = captureNotification(aTag); 201 assertSame(captured.getNotificationStyle(), Notification.InboxStyle.class); 202 final Notification.Builder builder = 203 Notification.Builder.recoverBuilder(mContext, captured); 204 final ArrayList<CharSequence> lines = 205 ((Notification.InboxStyle) (builder.getStyle())).getLines(); 206 for (int i = 0; i < n; i++) { 207 assertEquals(lines.get(i), args.get(1 + 2 * i + startOfLineArgs)); 208 } 209 } 210 211 static final String[] PEOPLE = { 212 "Alice", 213 "Bob", 214 "Charlotte" 215 }; 216 static final String[] MESSAGES = { 217 "Shall I compare thee to a summer's day?", 218 "Thou art more lovely and more temperate:", 219 "Rough winds do shake the darling buds of May,", 220 "And summer's lease hath all too short a date;", 221 "Sometime too hot the eye of heaven shines,", 222 "And often is his gold complexion dimm'd;", 223 "And every fair from fair sometime declines,", 224 "By chance or nature's changing course untrimm'd;", 225 "But thy eternal summer shall not fade,", 226 "Nor lose possession of that fair thou ow'st;", 227 "Nor shall death brag thou wander'st in his shade,", 228 "When in eternal lines to time thou grow'st:", 229 " So long as men can breathe or eyes can see,", 230 " So long lives this, and this gives life to thee.", 231 }; 232 233 @Test testMessaging()234 public void testMessaging() throws Exception { 235 final String aTag = "messagingTag"; 236 final String aText = "messagingText"; 237 ArrayList<String> args = new ArrayList<>(); 238 args.add("notify"); 239 args.add("--style"); 240 args.add("messaging"); 241 args.add("--conversation"); 242 args.add("Sonnet 18"); 243 final int startOfLineArgs = args.size(); 244 for (int i = 0; i < MESSAGES.length; i++) { 245 args.add("--message"); 246 args.add(String.format("%s:%s", 247 PEOPLE[i % PEOPLE.length], 248 MESSAGES[i % MESSAGES.length])); 249 } 250 args.add(aTag); 251 args.add(aText); 252 253 doCmd(args.toArray(new String[0])); 254 final Notification captured = captureNotification(aTag); 255 assertSame(Notification.MessagingStyle.class, captured.getNotificationStyle()); 256 final Notification.Builder builder = 257 Notification.Builder.recoverBuilder(mContext, captured); 258 final Notification.MessagingStyle messagingStyle = 259 (Notification.MessagingStyle) (builder.getStyle()); 260 261 assertEquals("Sonnet 18", messagingStyle.getConversationTitle()); 262 final List<Notification.MessagingStyle.Message> messages = messagingStyle.getMessages(); 263 for (int i = 0; i < messages.size(); i++) { 264 final Notification.MessagingStyle.Message m = messages.get(i); 265 assertEquals(MESSAGES[i], m.getText()); 266 assertEquals(PEOPLE[i % PEOPLE.length], m.getSenderPerson().getName()); 267 } 268 269 } 270 } 271