blob: 9f3d656188e126b46770907548f6767eb0fa4900 [file] [log] [blame]
Danning Chena1bf86d2020-01-14 16:09:24 -08001/*
2 * Copyright (C) 2020 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
17package com.android.server.people.data;
18
19import static android.app.usage.UsageEvents.Event.SHORTCUT_INVOCATION;
20
21import static org.junit.Assert.assertEquals;
22import static org.junit.Assert.assertFalse;
23import static org.junit.Assert.assertTrue;
24import static org.mockito.ArgumentMatchers.any;
25import static org.mockito.ArgumentMatchers.anyBoolean;
26import static org.mockito.ArgumentMatchers.anyInt;
27import static org.mockito.ArgumentMatchers.anyLong;
28import static org.mockito.ArgumentMatchers.anyString;
29import static org.mockito.Mockito.eq;
30import static org.mockito.Mockito.mock;
31import static org.mockito.Mockito.verify;
32import static org.mockito.Mockito.when;
33
34import android.annotation.NonNull;
35import android.annotation.Nullable;
36import android.app.Notification;
37import android.app.Person;
38import android.app.prediction.AppTarget;
39import android.app.prediction.AppTargetEvent;
40import android.app.prediction.AppTargetId;
41import android.app.usage.UsageEvents;
42import android.app.usage.UsageStatsManagerInternal;
43import android.content.ComponentName;
44import android.content.ContentResolver;
45import android.content.Context;
46import android.content.Intent;
47import android.content.IntentFilter;
48import android.content.pm.ShortcutInfo;
49import android.content.pm.ShortcutManager;
50import android.content.pm.ShortcutManager.ShareShortcutInfo;
51import android.content.pm.ShortcutServiceInternal;
52import android.content.pm.UserInfo;
53import android.database.ContentObserver;
54import android.net.Uri;
55import android.os.Looper;
56import android.os.UserHandle;
57import android.os.UserManager;
58import android.provider.ContactsContract;
59import android.service.notification.NotificationListenerService;
60import android.service.notification.StatusBarNotification;
61import android.telephony.TelephonyManager;
62import android.util.Range;
63
64import com.android.internal.app.ChooserActivity;
65import com.android.server.LocalServices;
66
67import org.junit.After;
68import org.junit.Before;
69import org.junit.Test;
70import org.junit.runner.RunWith;
71import org.junit.runners.JUnit4;
72import org.mockito.Mock;
73import org.mockito.MockitoAnnotations;
74
75import java.util.ArrayList;
76import java.util.Arrays;
77import java.util.Collections;
78import java.util.List;
79import java.util.concurrent.ScheduledExecutorService;
80import java.util.concurrent.ScheduledFuture;
81import java.util.concurrent.TimeUnit;
82
83@RunWith(JUnit4.class)
84public final class DataManagerTest {
85
86 private static final int USER_ID_PRIMARY = 0;
87 private static final int USER_ID_PRIMARY_MANAGED = 10;
88 private static final int USER_ID_SECONDARY = 11;
89 private static final String TEST_PKG_NAME = "pkg";
90 private static final String TEST_SHORTCUT_ID = "sc";
91 private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
92 private static final String PHONE_NUMBER = "+1234567890";
93
94 @Mock private Context mContext;
95 @Mock private ShortcutServiceInternal mShortcutServiceInternal;
96 @Mock private UsageStatsManagerInternal mUsageStatsManagerInternal;
97 @Mock private ShortcutManager mShortcutManager;
98 @Mock private UserManager mUserManager;
99 @Mock private TelephonyManager mTelephonyManager;
100 @Mock private ContentResolver mContentResolver;
101 @Mock private ScheduledExecutorService mExecutorService;
102 @Mock private ScheduledFuture mScheduledFuture;
103 @Mock private StatusBarNotification mStatusBarNotification;
104 @Mock private Notification mNotification;
105
106 private DataManager mDataManager;
107 private int mCallingUserId;
108 private TestInjector mInjector;
109
110 @Before
111 public void setUp() {
112 MockitoAnnotations.initMocks(this);
113
114 addLocalServiceMock(ShortcutServiceInternal.class, mShortcutServiceInternal);
115
116 addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal);
117
118 when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
119
120 when(mContext.getSystemService(Context.SHORTCUT_SERVICE)).thenReturn(mShortcutManager);
121 when(mContext.getSystemServiceName(ShortcutManager.class)).thenReturn(
122 Context.SHORTCUT_SERVICE);
123
124 when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
125 when(mContext.getSystemServiceName(UserManager.class)).thenReturn(
126 Context.USER_SERVICE);
127
128 when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
129
130 when(mExecutorService.scheduleAtFixedRate(any(Runnable.class), anyLong(), anyLong(), any(
131 TimeUnit.class))).thenReturn(mScheduledFuture);
132
133 when(mUserManager.getEnabledProfiles(USER_ID_PRIMARY))
134 .thenReturn(Arrays.asList(
135 buildUserInfo(USER_ID_PRIMARY),
136 buildUserInfo(USER_ID_PRIMARY_MANAGED)));
137 when(mUserManager.getEnabledProfiles(USER_ID_SECONDARY))
138 .thenReturn(Collections.singletonList(buildUserInfo(USER_ID_SECONDARY)));
139
140 when(mContext.getContentResolver()).thenReturn(mContentResolver);
141
142 when(mStatusBarNotification.getNotification()).thenReturn(mNotification);
143 when(mStatusBarNotification.getPackageName()).thenReturn(TEST_PKG_NAME);
144 when(mStatusBarNotification.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
145 when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
146
147 mCallingUserId = USER_ID_PRIMARY;
148
149 mInjector = new TestInjector();
150 mDataManager = new DataManager(mContext, mInjector);
151 mDataManager.initialize();
152 }
153
154 @After
155 public void tearDown() {
156 LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
157 LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
158 }
159
160 @Test
161 public void testAccessConversationFromTheSameProfileGroup() {
162 mDataManager.onUserUnlocked(USER_ID_PRIMARY);
163 mDataManager.onUserUnlocked(USER_ID_PRIMARY_MANAGED);
164 mDataManager.onUserUnlocked(USER_ID_SECONDARY);
165
166 mDataManager.onShortcutAddedOrUpdated(
167 buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1",
168 buildPerson(true, false)));
169 mDataManager.onShortcutAddedOrUpdated(
170 buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2",
171 buildPerson(false, true)));
172 mDataManager.onShortcutAddedOrUpdated(
173 buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson()));
174
175 List<ConversationInfo> conversations = new ArrayList<>();
176 mDataManager.forAllPackages(
177 packageData -> packageData.forAllConversations(conversations::add));
178
179 // USER_ID_SECONDARY is not in the same profile group as USER_ID_PRIMARY.
180 assertEquals(2, conversations.size());
181
182 assertEquals("sc_1", conversations.get(0).getShortcutId());
183 assertTrue(conversations.get(0).isPersonImportant());
184 assertFalse(conversations.get(0).isPersonBot());
185 assertFalse(conversations.get(0).isContactStarred());
186 assertEquals(PHONE_NUMBER, conversations.get(0).getContactPhoneNumber());
187
188 assertEquals("sc_2", conversations.get(1).getShortcutId());
189 assertFalse(conversations.get(1).isPersonImportant());
190 assertTrue(conversations.get(1).isPersonBot());
191 assertFalse(conversations.get(0).isContactStarred());
192 assertEquals(PHONE_NUMBER, conversations.get(0).getContactPhoneNumber());
193 }
194
195 @Test
196 public void testAccessConversationForUnlockedUsersOnly() {
197 mDataManager.onUserUnlocked(USER_ID_PRIMARY);
198 mDataManager.onShortcutAddedOrUpdated(
199 buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson()));
200 mDataManager.onShortcutAddedOrUpdated(
201 buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson()));
202
203 List<ConversationInfo> conversations = new ArrayList<>();
204 mDataManager.forAllPackages(
205 packageData -> packageData.forAllConversations(conversations::add));
206
207 // USER_ID_PRIMARY_MANAGED is not locked, so only USER_ID_PRIMARY's conversation is stored.
208 assertEquals(1, conversations.size());
209 assertEquals("sc_1", conversations.get(0).getShortcutId());
210
211 mDataManager.onUserStopped(USER_ID_PRIMARY);
212 conversations.clear();
213 mDataManager.forAllPackages(
214 packageData -> packageData.forAllConversations(conversations::add));
215 assertTrue(conversations.isEmpty());
216 }
217
218 @Test
219 public void testGetShortcut() {
220 mDataManager.getShortcut(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID);
221 verify(mShortcutServiceInternal).getShortcuts(anyInt(), anyString(), anyLong(),
222 eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)),
223 eq(null), anyInt(), eq(USER_ID_PRIMARY), anyInt(), anyInt());
224 }
225
226 @Test
227 public void testGetShareTargets() {
228 mDataManager.onUserUnlocked(USER_ID_PRIMARY);
229
230 ShortcutInfo shortcut1 =
231 buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson());
232 ShareShortcutInfo shareShortcut1 =
233 new ShareShortcutInfo(shortcut1, new ComponentName("pkg_1", "activity"));
234
235 ShortcutInfo shortcut2 =
236 buildShortcutInfo("pkg_2", USER_ID_PRIMARY, "sc_2", buildPerson());
237 ShareShortcutInfo shareShortcut2 =
238 new ShareShortcutInfo(shortcut2, new ComponentName("pkg_2", "activity"));
239 mDataManager.onShortcutAddedOrUpdated(shortcut2);
240
241 when(mShortcutManager.getShareTargets(any(IntentFilter.class)))
242 .thenReturn(Arrays.asList(shareShortcut1, shareShortcut2));
243
244 List<ShareShortcutInfo> shareShortcuts =
245 mDataManager.getConversationShareTargets(new IntentFilter());
246 // Only "sc_2" is stored as a conversation.
247 assertEquals(1, shareShortcuts.size());
248 assertEquals("sc_2", shareShortcuts.get(0).getShortcutInfo().getId());
249 }
250
251 @Test
252 public void testReportAppTargetEvent() throws IntentFilter.MalformedMimeTypeException {
253 mDataManager.onUserUnlocked(USER_ID_PRIMARY);
254 ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
255 buildPerson());
256 mDataManager.onShortcutAddedOrUpdated(shortcut);
257
258 AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
259 .build();
260 AppTargetEvent appTargetEvent =
261 new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
262 .setLaunchLocation(ChooserActivity.LAUNCH_LOCATON_DIRECT_SHARE)
263 .build();
264 IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
265 mDataManager.reportAppTargetEvent(appTargetEvent, intentFilter);
266
267 List<Range<Long>> activeShareTimeSlots = new ArrayList<>();
268 mDataManager.forAllPackages(packageData ->
269 activeShareTimeSlots.addAll(
270 packageData.getEventHistory(TEST_SHORTCUT_ID)
271 .getEventIndex(Event.TYPE_SHARE_IMAGE)
272 .getActiveTimeSlots()));
273 assertEquals(1, activeShareTimeSlots.size());
274 }
275
276 @Test
277 public void testContactsChanged() {
278 mDataManager.onUserUnlocked(USER_ID_PRIMARY);
279
280 ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
281 buildPerson());
282 mDataManager.onShortcutAddedOrUpdated(shortcut);
283
284 final String newPhoneNumber = "+1000000000";
285 mInjector.mContactsQueryHelper.mIsStarred = true;
286 mInjector.mContactsQueryHelper.mPhoneNumber = newPhoneNumber;
287
288 ContentObserver contentObserver = mDataManager.getContactsContentObserverForTesting(
289 USER_ID_PRIMARY);
290 contentObserver.onChange(false, ContactsContract.Contacts.CONTENT_URI, USER_ID_PRIMARY);
291
292 List<ConversationInfo> conversations = new ArrayList<>();
293 mDataManager.forAllPackages(
294 packageData -> packageData.forAllConversations(conversations::add));
295 assertEquals(1, conversations.size());
296
297 assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId());
298 assertTrue(conversations.get(0).isContactStarred());
299 assertEquals(newPhoneNumber, conversations.get(0).getContactPhoneNumber());
300 }
301
302 @Test
303 public void testNotificationListener() {
304 mDataManager.onUserUnlocked(USER_ID_PRIMARY);
305
306 ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
307 buildPerson());
308 mDataManager.onShortcutAddedOrUpdated(shortcut);
309
310 NotificationListenerService listenerService =
311 mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
312
313 listenerService.onNotificationRemoved(mStatusBarNotification, null,
314 NotificationListenerService.REASON_CLICK);
315
316 List<Range<Long>> activeNotificationOpenTimeSlots = new ArrayList<>();
317 mDataManager.forAllPackages(packageData ->
318 activeNotificationOpenTimeSlots.addAll(
319 packageData.getEventHistory(TEST_SHORTCUT_ID)
320 .getEventIndex(Event.TYPE_NOTIFICATION_OPENED)
321 .getActiveTimeSlots()));
322 assertEquals(1, activeNotificationOpenTimeSlots.size());
323 }
324
325 @Test
326 public void testQueryUsageStatsService() {
327 UsageEvents.Event e = new UsageEvents.Event(SHORTCUT_INVOCATION,
328 System.currentTimeMillis());
329 e.mPackage = TEST_PKG_NAME;
330 e.mShortcutId = TEST_SHORTCUT_ID;
331 List<UsageEvents.Event> events = new ArrayList<>();
332 events.add(e);
333 UsageEvents usageEvents = new UsageEvents(events, new String[]{});
334 when(mUsageStatsManagerInternal.queryEventsForUser(anyInt(), anyLong(), anyLong(),
335 anyBoolean())).thenReturn(usageEvents);
336
337 mDataManager.onUserUnlocked(USER_ID_PRIMARY);
338
339 ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
340 buildPerson());
341 mDataManager.onShortcutAddedOrUpdated(shortcut);
342
343 mDataManager.queryUsageStatsService(USER_ID_PRIMARY, 0L, Long.MAX_VALUE);
344
345 List<Range<Long>> activeShortcutInvocationTimeSlots = new ArrayList<>();
346 mDataManager.forAllPackages(packageData ->
347 activeShortcutInvocationTimeSlots.addAll(
348 packageData.getEventHistory(TEST_SHORTCUT_ID)
349 .getEventIndex(Event.TYPE_SHORTCUT_INVOCATION)
350 .getActiveTimeSlots()));
351 assertEquals(1, activeShortcutInvocationTimeSlots.size());
352 }
353
354 private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
355 LocalServices.removeServiceForTest(clazz);
356 LocalServices.addService(clazz, mock);
357 }
358
359 private ShortcutInfo buildShortcutInfo(String packageName, int userId, String id,
360 @Nullable Person person) {
361 Context mockContext = mock(Context.class);
362 when(mockContext.getPackageName()).thenReturn(packageName);
363 when(mockContext.getUserId()).thenReturn(userId);
364 when(mockContext.getUser()).thenReturn(UserHandle.of(userId));
365 ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, id)
366 .setShortLabel(id)
367 .setIntent(new Intent("TestIntent"));
368 if (person != null) {
369 builder.setPersons(new Person[] {person});
370 }
371 return builder.build();
372 }
373
374 private Person buildPerson() {
375 return buildPerson(true, false);
376 }
377
378 private Person buildPerson(boolean isImportant, boolean isBot) {
379 return new Person.Builder()
380 .setImportant(isImportant)
381 .setBot(isBot)
382 .setUri(CONTACT_URI)
383 .build();
384 }
385
386 private UserInfo buildUserInfo(int userId) {
387 return new UserInfo(userId, "", 0);
388 }
389
390 private class TestContactsQueryHelper extends ContactsQueryHelper {
391
392 private Uri mContactUri;
393 private boolean mIsStarred;
394 private String mPhoneNumber;
395
396 TestContactsQueryHelper(Context context) {
397 super(context);
398 mContactUri = Uri.parse(CONTACT_URI);
399 mIsStarred = false;
400 mPhoneNumber = PHONE_NUMBER;
401 }
402
403 @Override
404 boolean query(@NonNull String contactUri) {
405 return true;
406 }
407
408 @Override
409 boolean querySince(long sinceTime) {
410 return true;
411 }
412
413 @Override
414 @Nullable
415 Uri getContactUri() {
416 return mContactUri;
417 }
418
419 @Override
420 boolean isStarred() {
421 return mIsStarred;
422 }
423
424 @Override
425 @Nullable
426 String getPhoneNumber() {
427 return mPhoneNumber;
428 }
429 }
430
431 private class TestInjector extends DataManager.Injector {
432
433 private final TestContactsQueryHelper mContactsQueryHelper =
434 new TestContactsQueryHelper(mContext);
435
436 @Override
437 ScheduledExecutorService createScheduledExecutor() {
438 return mExecutorService;
439 }
440
441 @Override
442 ContactsQueryHelper createContactsQueryHelper(Context context) {
443 return mContactsQueryHelper;
444 }
445
446 @Override
447 int getCallingUserId() {
448 return mCallingUserId;
449 }
450 }
451}