blob: 66c3f07304040843ad8eed1a92183670fc2a6e82 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.people.data;
import static android.app.people.ConversationStatus.ACTIVITY_ANNIVERSARY;
import static android.app.people.ConversationStatus.ACTIVITY_GAME;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_ADDED;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_DELETED;
import static android.service.notification.NotificationListenerService.NOTIFICATION_CHANNEL_OR_GROUP_UPDATED;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Person;
import android.app.job.JobScheduler;
import android.app.people.ConversationChannel;
import android.app.people.ConversationStatus;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.LauncherApps.ShortcutChangeCallback;
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutServiceInternal;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.CancellationSignal;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.test.TestLooper;
import android.provider.ContactsContract;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.telecom.TelecomManager;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
import android.util.Range;
import com.android.internal.app.ChooserActivity;
import com.android.internal.content.PackageMonitor;
import com.android.server.LocalServices;
import com.android.server.notification.NotificationManagerInternal;
import com.android.server.people.PeopleService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.google.common.collect.Iterables;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@RunWith(JUnit4.class)
public final class DataManagerTest {
private static final int USER_ID_PRIMARY = 0;
private static final int USER_ID_PRIMARY_MANAGED = 10;
private static final int USER_ID_SECONDARY = 11;
private static final String TEST_PKG_NAME = "pkg";
private static final String TEST_CLASS_NAME = "class";
private static final String TEST_SHORTCUT_ID = "sc";
private static final String TEST_SHORTCUT_ID_2 = "sc2";
private static final int TEST_PKG_UID = 35;
private static final String CONTACT_URI = "content://com.android.contacts/contacts/lookup/123";
private static final String PHONE_NUMBER = "+1234567890";
private static final String NOTIFICATION_CHANNEL_ID = "test : sc";
private static final String PARENT_NOTIFICATION_CHANNEL_ID = "test";
private static final long MILLIS_PER_MINUTE = 1000L * 60L;
private static final String GENERIC_KEY = "key";
private static final String CUSTOM_KEY = "custom";
@Mock
private Context mContext;
@Mock
private ShortcutServiceInternal mShortcutServiceInternal;
@Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
private NotificationManagerInternal mNotificationManagerInternal;
@Mock
private UserManager mUserManager;
@Mock
private PackageManager mPackageManager;
@Mock
private TelephonyManager mTelephonyManager;
@Mock
private TelecomManager mTelecomManager;
@Mock
private ContentResolver mContentResolver;
@Mock
private JobScheduler mJobScheduler;
@Mock
private StatusBarNotification mGenericSbn;
@Mock
private StatusBarNotification mConvoSbn;
@Mock
private NotificationListenerService.RankingMap mRankingMap;
@Mock
private Notification mNotification;
@Mock
private AlarmManager mAlarmManager;
@Captor
private ArgumentCaptor<ShortcutChangeCallback> mShortcutChangeCallbackCaptor;
@Captor
private ArgumentCaptor<BroadcastReceiver> mBroadcastReceiverCaptor;
@Captor
private ArgumentCaptor<Integer> mQueryFlagsCaptor;
private ScheduledExecutorService mExecutorService;
private NotificationChannel mNotificationChannel;
private NotificationChannel mParentNotificationChannel;
private DataManager mDataManager;
private CancellationSignal mCancellationSignal;
private ShortcutChangeCallback mShortcutChangeCallback;
private ShortcutInfo mShortcutInfo;
private TestInjector mInjector;
private TestLooper mLooper;
@Before
public void setUp() throws PackageManager.NameNotFoundException {
MockitoAnnotations.initMocks(this);
addLocalServiceMock(ShortcutServiceInternal.class, mShortcutServiceInternal);
addLocalServiceMock(UsageStatsManagerInternal.class, mUsageStatsManagerInternal);
addLocalServiceMock(PackageManagerInternal.class, mPackageManagerInternal);
AndroidPackage androidPackage = mock(AndroidPackage.class);
when(androidPackage.getPackageName()).thenReturn(TEST_PKG_NAME);
doAnswer(ans -> {
Consumer<AndroidPackage> callback = (Consumer<AndroidPackage>) ans.getArguments()[0];
callback.accept(androidPackage);
return null;
}).when(mPackageManagerInternal).forEachInstalledPackage(any(Consumer.class), anyInt());
addLocalServiceMock(NotificationManagerInternal.class, mNotificationManagerInternal);
mParentNotificationChannel = new NotificationChannel(
PARENT_NOTIFICATION_CHANNEL_ID, "test channel",
NotificationManager.IMPORTANCE_DEFAULT);
when(mContext.getContentResolver()).thenReturn(mContentResolver);
when(mContext.getMainLooper()).thenReturn(Looper.getMainLooper());
when(mContext.getPackageName()).thenReturn("android");
when(mContext.getPackageManager()).thenReturn(mPackageManager);
Context originalContext = getInstrumentation().getTargetContext();
when(mContext.getApplicationInfo()).thenReturn(originalContext.getApplicationInfo());
when(mContext.getUser()).thenReturn(originalContext.getUser());
when(mContext.getPackageName()).thenReturn(originalContext.getPackageName());
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
when(mContext.getSystemServiceName(UserManager.class)).thenReturn(
Context.USER_SERVICE);
when(mContext.getSystemService(Context.ALARM_SERVICE)).thenReturn(mAlarmManager);
when(mContext.getSystemServiceName(AlarmManager.class)).thenReturn(
Context.ALARM_SERVICE);
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTelephonyManager);
when(mContext.getSystemService(Context.TELECOM_SERVICE)).thenReturn(mTelecomManager);
when(mContext.getSystemServiceName(TelecomManager.class)).thenReturn(
Context.TELECOM_SERVICE);
when(mTelecomManager.getDefaultDialerPackage(any(UserHandle.class)))
.thenReturn(TEST_PKG_NAME);
when(mContext.getSystemService(Context.JOB_SCHEDULER_SERVICE)).thenReturn(mJobScheduler);
when(mContext.getSystemServiceName(JobScheduler.class)).thenReturn(
Context.JOB_SCHEDULER_SERVICE);
mExecutorService = new MockScheduledExecutorService();
when(mUserManager.getEnabledProfiles(USER_ID_PRIMARY))
.thenReturn(Arrays.asList(
buildUserInfo(USER_ID_PRIMARY),
buildUserInfo(USER_ID_PRIMARY_MANAGED)));
when(mUserManager.getEnabledProfiles(USER_ID_SECONDARY))
.thenReturn(Collections.singletonList(buildUserInfo(USER_ID_SECONDARY)));
when(mPackageManager.getPackageUidAsUser(TEST_PKG_NAME, USER_ID_PRIMARY))
.thenReturn(TEST_PKG_UID);
mNotificationChannel = new NotificationChannel(
NOTIFICATION_CHANNEL_ID, "test channel", NotificationManager.IMPORTANCE_DEFAULT);
mNotificationChannel.setConversationId(PARENT_NOTIFICATION_CHANNEL_ID, TEST_SHORTCUT_ID);
when(mNotificationManagerInternal.getNotificationChannel(anyString(), anyInt(),
eq(mNotificationChannel.getId()))).thenReturn(mNotificationChannel);
when(mNotificationManagerInternal.getNotificationChannel(anyString(), anyInt(),
eq(mParentNotificationChannel.getId()))).thenReturn(mParentNotificationChannel);
when(mGenericSbn.getKey()).thenReturn(GENERIC_KEY);
when(mGenericSbn.getNotification()).thenReturn(mNotification);
when(mGenericSbn.getPackageName()).thenReturn(TEST_PKG_NAME);
when(mGenericSbn.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
when(mGenericSbn.getPostTime()).thenReturn(System.currentTimeMillis());
when(mConvoSbn.getKey()).thenReturn(CUSTOM_KEY);
when(mConvoSbn.getNotification()).thenReturn(mNotification);
when(mConvoSbn.getPackageName()).thenReturn(TEST_PKG_NAME);
when(mConvoSbn.getUser()).thenReturn(UserHandle.of(USER_ID_PRIMARY));
when(mConvoSbn.getPostTime()).thenReturn(System.currentTimeMillis());
when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
mCancellationSignal = new CancellationSignal();
mInjector = new TestInjector();
mLooper = new TestLooper();
mDataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
mDataManager.initialize();
when(mShortcutServiceInternal.isSharingShortcut(anyInt(), anyString(), anyString(),
anyString(), anyInt(), any())).thenReturn(true);
mShortcutInfo = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
when(mShortcutServiceInternal.getShortcuts(
anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
anyInt(), anyInt(), anyInt(), anyInt()))
.thenReturn(Collections.singletonList(mShortcutInfo));
verify(mShortcutServiceInternal).addShortcutChangeCallback(
mShortcutChangeCallbackCaptor.capture());
mShortcutChangeCallback = mShortcutChangeCallbackCaptor.getValue();
verify(mContext, times(1)).registerReceiver(any(), any());
verify(mContext, times(1)).registerReceiver(any(), any(), anyInt());
}
@After
public void tearDown() {
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
}
@Test
public void testAccessConversationFromTheSameProfileGroup() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_PRIMARY_MANAGED);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1",
buildPerson(true, false)));
mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2",
buildPerson(false, true)));
mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_3", USER_ID_SECONDARY, "sc_3", buildPerson()));
List<ConversationInfo> conversations = getConversationsInPrimary();
// USER_ID_SECONDARY is not in the same profile group as USER_ID_PRIMARY.
assertEquals(2, conversations.size());
assertEquals("sc_1", conversations.get(0).getShortcutId());
assertTrue(conversations.get(0).isPersonImportant());
assertFalse(conversations.get(0).isPersonBot());
assertFalse(conversations.get(0).isContactStarred());
assertEquals(PHONE_NUMBER, conversations.get(0).getContactPhoneNumber());
assertEquals("sc_2", conversations.get(1).getShortcutId());
assertFalse(conversations.get(1).isPersonImportant());
assertTrue(conversations.get(1).isPersonBot());
assertFalse(conversations.get(0).isContactStarred());
assertEquals(PHONE_NUMBER, conversations.get(0).getContactPhoneNumber());
}
@Test
public void testAccessConversationForUnlockedUsersOnly() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_1", USER_ID_PRIMARY, "sc_1", buildPerson()));
mDataManager.addOrUpdateConversationInfo(
buildShortcutInfo("pkg_2", USER_ID_PRIMARY_MANAGED, "sc_2", buildPerson()));
List<ConversationInfo> conversations = getConversationsInPrimary();
// USER_ID_PRIMARY_MANAGED is not locked, so only USER_ID_PRIMARY's conversation is stored.
assertEquals(1, conversations.size());
assertEquals("sc_1", conversations.get(0).getShortcutId());
mDataManager.onUserStopping(USER_ID_PRIMARY);
conversations = getConversationsInPrimary();
assertTrue(conversations.isEmpty());
}
@Test
public void testGetShortcut() {
mDataManager.getShortcut(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID);
verify(mShortcutServiceInternal).getShortcuts(anyInt(), anyString(), anyLong(),
eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)),
eq(null), eq(null), anyInt(), eq(USER_ID_PRIMARY), anyInt(), anyInt());
}
@Test
public void testReportAppTargetEvent_directSharing()
throws IntentFilter.MalformedMimeTypeException {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
.build();
AppTargetEvent appTargetEvent =
new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
.build();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
Event.SHARE_EVENT_TYPES);
assertEquals(1, activeShareTimeSlots.size());
}
@Test
public void testReportAppTargetEvent_directSharing_createConversation()
throws IntentFilter.MalformedMimeTypeException {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
null);
AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
.build();
AppTargetEvent appTargetEvent =
new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
.build();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
List<Range<Long>> activeShareTimeSlots = getActiveSlotsForTestShortcut(
Event.SHARE_EVENT_TYPES);
assertEquals(1, activeShareTimeSlots.size());
ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
.getConversationStore()
.getConversation(TEST_SHORTCUT_ID);
assertNotNull(conversationInfo);
assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
}
@Test
public void testReportAppTargetEvent_appSharing()
throws IntentFilter.MalformedMimeTypeException {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
AppTarget appTarget = new AppTarget.Builder(
new AppTargetId(TEST_SHORTCUT_ID),
TEST_PKG_NAME,
UserHandle.of(USER_ID_PRIMARY))
.setClassName(TEST_CLASS_NAME)
.build();
AppTargetEvent appTargetEvent =
new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
.build();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
List<Range<Long>> activeShareTimeSlots = getActiveSlotsForAppShares();
assertEquals(1, activeShareTimeSlots.size());
}
@Test
public void testContactsChanged() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
final String newPhoneNumber = "+1000000000";
mInjector.mContactsQueryHelper.mIsStarred = true;
mInjector.mContactsQueryHelper.mPhoneNumber = newPhoneNumber;
ContentObserver contentObserver = mDataManager.getContactsContentObserverForTesting(
USER_ID_PRIMARY);
contentObserver.onChange(false, ContactsContract.Contacts.CONTENT_URI, USER_ID_PRIMARY);
List<ConversationInfo> conversations = getConversationsInPrimary();
assertEquals(1, conversations.size());
assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId());
assertTrue(conversations.get(0).isContactStarred());
assertEquals(newPhoneNumber, conversations.get(0).getContactPhoneNumber());
}
@Test
public void testNotificationPosted() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
sendGenericNotification();
List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
Event.NOTIFICATION_EVENT_TYPES);
assertEquals(1, activeNotificationOpenTimeSlots.size());
}
@Test
public void testNotificationOpened() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationRemoved(mGenericSbn, null,
NotificationListenerService.REASON_CLICK);
List<Range<Long>> activeNotificationOpenTimeSlots = getActiveSlotsForTestShortcut(
Event.NOTIFICATION_EVENT_TYPES);
assertEquals(1, activeNotificationOpenTimeSlots.size());
}
@Test
public void testUncacheShortcutsWhenNotificationsDismissed() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
// The cached conversations are above the limit because every conversation has active
// notifications. To uncache one of them, the notifications for that conversation need to
// be dismissed.
for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
String shortcutId = TEST_SHORTCUT_ID + i;
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
when(mNotification.getShortcutId()).thenReturn(shortcutId);
sendGenericNotification();
}
// Post another notification for the last conversation.
sendGenericNotification();
// Removing one of the two notifications does not un-cache the shortcut.
listenerService.onNotificationRemoved(mGenericSbn, null,
NotificationListenerService.REASON_CANCEL);
verify(mShortcutServiceInternal, never()).uncacheShortcuts(
anyInt(), any(), anyString(), any(), anyInt(), anyInt());
// Removing the second notification un-caches the shortcut.
listenerService.onNotificationRemoved(mGenericSbn, null,
NotificationListenerService.REASON_CANCEL_ALL);
verify(mShortcutServiceInternal).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME), anyList(), eq(USER_ID_PRIMARY),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
@Test
public void testConversationIsNotRecentIfCustomized() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
sendGenericNotification();
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
assertEquals(1, mDataManager.getRecentConversations(USER_ID_PRIMARY).size());
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
assertTrue(mDataManager.getRecentConversations(USER_ID_PRIMARY).isEmpty());
}
@Test
public void testAddConversationsListener() throws Exception {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
ConversationChannel conversationChannel = mDataManager.getConversation(TEST_PKG_NAME,
USER_ID_PRIMARY,
TEST_SHORTCUT_ID);
PeopleService.ConversationsListener listener = mock(
PeopleService.ConversationsListener.class);
mDataManager.addConversationsListener(listener);
List<ConversationChannel> changedConversations = Arrays.asList(conversationChannel);
verify(listener, times(0)).onConversationsUpdate(eq(changedConversations));
mDataManager.notifyConversationsListeners(changedConversations);
mLooper.dispatchAll();
verify(listener, times(1)).onConversationsUpdate(eq(changedConversations));
}
@Test
public void testAddConversationListenersNotifiesMultipleConversations() throws Exception {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
ConversationChannel conversationChannel = mDataManager.getConversation(TEST_PKG_NAME,
USER_ID_PRIMARY,
TEST_SHORTCUT_ID);
ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID_2,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut2);
mLooper.dispatchAll();
ConversationChannel conversationChannel2 = mDataManager.getConversation(TEST_PKG_NAME,
USER_ID_PRIMARY,
TEST_SHORTCUT_ID_2);
PeopleService.ConversationsListener listener = mock(
PeopleService.ConversationsListener.class);
mDataManager.addConversationsListener(listener);
List<ConversationChannel> changedConversations = Arrays.asList(conversationChannel,
conversationChannel2);
verify(listener, times(0)).onConversationsUpdate(eq(changedConversations));
mDataManager.notifyConversationsListeners(changedConversations);
mLooper.dispatchAll();
verify(listener, times(1)).onConversationsUpdate(eq(changedConversations));
ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
List.class);
verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
assertThat(capturedConversation.getValue()).containsExactly(conversationChannel,
conversationChannel2);
}
@Test
public void testAddOrUpdateStatusNotifiesConversationsListeners() throws Exception {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
mLooper.dispatchAll();
PeopleService.ConversationsListener listener = mock(
PeopleService.ConversationsListener.class);
mDataManager.addConversationsListener(listener);
ConversationStatus status = new ConversationStatus.Builder("cs1", ACTIVITY_GAME).build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, status);
mLooper.dispatchAll();
ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
List.class);
verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
assertThat(result.getStatuses()).containsExactly(status);
assertEquals(result.getShortcutInfo().getId(), TEST_SHORTCUT_ID);
}
@Test
public void testGetConversationReturnsCustomizedConversation() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
sendConvoNotification();
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isNotNull();
ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID);
assertThat(result).isNotNull();
assertThat(result.hasBirthdayToday()).isFalse();
assertThat(result.getStatuses()).isEmpty();
}
@Test
public void testGetConversation() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isNull();
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_PINNED);
mDataManager.addOrUpdateConversationInfo(shortcut);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isNotNull();
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID + "1")).isNull();
sendConvoNotification();
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID);
assertThat(result).isNotNull();
assertEquals(shortcut.getId(), result.getShortcutInfo().getId());
assertEquals(1, result.getShortcutInfo().getPersons().length);
assertEquals(CONTACT_URI, result.getShortcutInfo().getPersons()[0].getUri());
assertEquals(mNotificationChannel.getId(), result.getNotificationChannel().getId());
assertEquals(mParentNotificationChannel.getId(),
result.getNotificationChannel().getParentChannelId());
assertEquals(mConvoSbn.getPostTime(), result.getLastEventTimestamp());
assertTrue(result.hasActiveNotifications());
assertFalse(result.hasBirthdayToday());
assertThat(result.getStatuses()).containsExactly(cs);
}
@Test
public void testOnNotificationChannelModified() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isNull();
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_PINNED);
mDataManager.addOrUpdateConversationInfo(shortcut);
sendConvoNotification();
ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID);
assertFalse(result.getNotificationChannel().canBubble());
NotificationChannel updated = new NotificationChannel(mNotificationChannel.getId(),
mNotificationChannel.getDescription(), mNotificationChannel.getImportance());
updated.setConversationId(mNotificationChannel.getParentChannelId(),
mNotificationChannel.getConversationId());
updated.setAllowBubbles(true);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
updated, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
ConversationInfo ci = mDataManager.getConversationInfo(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID);
assertThat(ci).isNotNull();
assertEquals(mNotificationChannel.getId(), ci.getNotificationChannelId());
assertEquals(mParentNotificationChannel.getId(), ci.getParentNotificationChannelId());
assertTrue(ci.isBubbled());
}
@Test
public void testGetConversation_demoted() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isNull();
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_PINNED);
mDataManager.addOrUpdateConversationInfo(shortcut);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isNotNull();
mNotificationChannel.setDemoted(true);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isNull();
}
@Test
public void testGetConversationGetsPersonsData() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_PINNED);
mDataManager.addOrUpdateConversationInfo(shortcut);
sendGenericNotification();
mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID);
verify(mShortcutServiceInternal).getShortcuts(
anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
mQueryFlagsCaptor.capture(), anyInt(), anyInt(), anyInt());
Integer queryFlags = mQueryFlagsCaptor.getValue();
assertThat(hasFlag(queryFlags, ShortcutQuery.FLAG_GET_PERSONS_DATA)).isTrue();
}
@Test
public void testIsConversation() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isFalse();
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_PINNED);
mDataManager.addOrUpdateConversationInfo(shortcut);
assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isTrue();
assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID + "1")).isFalse();
}
@Test
public void testIsConversation_demoted() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isFalse();
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_PINNED);
mDataManager.addOrUpdateConversationInfo(shortcut);
mNotificationChannel.setDemoted(true);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
assertThat(mDataManager.isConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
TEST_SHORTCUT_ID)).isFalse();
}
@Test
public void testNotificationChannelCreated() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
.getConversationStore()
.getConversation(TEST_SHORTCUT_ID);
assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
assertFalse(conversationInfo.isImportant());
assertFalse(conversationInfo.isNotificationSilenced());
assertFalse(conversationInfo.isDemoted());
}
@Test
public void testNotificationChannelModified() {
mNotificationChannel.setImportantConversation(true);
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
.getConversationStore()
.getConversation(TEST_SHORTCUT_ID);
assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
assertTrue(conversationInfo.isImportant());
assertFalse(conversationInfo.isNotificationSilenced());
assertFalse(conversationInfo.isDemoted());
}
@Test
public void testNotificationChannelDeleted() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_ADDED);
ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
.getConversationStore()
.getConversation(TEST_SHORTCUT_ID);
assertEquals(NOTIFICATION_CHANNEL_ID, conversationInfo.getNotificationChannelId());
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_DELETED);
conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
.getConversationStore()
.getConversation(TEST_SHORTCUT_ID);
assertNull(conversationInfo.getNotificationChannelId());
assertFalse(conversationInfo.isImportant());
assertFalse(conversationInfo.isNotificationSilenced());
assertFalse(conversationInfo.isDemoted());
}
@Test
public void testShortcutAddedOrUpdated() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
PeopleService.ConversationsListener listener = mock(
PeopleService.ConversationsListener.class);
mDataManager.addConversationsListener(listener);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
Collections.singletonList(shortcut), UserHandle.of(USER_ID_PRIMARY));
mLooper.dispatchAll();
List<ConversationInfo> conversations = getConversationsInPrimary();
assertEquals(1, conversations.size());
assertEquals(TEST_SHORTCUT_ID, conversations.get(0).getShortcutId());
ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
List.class);
verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
assertEquals(result.getShortcutInfo().getId(), TEST_SHORTCUT_ID);
}
@Test
public void testShortcutsDeleted() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut1 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc1",
buildPerson());
ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc2",
buildPerson());
ShortcutInfo shortcut3 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "sc3",
buildPerson());
mShortcutChangeCallback.onShortcutsAddedOrUpdated(TEST_PKG_NAME,
Arrays.asList(shortcut1, shortcut2, shortcut3), UserHandle.of(USER_ID_PRIMARY));
mShortcutChangeCallback.onShortcutsRemoved(TEST_PKG_NAME,
List.of(shortcut1, shortcut3), UserHandle.of(USER_ID_PRIMARY));
List<ConversationInfo> conversations = getConversationsInPrimary();
assertEquals(1, conversations.size());
assertEquals("sc2", conversations.get(0).getShortcutId());
verify(mNotificationManagerInternal)
.onConversationRemoved(TEST_PKG_NAME, TEST_PKG_UID, Set.of("sc1", "sc3"));
}
@Test
public void testCallLogContentObserver() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
ContentObserver contentObserver = mDataManager.getCallLogContentObserverForTesting();
contentObserver.onChange(false);
long currentTimestamp = System.currentTimeMillis();
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE * 15L, Event.TYPE_CALL_OUTGOING));
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE * 10L, Event.TYPE_CALL_INCOMING));
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE * 5L, Event.TYPE_CALL_MISSED));
List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
assertEquals(3, activeTimeSlots.size());
}
@Test
public void testMmsSmsContentObserver() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
ContentObserver contentObserver = mDataManager.getMmsSmsContentObserverForTesting();
contentObserver.onChange(false);
long currentTimestamp = System.currentTimeMillis();
Event outgoingSmsEvent =
new Event(currentTimestamp - MILLIS_PER_MINUTE * 10L, Event.TYPE_SMS_OUTGOING);
Event incomingSmsEvent =
new Event(currentTimestamp - MILLIS_PER_MINUTE * 5L, Event.TYPE_SMS_INCOMING);
mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, outgoingSmsEvent);
mInjector.mSmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER, incomingSmsEvent);
List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
assertEquals(2, activeTimeSlots.size());
}
@Test
public void testConversationLastEventTimestampUpdate() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
PackageData packageData = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY);
ConversationInfo conversationInfo =
packageData.getConversationStore().getConversation(TEST_SHORTCUT_ID);
Event event = new Event(123L, Event.TYPE_IN_APP_CONVERSATION);
mInjector.mUsageStatsQueryHelper.mEventListener.onEvent(packageData, conversationInfo,
event);
ConversationInfo newConversationInfo =
packageData.getConversationStore().getConversation(TEST_SHORTCUT_ID);
assertEquals(123L, newConversationInfo.getLastEventTimestamp());
}
@Test
public void testDeleteUninstalledPackageDataOnPackageRemoved() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
PackageMonitor packageMonitor = mDataManager.getPackageMonitorForTesting(USER_ID_PRIMARY);
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
intent.putExtra(Intent.EXTRA_USER_HANDLE, USER_ID_PRIMARY);
intent.setData(Uri.parse("package:" + TEST_PKG_NAME));
packageMonitor.onReceive(mContext, intent);
assertNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
}
@Test
public void testPruneUninstalledPackageData() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
assertNotNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
doAnswer(ans -> null).when(mPackageManagerInternal)
.forEachInstalledPackage(any(Consumer.class), anyInt());
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
assertNull(mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY));
}
@Test
public void testPruneCallEventsFromNonDialer() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
long currentTimestamp = System.currentTimeMillis();
mInjector.mCallLogQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_CALL_OUTGOING));
List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
assertEquals(1, activeTimeSlots.size());
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultDialer(null);
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
activeTimeSlots = getActiveSlotsForTestShortcut(Event.CALL_EVENT_TYPES);
assertTrue(activeTimeSlots.isEmpty());
}
@Test
public void testPruneSmsEventsFromNonDefaultSmsApp() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(TEST_PKG_NAME);
long currentTimestamp = System.currentTimeMillis();
mInjector.mMmsQueryHelper.mEventConsumer.accept(PHONE_NUMBER,
new Event(currentTimestamp - MILLIS_PER_MINUTE, Event.TYPE_SMS_OUTGOING));
List<Range<Long>> activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
assertEquals(1, activeTimeSlots.size());
mDataManager.getUserDataForTesting(USER_ID_PRIMARY).setDefaultSmsApp(null);
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
activeTimeSlots = getActiveSlotsForTestShortcut(Event.SMS_EVENT_TYPES);
assertTrue(activeTimeSlots.isEmpty());
}
@Test
public void testPruneExpiredConversationStatuses() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
ConversationStatus cs1 = new ConversationStatus.Builder("cs1", 9)
.setEndTimeMillis(System.currentTimeMillis())
.build();
ConversationStatus cs2 = new ConversationStatus.Builder("cs2", 10)
.build();
ConversationStatus cs3 = new ConversationStatus.Builder("cs3", 1)
.setEndTimeMillis(Long.MAX_VALUE)
.build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs1);
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs3);
mLooper.dispatchAll();
PeopleService.ConversationsListener listener = mock(
PeopleService.ConversationsListener.class);
mDataManager.addConversationsListener(listener);
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
mLooper.dispatchAll();
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.doesNotContain(cs1);
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.contains(cs2);
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.contains(cs3);
ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
List.class);
verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
List<ConversationChannel> results = capturedConversation.getValue();
ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
// CHeck cs1 has been removed and only cs2 and cs3 remain.
assertThat(result.getStatuses()).containsExactly(cs2, cs3);
}
@Test
public void testDoNotUncacheShortcutWithActiveNotifications() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
String shortcutId = TEST_SHORTCUT_ID + i;
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
when(mNotification.getShortcutId()).thenReturn(shortcutId);
sendGenericNotification();
}
mDataManager.pruneDataForUser(USER_ID_PRIMARY, mCancellationSignal);
verify(mShortcutServiceInternal, never()).uncacheShortcuts(
anyInt(), anyString(), anyString(), anyList(), anyInt(), anyInt());
}
@Test
public void testUncacheOldestCachedShortcut() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
String shortcutId = TEST_SHORTCUT_ID + i;
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
when(mNotification.getShortcutId()).thenReturn(shortcutId);
when(mGenericSbn.getPostTime()).thenReturn(100L + i);
sendGenericNotification();
listenerService.onNotificationRemoved(mGenericSbn, null,
NotificationListenerService.REASON_CANCEL);
}
// Only the shortcut #0 is uncached, all the others are not.
verify(mShortcutServiceInternal).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME),
eq(Collections.singletonList(TEST_SHORTCUT_ID + 0)), eq(USER_ID_PRIMARY),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
for (int i = 1; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
verify(mShortcutServiceInternal, never()).uncacheShortcuts(
anyInt(), anyString(), anyString(),
eq(Collections.singletonList(TEST_SHORTCUT_ID + i)), anyInt(),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
}
@Test
public void testUncacheOldestCachedShortcut_missingNotificationEvents() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
for (int i = 0; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
String shortcutId = TEST_SHORTCUT_ID + i;
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mShortcutChangeCallback.onShortcutsAddedOrUpdated(
TEST_PKG_NAME,
Collections.singletonList(shortcut),
UserHandle.of(USER_ID_PRIMARY));
mLooper.dispatchAll();
}
// Only the shortcut #0 is uncached, all the others are not.
verify(mShortcutServiceInternal).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME),
eq(Collections.singletonList(TEST_SHORTCUT_ID + 0)), eq(USER_ID_PRIMARY),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
for (int i = 1; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
verify(mShortcutServiceInternal, never()).uncacheShortcuts(
anyInt(), anyString(), anyString(),
eq(Collections.singletonList(TEST_SHORTCUT_ID + i)), anyInt(),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
}
@Test
public void testUncacheOldestCachedShortcut_legacyConversation() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
// Add an extra conversation with a legacy type (no creationTime)
ConversationStore conversationStore = mDataManager
.getUserDataForTesting(USER_ID_PRIMARY)
.getOrCreatePackageData(TEST_PKG_NAME)
.getConversationStore();
ConversationInfo.Builder builder = new ConversationInfo.Builder();
builder.setShortcutId(TEST_SHORTCUT_ID + 0);
builder.setShortcutFlags(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.updateConversationStoreThenNotifyListeners(
conversationStore,
builder.build(),
TEST_PKG_NAME, USER_ID_PRIMARY);
for (int i = 1; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
String shortcutId = TEST_SHORTCUT_ID + i;
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, shortcutId,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mShortcutChangeCallback.onShortcutsAddedOrUpdated(
TEST_PKG_NAME,
Collections.singletonList(shortcut),
UserHandle.of(USER_ID_PRIMARY));
mLooper.dispatchAll();
}
// Only the shortcut #0 is uncached, all the others are not.
verify(mShortcutServiceInternal).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME),
eq(Collections.singletonList(TEST_SHORTCUT_ID + 0)), eq(USER_ID_PRIMARY),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
for (int i = 1; i < DataManager.MAX_CACHED_RECENT_SHORTCUTS + 1; i++) {
verify(mShortcutServiceInternal, never()).uncacheShortcuts(
anyInt(), anyString(), anyString(),
eq(Collections.singletonList(TEST_SHORTCUT_ID + i)), anyInt(),
eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
}
@Test
public void testBackupAndRestoration()
throws IntentFilter.MalformedMimeTypeException {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
null);
AppTarget appTarget = new AppTarget.Builder(new AppTargetId(TEST_SHORTCUT_ID), shortcut)
.build();
AppTargetEvent appTargetEvent =
new AppTargetEvent.Builder(appTarget, AppTargetEvent.ACTION_LAUNCH)
.setLaunchLocation(ChooserActivity.LAUNCH_LOCATION_DIRECT_SHARE)
.build();
IntentFilter intentFilter = new IntentFilter(Intent.ACTION_SEND, "image/jpg");
mDataManager.reportShareTargetEvent(appTargetEvent, intentFilter);
byte[] payload = mDataManager.getBackupPayload(USER_ID_PRIMARY);
DataManager dataManager = new DataManager(mContext, mInjector, mLooper.getLooper());
dataManager.onUserUnlocked(USER_ID_PRIMARY);
dataManager.restore(USER_ID_PRIMARY, payload);
ConversationInfo conversationInfo = dataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
.getConversationStore()
.getConversation(TEST_SHORTCUT_ID);
assertNotNull(conversationInfo);
assertEquals(conversationInfo.getShortcutId(), TEST_SHORTCUT_ID);
}
@Test
public void testGetRecentConversations() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
sendGenericNotification();
List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
assertEquals(1, result.size());
assertEquals(shortcut.getId(), result.get(0).getShortcutInfo().getId());
assertEquals(1, result.get(0).getShortcutInfo().getPersons().length);
assertEquals(CONTACT_URI, result.get(0).getShortcutInfo().getPersons()[0].getUri());
assertEquals(mParentNotificationChannel.getId(),
result.get(0).getNotificationChannel().getId());
assertEquals(null, result.get(0).getNotificationChannel().getParentChannelId());
assertEquals(mGenericSbn.getPostTime(), result.get(0).getLastEventTimestamp());
assertTrue(result.get(0).hasActiveNotifications());
}
@Test
public void testGetRecentConversationsGetsPersonsData() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
sendGenericNotification();
mDataManager.getRecentConversations(USER_ID_PRIMARY);
verify(mShortcutServiceInternal).getShortcuts(
anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
mQueryFlagsCaptor.capture(), anyInt(), anyInt(), anyInt());
Integer queryFlags = mQueryFlagsCaptor.getValue();
assertThat(hasFlag(queryFlags, ShortcutQuery.FLAG_GET_PERSONS_DATA)).isTrue();
}
@Test
public void testPruneOldRecentConversations() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
when(mNotification.getShortcutId()).thenReturn(TEST_SHORTCUT_ID);
sendGenericNotification();
listenerService.onNotificationRemoved(mGenericSbn, null,
NotificationListenerService.REASON_CLICK);
mDataManager.pruneOldRecentConversations(USER_ID_PRIMARY,
System.currentTimeMillis() + (10 * DateUtils.DAY_IN_MILLIS) + 1);
verify(mShortcutServiceInternal).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)),
eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
@Test
public void testGetLastInteraction() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
sendGenericNotification();
assertEquals(mGenericSbn.getPostTime(),
mDataManager.getLastInteraction(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID));
assertEquals(0L,
mDataManager.getLastInteraction("not_test_pkg", USER_ID_PRIMARY, TEST_SHORTCUT_ID));
assertEquals(0L,
mDataManager.getLastInteraction(TEST_PKG_NAME, USER_ID_PRIMARY_MANAGED,
TEST_SHORTCUT_ID));
assertEquals(0L,
mDataManager.getLastInteraction(TEST_PKG_NAME, USER_ID_SECONDARY,
TEST_SHORTCUT_ID));
}
@Test
public void testAddOrUpdateStatus_noCachedShortcut() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
try {
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
fail("Updated a conversation info that didn't previously exist");
} catch (IllegalArgumentException e) {
// good
}
}
@Test
public void testAddOrUpdateStatus() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.contains(cs);
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.contains(cs);
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.contains(cs2);
verify(mAlarmManager, never()).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
}
@Test
public void testAddOrUpdateStatus_schedulesJob() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY)
.setEndTimeMillis(1000)
.build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME)
.setEndTimeMillis(3000)
.build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
verify(mAlarmManager, times(2)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
}
@Test
public void testClearStatus() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
mLooper.dispatchAll();
PeopleService.ConversationsListener listener = mock(
PeopleService.ConversationsListener.class);
mDataManager.addConversationsListener(listener);
mDataManager.clearStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2.getId());
mLooper.dispatchAll();
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.contains(cs);
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.doesNotContain(cs2);
ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
List.class);
verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
assertThat(result.getStatuses()).containsExactly(cs);
}
@Test
public void testClearStatuses() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
ConversationStatus cs = new ConversationStatus.Builder("id", ACTIVITY_ANNIVERSARY).build();
ConversationStatus cs2 = new ConversationStatus.Builder("id2", ACTIVITY_GAME).build();
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs);
mDataManager.addOrUpdateStatus(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID, cs2);
mLooper.dispatchAll();
PeopleService.ConversationsListener listener = mock(
PeopleService.ConversationsListener.class);
mDataManager.addConversationsListener(listener);
mDataManager.clearStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID);
mLooper.dispatchAll();
assertThat(mDataManager.getStatuses(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID))
.isEmpty();
ArgumentCaptor<List<ConversationChannel>> capturedConversation = ArgumentCaptor.forClass(
List.class);
verify(listener, times(1)).onConversationsUpdate(capturedConversation.capture());
ConversationChannel result = Iterables.getOnlyElement(capturedConversation.getValue());
assertThat(result.getStatuses()).isEmpty();
}
@Test
public void testNonCachedShortcutNotInRecentList() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY_MANAGED,
TEST_SHORTCUT_ID, buildPerson());
mDataManager.addOrUpdateConversationInfo(shortcut);
sendGenericNotification();
List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
assertTrue(result.isEmpty());
}
@Test
public void testCustomizedConversationNotInRecentList() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
// Post a notification and customize the notification settings.
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
sendGenericNotification();
listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
List<ConversationChannel> result = mDataManager.getRecentConversations(USER_ID_PRIMARY);
assertTrue(result.isEmpty());
}
@Test
public void testNotificationRemoved() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
sendGenericNotification();
// posting updates the last interaction time, so delay before deletion
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
long approxDeletionTime = System.currentTimeMillis();
listenerService.onNotificationRemoved(mGenericSbn, null,
NotificationListenerService.REASON_CANCEL);
ConversationInfo conversationInfo = mDataManager.getPackage(TEST_PKG_NAME, USER_ID_PRIMARY)
.getConversationStore()
.getConversation(TEST_SHORTCUT_ID);
assertTrue(conversationInfo.getLastEventTimestamp() - approxDeletionTime < 100);
}
@Test
public void testRemoveRecentConversation() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
buildPerson());
shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
sendGenericNotification();
listenerService.onNotificationRemoved(mGenericSbn, null,
NotificationListenerService.REASON_CANCEL);
mDataManager.removeRecentConversation(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
USER_ID_PRIMARY);
verify(mShortcutServiceInternal).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList(TEST_SHORTCUT_ID)),
eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
@Test
public void testRemoveAllRecentConversations() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
ShortcutInfo shortcut1 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "1",
buildPerson());
shortcut1.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut1);
ShortcutInfo shortcut2 = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, "2",
buildPerson());
shortcut2.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
mDataManager.addOrUpdateConversationInfo(shortcut2);
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
// Post a notification and then dismiss it for conversation #1.
when(mNotification.getShortcutId()).thenReturn("1");
sendGenericNotification();
listenerService.onNotificationRemoved(mGenericSbn, null,
NotificationListenerService.REASON_CANCEL);
// Post a notification for conversation #2, but don't dismiss it. Its shortcut won't be
// uncached when removeAllRecentConversations() is called.
when(mNotification.getShortcutId()).thenReturn("2");
sendGenericNotification();
mDataManager.removeAllRecentConversations(USER_ID_PRIMARY);
verify(mShortcutServiceInternal).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList("1")),
eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
verify(mShortcutServiceInternal, never()).uncacheShortcuts(
anyInt(), any(), eq(TEST_PKG_NAME), eq(Collections.singletonList("2")),
eq(USER_ID_PRIMARY), eq(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS));
}
private static <T> void addLocalServiceMock(Class<T> clazz, T mock) {
LocalServices.removeServiceForTest(clazz);
LocalServices.addService(clazz, mock);
}
private List<ConversationInfo> getConversationsInPrimary() {
List<ConversationInfo> conversations = new ArrayList<>();
mDataManager.forPackagesInProfile(USER_ID_PRIMARY,
packageData -> packageData.forAllConversations(conversations::add));
return conversations;
}
private List<Range<Long>> getActiveSlotsForTestShortcut(Set<Integer> eventTypes) {
List<Range<Long>> activeSlots = new ArrayList<>();
mDataManager.forPackagesInProfile(USER_ID_PRIMARY, packageData ->
activeSlots.addAll(
packageData.getEventHistory(TEST_SHORTCUT_ID)
.getEventIndex(eventTypes)
.getActiveTimeSlots()));
return activeSlots;
}
private List<Range<Long>> getActiveSlotsForAppShares() {
List<Range<Long>> activeSlots = new ArrayList<>();
mDataManager.forPackagesInProfile(USER_ID_PRIMARY, packageData ->
activeSlots.addAll(
packageData.getClassLevelEventHistory(TEST_CLASS_NAME)
.getEventIndex(Event.SHARE_EVENT_TYPES)
.getActiveTimeSlots()));
return activeSlots;
}
private ShortcutInfo buildShortcutInfo(String packageName, int userId, String id,
@Nullable Person person) {
Context mockContext = mock(Context.class);
when(mockContext.getPackageName()).thenReturn(packageName);
when(mockContext.getUserId()).thenReturn(userId);
when(mockContext.getUser()).thenReturn(UserHandle.of(userId));
ShortcutInfo.Builder builder = new ShortcutInfo.Builder(mockContext, id)
.setShortLabel(id)
.setLongLived(true)
.setIntent(new Intent("TestIntent"));
if (person != null) {
builder.setPersons(new Person[]{person});
}
return builder.build();
}
private Person buildPerson() {
return buildPerson(true, false);
}
private Person buildPerson(boolean isImportant, boolean isBot) {
return new Person.Builder()
.setImportant(isImportant)
.setBot(isBot)
.setUri(CONTACT_URI)
.build();
}
private UserInfo buildUserInfo(int userId) {
return new UserInfo(userId, "", 0);
}
/**
* Returns {@code true} iff {@link ShortcutQuery}'s {@code queryFlags} has {@code flag} set.
*/
private static boolean hasFlag(int queryFlags, int flag) {
return (queryFlags & flag) != 0;
}
// "Sends" a notification to a non-customized notification channel - the notification channel
// is something generic like "messages" and the notification has a shortcut id
private void sendGenericNotification() {
when(mNotification.getChannelId()).thenReturn(PARENT_NOTIFICATION_CHANNEL_ID);
doAnswer(invocationOnMock -> {
NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
invocationOnMock.getArguments()[1];
ranking.populate(
(String) invocationOnMock.getArguments()[0],
0,
false,
0,
0,
mParentNotificationChannel.getImportance(),
null, null,
mParentNotificationChannel, null, null, true, 0, false, -1, false, null, null,
false, false, false, null, 0, false);
return true;
}).when(mRankingMap).getRanking(eq(GENERIC_KEY),
any(NotificationListenerService.Ranking.class));
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationPosted(mGenericSbn, mRankingMap);
}
// "Sends" a notification to a customized notification channel - the notification channel
// is specific to a person, and the channel has a convo id matching the notification's shortcut
// and the channel has a parent channel id
private void sendConvoNotification() {
when(mNotification.getChannelId()).thenReturn(NOTIFICATION_CHANNEL_ID);
doAnswer(invocationOnMock -> {
NotificationListenerService.Ranking ranking = (NotificationListenerService.Ranking)
invocationOnMock.getArguments()[1];
ranking.populate(
(String) invocationOnMock.getArguments()[0],
0,
false,
0,
0,
mNotificationChannel.getImportance(),
null, null,
mNotificationChannel, null, null, true, 0, false, -1, false, null, null, false,
false, false, null, 0, false);
return true;
}).when(mRankingMap).getRanking(eq(CUSTOM_KEY),
any(NotificationListenerService.Ranking.class));
NotificationListenerService listenerService =
mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
listenerService.onNotificationPosted(mConvoSbn, mRankingMap);
}
private class TestContactsQueryHelper extends ContactsQueryHelper {
private Uri mContactUri;
private boolean mIsStarred;
private String mPhoneNumber;
TestContactsQueryHelper(Context context) {
super(context);
mContactUri = Uri.parse(CONTACT_URI);
mIsStarred = false;
mPhoneNumber = PHONE_NUMBER;
}
@Override
boolean query(@NonNull String contactUri) {
return true;
}
@Override
boolean querySince(long sinceTime) {
return true;
}
@Override
@Nullable
Uri getContactUri() {
return mContactUri;
}
@Override
boolean isStarred() {
return mIsStarred;
}
@Override
@Nullable
String getPhoneNumber() {
return mPhoneNumber;
}
}
private class TestCallLogQueryHelper extends CallLogQueryHelper {
private final BiConsumer<String, Event> mEventConsumer;
TestCallLogQueryHelper(Context context, BiConsumer<String, Event> eventConsumer) {
super(context, eventConsumer);
mEventConsumer = eventConsumer;
}
@Override
boolean querySince(long sinceTime) {
return true;
}
@Override
long getLastCallTimestamp() {
return 100L;
}
}
private class TestSmsQueryHelper extends SmsQueryHelper {
private final BiConsumer<String, Event> mEventConsumer;
TestSmsQueryHelper(Context context, BiConsumer<String, Event> eventConsumer) {
super(context, eventConsumer);
mEventConsumer = eventConsumer;
}
@Override
boolean querySince(long sinceTime) {
return true;
}
@Override
long getLastMessageTimestamp() {
return 100L;
}
}
private class TestMmsQueryHelper extends MmsQueryHelper {
private final BiConsumer<String, Event> mEventConsumer;
TestMmsQueryHelper(Context context, BiConsumer<String, Event> eventConsumer) {
super(context, eventConsumer);
mEventConsumer = eventConsumer;
}
@Override
boolean querySince(long sinceTime) {
return true;
}
@Override
long getLastMessageTimestamp() {
return 100L;
}
}
private class TestUsageStatsQueryHelper extends UsageStatsQueryHelper {
private final EventListener mEventListener;
TestUsageStatsQueryHelper(int userId,
Function<String, PackageData> packageDataGetter,
EventListener eventListener) {
super(userId, packageDataGetter, eventListener);
mEventListener = eventListener;
}
}
private class TestInjector extends DataManager.Injector {
private final TestContactsQueryHelper mContactsQueryHelper =
new TestContactsQueryHelper(mContext);
private TestCallLogQueryHelper mCallLogQueryHelper;
private TestMmsQueryHelper mMmsQueryHelper;
private TestSmsQueryHelper mSmsQueryHelper;
private TestUsageStatsQueryHelper mUsageStatsQueryHelper;
@Override
ScheduledExecutorService createScheduledExecutor() {
return mExecutorService;
}
@Override
Executor getBackgroundExecutor() {
return Runnable::run;
}
@Override
ContactsQueryHelper createContactsQueryHelper(Context context) {
return mContactsQueryHelper;
}
@Override
CallLogQueryHelper createCallLogQueryHelper(Context context,
BiConsumer<String, Event> eventConsumer) {
mCallLogQueryHelper = new TestCallLogQueryHelper(context, eventConsumer);
return mCallLogQueryHelper;
}
@Override
MmsQueryHelper createMmsQueryHelper(Context context,
BiConsumer<String, Event> eventConsumer) {
mMmsQueryHelper = new TestMmsQueryHelper(context, eventConsumer);
return mMmsQueryHelper;
}
@Override
SmsQueryHelper createSmsQueryHelper(Context context,
BiConsumer<String, Event> eventConsumer) {
mSmsQueryHelper = new TestSmsQueryHelper(context, eventConsumer);
return mSmsQueryHelper;
}
@Override
UsageStatsQueryHelper createUsageStatsQueryHelper(@UserIdInt int userId,
Function<String, PackageData> packageDataGetter,
UsageStatsQueryHelper.EventListener eventListener) {
mUsageStatsQueryHelper =
new TestUsageStatsQueryHelper(userId, packageDataGetter, eventListener);
return mUsageStatsQueryHelper;
}
}
}