Merge "Improve the latency of NotificationChannelSlice" into qt-dev
am: c7564be562
Change-Id: I86c35f4c8d4dde32592ddec09926e226dbcc0751
This commit is contained in:
@@ -66,7 +66,12 @@ import com.android.settingslib.applications.ApplicationsState;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class NotificationChannelSlice implements CustomSliceable {
|
||||
@@ -98,6 +103,7 @@ public class NotificationChannelSlice implements CustomSliceable {
|
||||
private static final String PACKAGE_NAME = "package_name";
|
||||
private static final String PACKAGE_UID = "package_uid";
|
||||
private static final String CHANNEL_ID = "channel_id";
|
||||
private static final long TASK_TIMEOUT_MS = 100;
|
||||
|
||||
/**
|
||||
* Sort notification channel with weekly average sent count by descending.
|
||||
@@ -129,6 +135,7 @@ public class NotificationChannelSlice implements CustomSliceable {
|
||||
};
|
||||
|
||||
protected final Context mContext;
|
||||
private final ExecutorService mExecutorService;
|
||||
@VisibleForTesting
|
||||
NotificationBackend mNotificationBackend;
|
||||
private NotificationBackend.AppRow mAppRow;
|
||||
@@ -138,6 +145,7 @@ public class NotificationChannelSlice implements CustomSliceable {
|
||||
public NotificationChannelSlice(Context context) {
|
||||
mContext = context;
|
||||
mNotificationBackend = new NotificationBackend();
|
||||
mExecutorService = Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -151,10 +159,7 @@ public class NotificationChannelSlice implements CustomSliceable {
|
||||
* 2. Multiple channels.
|
||||
* 3. Sent at least ~10 notifications.
|
||||
*/
|
||||
// TODO(b/123065955): Review latency of NotificationChannelSlice
|
||||
final List<PackageInfo> multiChannelPackages = getMultiChannelPackages(
|
||||
getRecentlyInstalledPackages());
|
||||
mPackageName = getMaxSentNotificationsPackage(multiChannelPackages);
|
||||
mPackageName = getEligibleNotificationsPackage(getRecentlyInstalledPackages());
|
||||
if (mPackageName == null) {
|
||||
// Return a header with IsError flag, if package is not found.
|
||||
return listBuilder.setHeader(getNoSuggestedAppHeader())
|
||||
@@ -306,25 +311,6 @@ public class NotificationChannelSlice implements CustomSliceable {
|
||||
return PendingIntent.getBroadcast(mContext, intent.hashCode(), intent, 0);
|
||||
}
|
||||
|
||||
private List<PackageInfo> getMultiChannelPackages(List<PackageInfo> packageInfoList) {
|
||||
final List<PackageInfo> multiChannelPackages = new ArrayList<>();
|
||||
|
||||
if (packageInfoList.isEmpty()) {
|
||||
return multiChannelPackages;
|
||||
}
|
||||
|
||||
for (PackageInfo packageInfo : packageInfoList) {
|
||||
final int channelCount = mNotificationBackend.getChannelCount(packageInfo.packageName,
|
||||
getApplicationUid(packageInfo.packageName));
|
||||
if (channelCount > 1) {
|
||||
multiChannelPackages.add(packageInfo);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/119831690): Filter the packages which doesn't have any configurable channel.
|
||||
return multiChannelPackages;
|
||||
}
|
||||
|
||||
private List<PackageInfo> getRecentlyInstalledPackages() {
|
||||
final long startTime = System.currentTimeMillis() - DURATION_START_DAYS;
|
||||
final long endTime = System.currentTimeMillis() - DURATION_END_DAYS;
|
||||
@@ -383,19 +369,33 @@ public class NotificationChannelSlice implements CustomSliceable {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private String getMaxSentNotificationsPackage(List<PackageInfo> packageInfoList) {
|
||||
private String getEligibleNotificationsPackage(List<PackageInfo> packageInfoList) {
|
||||
if (packageInfoList.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create tasks to get notification data for multi-channel packages.
|
||||
final List<Future<NotificationBackend.AppRow>> appRowTasks = new ArrayList<>();
|
||||
for (PackageInfo packageInfo : packageInfoList) {
|
||||
final NotificationMultiChannelAppRow future = new NotificationMultiChannelAppRow(
|
||||
mContext, mNotificationBackend, packageInfo);
|
||||
appRowTasks.add(mExecutorService.submit(future));
|
||||
}
|
||||
|
||||
// Get the package which has sent at least ~10 notifications and not turn off channels.
|
||||
int maxSentCount = 0;
|
||||
String maxSentCountPackage = null;
|
||||
for (PackageInfo packageInfo : packageInfoList) {
|
||||
final NotificationBackend.AppRow appRow = mNotificationBackend.loadAppRow(mContext,
|
||||
mContext.getPackageManager(), packageInfo);
|
||||
for (Future<NotificationBackend.AppRow> appRowTask : appRowTasks) {
|
||||
NotificationBackend.AppRow appRow = null;
|
||||
try {
|
||||
appRow = appRowTask.get(TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (ExecutionException | InterruptedException | TimeoutException e) {
|
||||
Log.w(TAG, "Failed to get notification data.", e);
|
||||
}
|
||||
|
||||
// Ignore packages which are banned notifications or block all displayable channels.
|
||||
if (appRow.banned || isAllChannelsBlocked(getDisplayableChannels(appRow))) {
|
||||
if (appRow == null || appRow.banned || isAllChannelsBlocked(
|
||||
getDisplayableChannels(appRow))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ public class NotificationChannelSlice implements CustomSliceable {
|
||||
final int sentCount = appRow.sentByApp.sentCount;
|
||||
if (sentCount >= MIN_NOTIFICATION_SENT_COUNT && sentCount > maxSentCount) {
|
||||
maxSentCount = sentCount;
|
||||
maxSentCountPackage = packageInfo.packageName;
|
||||
maxSentCountPackage = appRow.pkg;
|
||||
mAppRow = appRow;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settings.homepage.contextualcards.slices;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageInfo;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* This class is responsible for getting notification app row from package which has multiple
|
||||
* notification channels.{@link NotificationChannelSlice} uses it to improve latency.
|
||||
*/
|
||||
class NotificationMultiChannelAppRow implements Callable<NotificationBackend.AppRow> {
|
||||
|
||||
private final Context mContext;
|
||||
private final NotificationBackend mNotificationBackend;
|
||||
private final PackageInfo mPackageInfo;
|
||||
|
||||
public NotificationMultiChannelAppRow(Context context, NotificationBackend notificationBackend,
|
||||
PackageInfo packageInfo) {
|
||||
mContext = context;
|
||||
mNotificationBackend = notificationBackend;
|
||||
mPackageInfo = packageInfo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NotificationBackend.AppRow call() throws Exception {
|
||||
final int channelCount = mNotificationBackend.getChannelCount(
|
||||
mPackageInfo.applicationInfo.packageName, mPackageInfo.applicationInfo.uid);
|
||||
if (channelCount > 1) {
|
||||
return mNotificationBackend.loadAppRow(mContext, mContext.getPackageManager(),
|
||||
mPackageInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -319,6 +319,7 @@ public class NotificationChannelSliceTest {
|
||||
applicationInfo.name = APP_LABEL;
|
||||
applicationInfo.uid = UID;
|
||||
applicationInfo.flags = flags;
|
||||
applicationInfo.packageName = PACKAGE_NAME;
|
||||
|
||||
final PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = PACKAGE_NAME;
|
||||
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.settings.homepage.contextualcards.slices;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import com.android.settings.notification.NotificationBackend;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.RuntimeEnvironment;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
public class NotificationMultiChannelAppRowTest {
|
||||
|
||||
@Mock
|
||||
private NotificationBackend mNotificationBackend;
|
||||
private Context mContext;
|
||||
private NotificationMultiChannelAppRow mNotificationMultiChannelAppRow;
|
||||
private PackageInfo mPackageInfo;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mContext = RuntimeEnvironment.application;
|
||||
mPackageInfo = new PackageInfo();
|
||||
mPackageInfo.applicationInfo = new ApplicationInfo();
|
||||
mPackageInfo.applicationInfo.packageName = "com.android.test";
|
||||
mNotificationMultiChannelAppRow = new NotificationMultiChannelAppRow(mContext,
|
||||
mNotificationBackend, mPackageInfo);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void call_isMultiChannel_shouldLoadAppRow() throws Exception {
|
||||
doReturn(3).when(mNotificationBackend).getChannelCount(any(String.class),
|
||||
any(int.class));
|
||||
|
||||
mNotificationMultiChannelAppRow.call();
|
||||
|
||||
verify(mNotificationBackend).loadAppRow(any(Context.class), any(PackageManager.class),
|
||||
any(PackageInfo.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void call_isNotMultiChannel_shouldNotLoadAppRow() throws Exception {
|
||||
doReturn(1).when(mNotificationBackend).getChannelCount(any(String.class),
|
||||
any(int.class));
|
||||
|
||||
mNotificationMultiChannelAppRow.call();
|
||||
|
||||
verify(mNotificationBackend, never()).loadAppRow(any(Context.class),
|
||||
any(PackageManager.class), any(PackageInfo.class));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user