• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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