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(getContext().getPackageName()), 120 eq(getContext().getPackageName()), 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 "Who has seen the wind?", 218 "Neither I nor you.", 219 "But when the leaves hang trembling,", 220 "The wind is passing through.", 221 "Who has seen the wind?", 222 "Neither you nor I.", 223 "But when the trees bow down their heads,", 224 "The wind is passing by." 225 }; 226 227 @Test testMessaging()228 public void testMessaging() throws Exception { 229 final String aTag = "messagingTag"; 230 final String aText = "messagingText"; 231 ArrayList<String> args = new ArrayList<>(); 232 args.add("notify"); 233 args.add("--style"); 234 args.add("messaging"); 235 args.add("--conversation"); 236 args.add("Sonnet 18"); 237 final int startOfLineArgs = args.size(); 238 for (int i = 0; i < MESSAGES.length; i++) { 239 args.add("--message"); 240 args.add(String.format("%s:%s", 241 PEOPLE[i % PEOPLE.length], 242 MESSAGES[i % MESSAGES.length])); 243 } 244 args.add(aTag); 245 args.add(aText); 246 247 doCmd(args.toArray(new String[0])); 248 final Notification captured = captureNotification(aTag); 249 assertSame(Notification.MessagingStyle.class, captured.getNotificationStyle()); 250 final Notification.Builder builder = 251 Notification.Builder.recoverBuilder(mContext, captured); 252 final Notification.MessagingStyle messagingStyle = 253 (Notification.MessagingStyle) (builder.getStyle()); 254 255 assertEquals("Sonnet 18", messagingStyle.getConversationTitle()); 256 final List<Notification.MessagingStyle.Message> messages = messagingStyle.getMessages(); 257 for (int i = 0; i < messages.size(); i++) { 258 final Notification.MessagingStyle.Message m = messages.get(i); 259 assertEquals(MESSAGES[i], m.getText()); 260 assertEquals(PEOPLE[i % PEOPLE.length], m.getSenderPerson().getName()); 261 } 262 263 } 264 } 265