*/
package com.android.settings.applications;
+import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManager;
import android.content.Context;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.view.View;
import android.widget.Switch;
import com.android.settings.R;
+import com.android.settings.Utils;
import com.android.settings.notification.NotificationBackend;
import com.android.settingslib.applications.ApplicationsState;
import com.android.settingslib.applications.ApplicationsState.AppEntry;
import java.util.ArrayList;
import java.util.Comparator;
+import java.util.List;
import java.util.Map;
/**
public class AppStateNotificationBridge extends AppStateBaseBridge {
private final Context mContext;
- private UsageStatsManager mUsageStatsManager;
+ private IUsageStatsManager mUsageStatsManager;
+ protected List<Integer> mUserIds;
private NotificationBackend mBackend;
private static final int DAYS_TO_CHECK = 7;
public AppStateNotificationBridge(Context context, ApplicationsState appState,
- Callback callback, UsageStatsManager usageStatsManager,
- NotificationBackend backend) {
+ Callback callback, IUsageStatsManager usageStatsManager,
+ UserManager userManager, NotificationBackend backend) {
super(appState, callback);
mContext = context;
mUsageStatsManager = usageStatsManager;
mBackend = backend;
+ mUserIds = new ArrayList<>();
+ mUserIds.add(mContext.getUserId());
+ int workUserId = Utils.getManagedProfileId(userManager, mContext.getUserId());
+ if (workUserId != UserHandle.USER_NULL) {
+ mUserIds.add(workUserId);
+ }
}
@Override
final Map<String, NotificationsSentState> map = getAggregatedUsageEvents();
for (AppEntry entry : apps) {
- NotificationsSentState stats = map.get(entry.info.packageName);
+ NotificationsSentState stats =
+ map.get(getKey(UserHandle.getUserId(entry.info.uid), entry.info.packageName));
calculateAvgSentCounts(stats);
addBlockStatus(entry, stats);
entry.extraInfo = stats;
@Override
protected void updateExtraInfo(AppEntry entry, String pkg, int uid) {
- Map<String, NotificationsSentState> map = getAggregatedUsageEvents();
- NotificationsSentState stats = map.get(entry.info.packageName);
+ NotificationsSentState stats = getAggregatedUsageEvents(
+ UserHandle.getUserId(entry.info.uid), entry.info.packageName);
calculateAvgSentCounts(stats);
addBlockStatus(entry, stats);
entry.extraInfo = stats;
long now = System.currentTimeMillis();
long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
- UsageEvents events = mUsageStatsManager.queryEvents(startTime, now);
+ for (int userId : mUserIds) {
+ UsageEvents events = null;
+ try {
+ events = mUsageStatsManager.queryEventsForUser(
+ startTime, now, userId, mContext.getPackageName());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ if (events != null) {
+ UsageEvents.Event event = new UsageEvents.Event();
+ while (events.hasNextEvent()) {
+ events.getNextEvent(event);
+ NotificationsSentState stats =
+ aggregatedStats.get(getKey(userId, event.getPackageName()));
+ if (stats == null) {
+ stats = new NotificationsSentState();
+ aggregatedStats.put(getKey(userId, event.getPackageName()), stats);
+ }
+
+ if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ if (event.getTimeStamp() > stats.lastSent) {
+ stats.lastSent = event.getTimeStamp();
+ }
+ stats.sentCount++;
+ }
+
+ }
+ }
+ }
+ return aggregatedStats;
+ }
+
+ protected NotificationsSentState getAggregatedUsageEvents(int userId, String pkg) {
+ NotificationsSentState stats = null;
+
+ long now = System.currentTimeMillis();
+ long startTime = now - (DateUtils.DAY_IN_MILLIS * DAYS_TO_CHECK);
+ UsageEvents events = null;
+ try {
+ events = mUsageStatsManager.queryEventsForPackageForUser(
+ startTime, now, userId, pkg, mContext.getPackageName());
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
if (events != null) {
UsageEvents.Event event = new UsageEvents.Event();
while (events.hasNextEvent()) {
events.getNextEvent(event);
- NotificationsSentState stats = aggregatedStats.get(event.getPackageName());
- if (stats == null) {
- stats = new NotificationsSentState();
- aggregatedStats.put(event.getPackageName(), stats);
- }
if (event.getEventType() == UsageEvents.Event.NOTIFICATION_INTERRUPTION) {
+ if (stats == null) {
+ stats = new NotificationsSentState();
+ }
if (event.getTimeStamp() > stats.lastSent) {
stats.lastSent = event.getTimeStamp();
}
}
}
- return aggregatedStats;
+ return stats;
}
private static NotificationsSentState getNotificationsSentState(AppEntry entry) {
return null;
}
+ protected static String getKey(int userId, String pkg) {
+ return userId + "|" + pkg;
+ }
+
public View.OnClickListener getSwitchOnClickListener(final AppEntry entry) {
if (entry != null) {
return v -> {
import android.annotation.Nullable;
import android.annotation.StringRes;
import android.app.Activity;
+import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageItemInfo;
import android.os.Bundle;
import android.os.Environment;
+import android.os.ServiceManager;
import android.os.UserHandle;
import android.os.UserManager;
import android.preference.PreferenceFrameLayout;
private View mSpinnerHeader;
private Spinner mFilterSpinner;
private FilterSpinnerAdapter mFilterAdapter;
- private UsageStatsManager mUsageStatsManager;
+ private IUsageStatsManager mUsageStatsManager;
+ private UserManager mUserManager;
private NotificationBackend mNotificationBackend;
private ResetAppsHelper mResetAppsHelper;
private String mVolumeUuid;
screenTitle = R.string.change_wifi_state_title;
} else if (className.equals(Settings.NotificationAppListActivity.class.getName())) {
mListType = LIST_TYPE_NOTIFICATION;
- mUsageStatsManager =
- (UsageStatsManager) getContext().getSystemService(Context.USAGE_STATS_SERVICE);
+ mUsageStatsManager = IUsageStatsManager.Stub.asInterface(
+ ServiceManager.getService(Context.USAGE_STATS_SERVICE));
+ mUserManager = UserManager.get(getContext());
mNotificationBackend = new NotificationBackend();
mSortOrder = R.id.sort_order_recent_notification;
screenTitle = R.string.app_notifications_title;
if (mManageApplications.mListType == LIST_TYPE_NOTIFICATION) {
mExtraInfoBridge = new AppStateNotificationBridge(mContext, mState, this,
manageApplications.mUsageStatsManager,
+ manageApplications.mUserManager,
manageApplications.mNotificationBackend);
} else if (mManageApplications.mListType == LIST_TYPE_USAGE_ACCESS) {
mExtraInfoBridge = new AppStateUsageBridge(mContext, mState, this);
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.app.usage.IUsageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageEvents.Event;
-import android.app.usage.UsageStatsManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.os.Looper;
import android.os.Parcel;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.view.ViewGroup;
import android.widget.Switch;
@Mock
private ApplicationsState mState;
@Mock
- private UsageStatsManager mUsageStats;
+ private IUsageStatsManager mUsageStats;
+ @Mock
+ private UserManager mUserManager;
@Mock
private NotificationBackend mBackend;
private Context mContext;
when(mState.getBackgroundLooper()).thenReturn(mock(Looper.class));
when(mBackend.getNotificationsBanned(anyString(), anyInt())).thenReturn(true);
when(mBackend.isSystemApp(any(), any())).thenReturn(true);
+ // most tests assume no work profile
+ when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
mContext = RuntimeEnvironment.application.getApplicationContext();
mBridge = new AppStateNotificationBridge(mContext, mState,
- mock(AppStateBaseBridge.Callback.class), mUsageStats, mBackend);
+ mock(AppStateBaseBridge.Callback.class), mUsageStats, mUserManager, mBackend);
}
private AppEntry getMockAppEntry(String pkg) {
}
@Test
- public void testGetAggregatedUsageEvents_noEvents() {
- when(mUsageStats.queryEvents(anyLong(), anyLong())).thenReturn(mock(UsageEvents.class));
+ public void testGetAggregatedUsageEvents_noEvents() throws Exception {
+ when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
+ .thenReturn(mock(UsageEvents.class));
assertThat(mBridge.getAggregatedUsageEvents()).isEmpty();
}
@Test
- public void testGetAggregatedUsageEvents_onlyNotificationEvents() {
+ public void testGetAggregatedUsageEvents_onlyNotificationEvents() throws Exception {
List<Event> events = new ArrayList<>();
Event good = new Event();
good.mEventType = Event.NOTIFICATION_INTERRUPTION;
events.add(bad);
UsageEvents usageEvents = getUsageEvents(events);
- when(mUsageStats.queryEvents(anyLong(), anyLong())).thenReturn(usageEvents);
+ when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
+ .thenReturn(usageEvents);
Map<String, NotificationsSentState> map = mBridge.getAggregatedUsageEvents();
- assertThat(map.get(PKG1).sentCount).isEqualTo(1);
+ assertThat(map.get(mBridge.getKey(0, PKG1)).sentCount).isEqualTo(1);
}
@Test
- public void testGetAggregatedUsageEvents_multipleEventsAgg() {
+ public void testGetAggregatedUsageEvents_multipleEventsAgg() throws Exception {
List<Event> events = new ArrayList<>();
Event good = new Event();
good.mEventType = Event.NOTIFICATION_INTERRUPTION;
events.add(good1);
UsageEvents usageEvents = getUsageEvents(events);
- when(mUsageStats.queryEvents(anyLong(), anyLong())).thenReturn(usageEvents);
+ when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
+ .thenReturn(usageEvents);
Map<String, NotificationsSentState> map = mBridge.getAggregatedUsageEvents();
- assertThat(map.get(PKG1).sentCount).isEqualTo(2);
- assertThat(map.get(PKG1).lastSent).isEqualTo(6);
+ assertThat(map.get(mBridge.getKey(0, PKG1)).sentCount).isEqualTo(2);
+ assertThat(map.get(mBridge.getKey(0, PKG1)).lastSent).isEqualTo(6);
}
@Test
- public void testGetAggregatedUsageEvents_multiplePkgs() {
+ public void testGetAggregatedUsageEvents_multiplePkgs() throws Exception {
List<Event> events = new ArrayList<>();
Event good = new Event();
good.mEventType = Event.NOTIFICATION_INTERRUPTION;
events.add(good1);
UsageEvents usageEvents = getUsageEvents(events);
- when(mUsageStats.queryEvents(anyLong(), anyLong())).thenReturn(usageEvents);
+ when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
+ .thenReturn(usageEvents);
Map<String, NotificationsSentState> map
= mBridge.getAggregatedUsageEvents();
- assertThat(map.get(PKG1).sentCount).isEqualTo(1);
- assertThat(map.get(PKG2).sentCount).isEqualTo(1);
- assertThat(map.get(PKG1).lastSent).isEqualTo(6);
- assertThat(map.get(PKG2).lastSent).isEqualTo(1);
+ assertThat(map.get(mBridge.getKey(0, PKG1)).sentCount).isEqualTo(1);
+ assertThat(map.get(mBridge.getKey(0, PKG2)).sentCount).isEqualTo(1);
+ assertThat(map.get(mBridge.getKey(0, PKG1)).lastSent).isEqualTo(6);
+ assertThat(map.get(mBridge.getKey(0, PKG2)).lastSent).isEqualTo(1);
}
@Test
- public void testLoadAllExtraInfo_noEvents() {
- when(mUsageStats.queryEvents(anyLong(), anyLong())).thenReturn(mock(UsageEvents.class));
+ public void testLoadAllExtraInfo_noEvents() throws RemoteException {
+ when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
+ .thenReturn(mock(UsageEvents.class));
ArrayList<AppEntry> apps = new ArrayList<>();
apps.add(getMockAppEntry(PKG1));
when(mSession.getAllApps()).thenReturn(apps);
}
@Test
- public void testLoadAllExtraInfo_multipleEventsAgg() {
+ public void testLoadAllExtraInfo_multipleEventsAgg() throws RemoteException {
List<Event> events = new ArrayList<>();
for (int i = 0; i < 7; i++) {
Event good = new Event();
}
UsageEvents usageEvents = getUsageEvents(events);
- when(mUsageStats.queryEvents(anyLong(), anyLong())).thenReturn(usageEvents);
+ when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
+ .thenReturn(usageEvents);
ArrayList<AppEntry> apps = new ArrayList<>();
apps.add(getMockAppEntry(PKG1));
}
@Test
- public void testLoadAllExtraInfo_multiplePkgs() {
+ public void testLoadAllExtraInfo_multiplePkgs() throws RemoteException {
List<Event> events = new ArrayList<>();
for (int i = 0; i < 8; i++) {
Event good = new Event();
events.add(good1);
UsageEvents usageEvents = getUsageEvents(events);
- when(mUsageStats.queryEvents(anyLong(), anyLong())).thenReturn(usageEvents);
+ when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), anyInt(), anyString()))
+ .thenReturn(usageEvents);
ArrayList<AppEntry> apps = new ArrayList<>();
apps.add(getMockAppEntry(PKG1));
}
@Test
- public void testUpdateExtraInfo_noEvents() {
- when(mUsageStats.queryEvents(anyLong(), anyLong())).thenReturn(mock(UsageEvents.class));
+ public void testLoadAllExtraInfo_multipleUsers() throws RemoteException {
+ // has work profile
+ when(mUserManager.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{1});
+ mBridge = new AppStateNotificationBridge(mContext, mState,
+ mock(AppStateBaseBridge.Callback.class), mUsageStats, mUserManager, mBackend);
+
+ List<Event> eventsProfileOwner = new ArrayList<>();
+ for (int i = 0; i < 8; i++) {
+ Event good = new Event();
+ good.mEventType = Event.NOTIFICATION_INTERRUPTION;
+ good.mPackage = PKG1;
+ good.mTimeStamp = i;
+ eventsProfileOwner.add(good);
+ }
+
+ List<Event> eventsProfile = new ArrayList<>();
+ for (int i = 0; i < 4; i++) {
+ Event good = new Event();
+ good.mEventType = Event.NOTIFICATION_INTERRUPTION;
+ good.mPackage = PKG1;
+ good.mTimeStamp = i;
+ eventsProfile.add(good);
+ }
+
+ UsageEvents usageEventsOwner = getUsageEvents(eventsProfileOwner);
+ when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), eq(0), anyString()))
+ .thenReturn(usageEventsOwner);
+
+ UsageEvents usageEventsProfile = getUsageEvents(eventsProfile);
+ when(mUsageStats.queryEventsForUser(anyLong(), anyLong(), eq(1), anyString()))
+ .thenReturn(usageEventsProfile);
+
+ ArrayList<AppEntry> apps = new ArrayList<>();
+ AppEntry owner = getMockAppEntry(PKG1);
+ owner.info.uid = 1;
+ apps.add(owner);
+
+ AppEntry profile = getMockAppEntry(PKG1);
+ profile.info.uid = UserHandle.PER_USER_RANGE + 1;
+ apps.add(profile);
+ when(mSession.getAllApps()).thenReturn(apps);
+
+ mBridge.loadAllExtraInfo();
+
+ assertThat(((NotificationsSentState) apps.get(0).extraInfo).sentCount).isEqualTo(8);
+ assertThat(((NotificationsSentState) apps.get(0).extraInfo).lastSent).isEqualTo(7);
+ assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentWeekly).isEqualTo(0);
+ assertThat(((NotificationsSentState) apps.get(0).extraInfo).avgSentDaily).isEqualTo(1);
+
+ assertThat(((NotificationsSentState) apps.get(1).extraInfo).sentCount).isEqualTo(4);
+ assertThat(((NotificationsSentState) apps.get(1).extraInfo).lastSent).isEqualTo(3);
+ assertThat(((NotificationsSentState) apps.get(1).extraInfo).avgSentWeekly).isEqualTo(4);
+ assertThat(((NotificationsSentState) apps.get(1).extraInfo).avgSentDaily).isEqualTo(1);
+ }
+
+ @Test
+ public void testUpdateExtraInfo_noEvents() throws RemoteException {
+ when(mUsageStats.queryEventsForPackageForUser(
+ anyLong(), anyLong(), anyInt(), anyString(), anyString()))
+ .thenReturn(mock(UsageEvents.class));
AppEntry entry = getMockAppEntry(PKG1);
mBridge.updateExtraInfo(entry, "", 0);
}
@Test
- public void testUpdateExtraInfo_multipleEventsAgg() {
+ public void testUpdateExtraInfo_multipleEventsAgg() throws RemoteException {
List<Event> events = new ArrayList<>();
for (int i = 0; i < 13; i++) {
Event good = new Event();
}
UsageEvents usageEvents = getUsageEvents(events);
- when(mUsageStats.queryEvents(anyLong(), anyLong())).thenReturn(usageEvents);
+ when(mUsageStats.queryEventsForPackageForUser(
+ anyLong(), anyLong(), anyInt(), anyString(), anyString())).thenReturn(usageEvents);
AppEntry entry = getMockAppEntry(PKG1);
mBridge.updateExtraInfo(entry, "", 0);
package com.android.settings.applications.manageapplications;
-import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING;
-import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
-import static com.android.settings.applications.manageapplications.AppFilterRegistry.FILTER_APPS_ALL;
-import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_MAIN;
-import static com.android.settings.applications.manageapplications.ManageApplications.LIST_TYPE_NOTIFICATION;
-import static com.google.common.truth.Truth.assertThat;
+import static com.android.settings.applications.manageapplications.AppFilterRegistry
+ .FILTER_APPS_ALL;
+import static com.android.settings.applications.manageapplications.ManageApplications
+ .LIST_TYPE_MAIN;
+import static com.android.settings.applications.manageapplications.ManageApplications
+ .LIST_TYPE_NOTIFICATION;
-import static junit.framework.Assert.assertEquals;
+import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING;
+import static androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_IDLE;
+
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Looper;
-import androidx.recyclerview.widget.RecyclerView;
+import android.os.UserManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import java.util.ArrayList;
+import androidx.recyclerview.widget.RecyclerView;
+
@RunWith(SettingsRobolectricTestRunner.class)
public class ManageApplicationsTest {
@Test
public void applicationsAdapter_onBindViewHolder_updateSwitch_notifications() {
ManageApplications manageApplications = mock(ManageApplications.class);
+ when(manageApplications.getActivity()).thenReturn(mock(Activity.class));
+ UserManager um = mock(UserManager.class);
+ when(um.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
+ ReflectionHelpers.setField(manageApplications, "mUserManager", um);
manageApplications.mListType = LIST_TYPE_NOTIFICATION;
ApplicationViewHolder holder = mock(ApplicationViewHolder.class);
ReflectionHelpers.setField(holder, "itemView", mock(View.class));
manageApplications.mListType = LIST_TYPE_MAIN;
ApplicationViewHolder holder = mock(ApplicationViewHolder.class);
ReflectionHelpers.setField(holder, "itemView", mock(View.class));
+ UserManager um = mock(UserManager.class);
+ when(um.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
+ ReflectionHelpers.setField(manageApplications, "mUserManager", um);
ManageApplications.ApplicationsAdapter adapter =
new ManageApplications.ApplicationsAdapter(mState,
manageApplications, mock(AppFilterItem.class),
@Test
public void sortOrderSavedOnRebuild() {
ManageApplications manageApplications = mock(ManageApplications.class);
+ when(manageApplications.getActivity()).thenReturn(mock(Activity.class));
+ UserManager um = mock(UserManager.class);
+ when(um.getProfileIdsWithDisabled(anyInt())).thenReturn(new int[]{});
+ ReflectionHelpers.setField(manageApplications, "mUserManager", um);
manageApplications.mListType = LIST_TYPE_NOTIFICATION;
manageApplications.mSortOrder = -1;
ManageApplications.ApplicationsAdapter adapter =