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