Extract app data usage to AppDataUsageRepository
From DataUsageList for better organization and testing. Bug: 290856342 Test: manual - on DataUsageList Test: unit test Change-Id: I97e327a220d40942b9345ec7f1f8c466ac1fc9da
This commit is contained in:
@@ -14,32 +14,23 @@
|
|||||||
|
|
||||||
package com.android.settings.datausage;
|
package com.android.settings.datausage;
|
||||||
|
|
||||||
import static android.app.usage.NetworkStats.Bucket.UID_REMOVED;
|
|
||||||
import static android.app.usage.NetworkStats.Bucket.UID_TETHERING;
|
|
||||||
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
import android.app.settings.SettingsEnums;
|
import android.app.settings.SettingsEnums;
|
||||||
import android.app.usage.NetworkStats;
|
import android.app.usage.NetworkStats;
|
||||||
import android.app.usage.NetworkStats.Bucket;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.UserInfo;
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.net.ConnectivityManager;
|
import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkPolicy;
|
import android.net.NetworkPolicy;
|
||||||
import android.net.NetworkTemplate;
|
import android.net.NetworkTemplate;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Process;
|
|
||||||
import android.os.UserHandle;
|
|
||||||
import android.os.UserManager;
|
import android.os.UserManager;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.telephony.SubscriptionInfo;
|
import android.telephony.SubscriptionInfo;
|
||||||
import android.telephony.SubscriptionManager;
|
import android.telephony.SubscriptionManager;
|
||||||
import android.util.EventLog;
|
import android.util.EventLog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.SparseArray;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.AccessibilityDelegate;
|
import android.view.View.AccessibilityDelegate;
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
import android.view.accessibility.AccessibilityEvent;
|
||||||
@@ -60,6 +51,7 @@ import androidx.preference.PreferenceGroup;
|
|||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
import com.android.settings.core.SubSettingLauncher;
|
import com.android.settings.core.SubSettingLauncher;
|
||||||
import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
|
import com.android.settings.datausage.CycleAdapter.SpinnerInterface;
|
||||||
|
import com.android.settings.datausage.lib.AppDataUsageRepository;
|
||||||
import com.android.settings.network.MobileDataEnabledListener;
|
import com.android.settings.network.MobileDataEnabledListener;
|
||||||
import com.android.settings.network.MobileNetworkRepository;
|
import com.android.settings.network.MobileNetworkRepository;
|
||||||
import com.android.settings.network.ProxySubscriptionManager;
|
import com.android.settings.network.ProxySubscriptionManager;
|
||||||
@@ -69,13 +61,10 @@ import com.android.settingslib.mobile.dataservice.SubscriptionInfoEntity;
|
|||||||
import com.android.settingslib.net.NetworkCycleChartData;
|
import com.android.settingslib.net.NetworkCycleChartData;
|
||||||
import com.android.settingslib.net.NetworkCycleChartDataLoader;
|
import com.android.settingslib.net.NetworkCycleChartDataLoader;
|
||||||
import com.android.settingslib.net.NetworkStatsSummaryLoader;
|
import com.android.settingslib.net.NetworkStatsSummaryLoader;
|
||||||
import com.android.settingslib.net.UidDetail;
|
|
||||||
import com.android.settingslib.net.UidDetailProvider;
|
import com.android.settingslib.net.UidDetailProvider;
|
||||||
import com.android.settingslib.utils.ThreadUtils;
|
import com.android.settingslib.utils.ThreadUtils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
@@ -423,110 +412,19 @@ public class DataUsageList extends DataUsageBaseFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bind the given {@link NetworkStats}, or {@code null} to clear list.
|
* Bind the given buckets.
|
||||||
*/
|
*/
|
||||||
private void bindStats(NetworkStats stats, int[] restrictedUids) {
|
private void bindStats(List<AppDataUsageRepository.Bucket> buckets) {
|
||||||
mApps.removeAll();
|
mApps.removeAll();
|
||||||
if (stats == null) {
|
AppDataUsageRepository repository = new AppDataUsageRepository(
|
||||||
if (LOGD) {
|
requireContext(),
|
||||||
Log.d(TAG, "No network stats data. App list cleared.");
|
ActivityManager.getCurrentUser(),
|
||||||
}
|
mSubscriptionInfoEntity == null ? null : mSubscriptionInfoEntity.carrierId,
|
||||||
return;
|
appItem -> mUidDetailProvider.getUidDetail(appItem.key, true).packageName
|
||||||
}
|
);
|
||||||
|
for (var itemPercentPair : repository.getAppPercent(buckets)) {
|
||||||
final ArrayList<AppItem> items = new ArrayList<>();
|
|
||||||
long largest = 0;
|
|
||||||
|
|
||||||
final int currentUserId = ActivityManager.getCurrentUser();
|
|
||||||
final UserManager userManager = UserManager.get(getContext());
|
|
||||||
final List<UserHandle> profiles = userManager.getUserProfiles();
|
|
||||||
final SparseArray<AppItem> knownItems = new SparseArray<AppItem>();
|
|
||||||
|
|
||||||
final Bucket bucket = new Bucket();
|
|
||||||
while (stats.hasNextBucket() && stats.getNextBucket(bucket)) {
|
|
||||||
// Decide how to collapse items together
|
|
||||||
final int uid = bucket.getUid();
|
|
||||||
final int collapseKey;
|
|
||||||
final int category;
|
|
||||||
final int userId = UserHandle.getUserId(uid);
|
|
||||||
if (UserHandle.isApp(uid) || Process.isSdkSandboxUid(uid)) {
|
|
||||||
if (profiles.contains(new UserHandle(userId))) {
|
|
||||||
if (userId != currentUserId) {
|
|
||||||
// Add to a managed user item.
|
|
||||||
final int managedKey = UidDetailProvider.buildKeyForUser(userId);
|
|
||||||
largest = accumulate(managedKey, knownItems, bucket,
|
|
||||||
AppItem.CATEGORY_USER, items, largest);
|
|
||||||
}
|
|
||||||
// Map SDK sandbox back to its corresponding app
|
|
||||||
if (Process.isSdkSandboxUid(uid)) {
|
|
||||||
collapseKey = Process.getAppUidForSdkSandboxUid(uid);
|
|
||||||
} else {
|
|
||||||
collapseKey = uid;
|
|
||||||
}
|
|
||||||
category = AppItem.CATEGORY_APP;
|
|
||||||
} else {
|
|
||||||
// If it is a removed user add it to the removed users' key
|
|
||||||
final UserInfo info = userManager.getUserInfo(userId);
|
|
||||||
if (info == null) {
|
|
||||||
collapseKey = UID_REMOVED;
|
|
||||||
category = AppItem.CATEGORY_APP;
|
|
||||||
} else {
|
|
||||||
// Add to other user item.
|
|
||||||
collapseKey = UidDetailProvider.buildKeyForUser(userId);
|
|
||||||
category = AppItem.CATEGORY_USER;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (uid == UID_REMOVED || uid == UID_TETHERING
|
|
||||||
|| uid == Process.OTA_UPDATE_UID) {
|
|
||||||
collapseKey = uid;
|
|
||||||
category = AppItem.CATEGORY_APP;
|
|
||||||
} else {
|
|
||||||
collapseKey = android.os.Process.SYSTEM_UID;
|
|
||||||
category = AppItem.CATEGORY_APP;
|
|
||||||
}
|
|
||||||
largest = accumulate(collapseKey, knownItems, bucket, category, items, largest);
|
|
||||||
}
|
|
||||||
stats.close();
|
|
||||||
|
|
||||||
for (final int uid : restrictedUids) {
|
|
||||||
// Only splice in restricted state for current user or managed users
|
|
||||||
if (!profiles.contains(UserHandle.getUserHandleForUid(uid))) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
AppItem item = knownItems.get(uid);
|
|
||||||
if (item == null) {
|
|
||||||
item = new AppItem(uid);
|
|
||||||
item.total = -1;
|
|
||||||
item.addUid(uid);
|
|
||||||
items.add(item);
|
|
||||||
knownItems.put(item.key, item);
|
|
||||||
}
|
|
||||||
item.restricted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Collections.sort(items);
|
|
||||||
final List<String> packageNames = Arrays.asList(getContext().getResources().getStringArray(
|
|
||||||
R.array.datausage_hiding_carrier_service_package_names));
|
|
||||||
// When there is no specified SubscriptionInfo, Wi-Fi data usage will be displayed.
|
|
||||||
// In this case, the carrier service package also needs to be hidden.
|
|
||||||
boolean shouldHidePackageName = mSubscriptionInfoEntity == null
|
|
||||||
|| Arrays.stream(getContext().getResources().getIntArray(
|
|
||||||
R.array.datausage_hiding_carrier_service_carrier_id))
|
|
||||||
.anyMatch(carrierId -> (carrierId == mSubscriptionInfoEntity.carrierId));
|
|
||||||
|
|
||||||
for (var item : items) {
|
|
||||||
UidDetail detail = mUidDetailProvider.getUidDetail(item.key, true);
|
|
||||||
// Do not show carrier service package in data usage list if it should be hidden for
|
|
||||||
// the carrier.
|
|
||||||
if (detail != null && shouldHidePackageName && packageNames.contains(
|
|
||||||
detail.packageName)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int percentTotal = largest != 0 ? (int) (item.total * 100 / largest) : 0;
|
|
||||||
final AppDataUsagePreference preference = new AppDataUsagePreference(getContext(),
|
final AppDataUsagePreference preference = new AppDataUsagePreference(getContext(),
|
||||||
item, percentTotal, mUidDetailProvider);
|
itemPercentPair.getFirst(), itemPercentPair.getSecond(), mUidDetailProvider);
|
||||||
preference.setOnPreferenceClickListener(p -> {
|
preference.setOnPreferenceClickListener(p -> {
|
||||||
AppDataUsagePreference pref = (AppDataUsagePreference) p;
|
AppDataUsagePreference pref = (AppDataUsagePreference) p;
|
||||||
startAppDataUsage(pref.getItem());
|
startAppDataUsage(pref.getItem());
|
||||||
@@ -565,30 +463,6 @@ public class DataUsageList extends DataUsageBaseFragment
|
|||||||
.launch();
|
.launch();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Accumulate data usage of a network stats entry for the item mapped by the collapse key.
|
|
||||||
* Creates the item if needed.
|
|
||||||
*
|
|
||||||
* @param collapseKey the collapse key used to map the item.
|
|
||||||
* @param knownItems collection of known (already existing) items.
|
|
||||||
* @param bucket the network stats bucket to extract data usage from.
|
|
||||||
* @param itemCategory the item is categorized on the list view by this category. Must be
|
|
||||||
*/
|
|
||||||
private static long accumulate(int collapseKey, final SparseArray<AppItem> knownItems,
|
|
||||||
Bucket bucket, int itemCategory, ArrayList<AppItem> items, long largest) {
|
|
||||||
final int uid = bucket.getUid();
|
|
||||||
AppItem item = knownItems.get(collapseKey);
|
|
||||||
if (item == null) {
|
|
||||||
item = new AppItem(collapseKey);
|
|
||||||
item.category = itemCategory;
|
|
||||||
items.add(item);
|
|
||||||
knownItems.put(item.key, item);
|
|
||||||
}
|
|
||||||
item.addUid(uid);
|
|
||||||
item.total += bucket.getRxBytes() + bucket.getTxBytes();
|
|
||||||
return Math.max(largest, item.total);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
|
private final OnItemSelectedListener mCycleListener = new OnItemSelectedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
@@ -643,15 +517,13 @@ public class DataUsageList extends DataUsageBaseFragment
|
|||||||
@Override
|
@Override
|
||||||
public void onLoadFinished(
|
public void onLoadFinished(
|
||||||
@NonNull Loader<NetworkStats> loader, NetworkStats data) {
|
@NonNull Loader<NetworkStats> loader, NetworkStats data) {
|
||||||
final int[] restrictedUids = services.mPolicyManager.getUidsWithPolicy(
|
bindStats(AppDataUsageRepository.Companion.convertToBuckets(data));
|
||||||
POLICY_REJECT_METERED_BACKGROUND);
|
|
||||||
bindStats(data, restrictedUids);
|
|
||||||
updateEmptyVisible();
|
updateEmptyVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoaderReset(@NonNull Loader<NetworkStats> loader) {
|
public void onLoaderReset(@NonNull Loader<NetworkStats> loader) {
|
||||||
bindStats(null, new int[0]);
|
mApps.removeAll();
|
||||||
updateEmptyVisible();
|
updateEmptyVisible();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
192
src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
Normal file
192
src/com/android/settings/datausage/lib/AppDataUsageRepository.kt
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.datausage.lib
|
||||||
|
|
||||||
|
import android.app.usage.NetworkStats
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.NetworkPolicyManager
|
||||||
|
import android.os.Process
|
||||||
|
import android.os.UserHandle
|
||||||
|
import android.util.SparseArray
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settingslib.AppItem
|
||||||
|
import com.android.settingslib.net.UidDetailProvider
|
||||||
|
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||||
|
|
||||||
|
class AppDataUsageRepository(
|
||||||
|
private val context: Context,
|
||||||
|
private val currentUserId: Int,
|
||||||
|
private val carrierId: Int?,
|
||||||
|
private val getPackageName: (AppItem) -> String,
|
||||||
|
) {
|
||||||
|
data class Bucket(
|
||||||
|
val uid: Int,
|
||||||
|
val bytes: Long,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun getAppPercent(buckets: List<Bucket>): List<Pair<AppItem, Int>> {
|
||||||
|
val items = ArrayList<AppItem>()
|
||||||
|
val knownItems = SparseArray<AppItem>()
|
||||||
|
val profiles = context.userManager.userProfiles
|
||||||
|
bindStats(buckets, profiles, knownItems, items)
|
||||||
|
val restrictedUids = context.getSystemService(NetworkPolicyManager::class.java)!!
|
||||||
|
.getUidsWithPolicy(NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND)
|
||||||
|
for (uid in restrictedUids) {
|
||||||
|
// Only splice in restricted state for current user or managed users
|
||||||
|
if (!profiles.contains(UserHandle.getUserHandleForUid(uid))) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var item = knownItems[uid]
|
||||||
|
if (item == null) {
|
||||||
|
item = AppItem(uid)
|
||||||
|
item.total = 0
|
||||||
|
item.addUid(uid)
|
||||||
|
items.add(item)
|
||||||
|
knownItems.put(item.key, item)
|
||||||
|
}
|
||||||
|
item.restricted = true
|
||||||
|
}
|
||||||
|
|
||||||
|
val filteredItems = filterItems(items).sorted()
|
||||||
|
val largest: Long = filteredItems.maxOfOrNull { it.total } ?: 0
|
||||||
|
return filteredItems.map { item ->
|
||||||
|
val percentTotal = if (largest > 0) (item.total * 100 / largest).toInt() else 0
|
||||||
|
item to percentTotal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun filterItems(items: List<AppItem>): List<AppItem> {
|
||||||
|
// When there is no specified SubscriptionInfo, Wi-Fi data usage will be displayed.
|
||||||
|
// In this case, the carrier service package also needs to be hidden.
|
||||||
|
if (carrierId != null && carrierId !in context.resources.getIntArray(
|
||||||
|
R.array.datausage_hiding_carrier_service_carrier_id
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
val hiddenPackageNames = context.resources.getStringArray(
|
||||||
|
R.array.datausage_hiding_carrier_service_package_names
|
||||||
|
)
|
||||||
|
return items.filter { item ->
|
||||||
|
// Do not show carrier service package in data usage list if it should be hidden for
|
||||||
|
// the carrier.
|
||||||
|
getPackageName(item) !in hiddenPackageNames
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindStats(
|
||||||
|
buckets: List<Bucket>,
|
||||||
|
profiles: MutableList<UserHandle>,
|
||||||
|
knownItems: SparseArray<AppItem>,
|
||||||
|
items: ArrayList<AppItem>,
|
||||||
|
) {
|
||||||
|
for (bucket in buckets) {
|
||||||
|
// Decide how to collapse items together
|
||||||
|
val uid = bucket.uid
|
||||||
|
val collapseKey: Int
|
||||||
|
val category: Int
|
||||||
|
val userId = UserHandle.getUserId(uid)
|
||||||
|
if (UserHandle.isApp(uid) || Process.isSdkSandboxUid(uid)) {
|
||||||
|
if (profiles.contains(UserHandle(userId))) {
|
||||||
|
if (userId != currentUserId) {
|
||||||
|
// Add to a managed user item.
|
||||||
|
accumulate(
|
||||||
|
collapseKey = UidDetailProvider.buildKeyForUser(userId),
|
||||||
|
knownItems = knownItems,
|
||||||
|
bucket = bucket,
|
||||||
|
itemCategory = AppItem.CATEGORY_USER,
|
||||||
|
items = items,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Map SDK sandbox back to its corresponding app
|
||||||
|
collapseKey = if (Process.isSdkSandboxUid(uid)) {
|
||||||
|
Process.getAppUidForSdkSandboxUid(uid)
|
||||||
|
} else {
|
||||||
|
uid
|
||||||
|
}
|
||||||
|
category = AppItem.CATEGORY_APP
|
||||||
|
} else {
|
||||||
|
// If it is a removed user add it to the removed users' key
|
||||||
|
if (context.userManager.getUserInfo(userId) == null) {
|
||||||
|
collapseKey = NetworkStats.Bucket.UID_REMOVED
|
||||||
|
category = AppItem.CATEGORY_APP
|
||||||
|
} else {
|
||||||
|
// Add to other user item.
|
||||||
|
collapseKey = UidDetailProvider.buildKeyForUser(userId)
|
||||||
|
category = AppItem.CATEGORY_USER
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (uid == NetworkStats.Bucket.UID_REMOVED ||
|
||||||
|
uid == NetworkStats.Bucket.UID_TETHERING ||
|
||||||
|
uid == Process.OTA_UPDATE_UID
|
||||||
|
) {
|
||||||
|
collapseKey = uid
|
||||||
|
category = AppItem.CATEGORY_APP
|
||||||
|
} else {
|
||||||
|
collapseKey = Process.SYSTEM_UID
|
||||||
|
category = AppItem.CATEGORY_APP
|
||||||
|
}
|
||||||
|
accumulate(
|
||||||
|
collapseKey = collapseKey,
|
||||||
|
knownItems = knownItems,
|
||||||
|
bucket = bucket,
|
||||||
|
itemCategory = category,
|
||||||
|
items = items,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accumulate data usage of a network stats entry for the item mapped by the collapse key.
|
||||||
|
* Creates the item if needed.
|
||||||
|
*
|
||||||
|
* @param collapseKey the collapse key used to map the item.
|
||||||
|
* @param knownItems collection of known (already existing) items.
|
||||||
|
* @param bucket the network stats bucket to extract data usage from.
|
||||||
|
* @param itemCategory the item is categorized on the list view by this category. Must be
|
||||||
|
*/
|
||||||
|
private fun accumulate(
|
||||||
|
collapseKey: Int,
|
||||||
|
knownItems: SparseArray<AppItem>,
|
||||||
|
bucket: Bucket,
|
||||||
|
itemCategory: Int,
|
||||||
|
items: ArrayList<AppItem>,
|
||||||
|
) {
|
||||||
|
var item = knownItems[collapseKey]
|
||||||
|
if (item == null) {
|
||||||
|
item = AppItem(collapseKey)
|
||||||
|
item.category = itemCategory
|
||||||
|
items.add(item)
|
||||||
|
knownItems.put(item.key, item)
|
||||||
|
}
|
||||||
|
item.addUid(bucket.uid)
|
||||||
|
item.total += bucket.bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun convertToBuckets(stats: NetworkStats): List<Bucket> {
|
||||||
|
val buckets = mutableListOf<Bucket>()
|
||||||
|
stats.use {
|
||||||
|
val bucket = NetworkStats.Bucket()
|
||||||
|
while (it.getNextBucket(bucket)) {
|
||||||
|
buckets += Bucket(uid = bucket.uid, bytes = bucket.rxBytes + bucket.txBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buckets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,131 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2023 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.datausage.lib
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.UserInfo
|
||||||
|
import android.content.res.Resources
|
||||||
|
import android.net.NetworkPolicyManager
|
||||||
|
import android.os.UserHandle
|
||||||
|
import android.os.UserManager
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import com.android.settings.R
|
||||||
|
import com.android.settings.datausage.lib.AppDataUsageRepository.Bucket
|
||||||
|
import com.android.settingslib.AppItem
|
||||||
|
import com.android.settingslib.spaprivileged.framework.common.userManager
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.junit.MockitoJUnit
|
||||||
|
import org.mockito.junit.MockitoRule
|
||||||
|
import org.mockito.kotlin.doReturn
|
||||||
|
import org.mockito.kotlin.mock
|
||||||
|
import org.mockito.kotlin.spy
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class AppDataUsageRepositoryTest {
|
||||||
|
@get:Rule
|
||||||
|
val mockito: MockitoRule = MockitoJUnit.rule()
|
||||||
|
|
||||||
|
private val mockUserManager = mock<UserManager> {
|
||||||
|
on { userProfiles } doReturn listOf(UserHandle.of(USER_ID))
|
||||||
|
on { getUserInfo(USER_ID) } doReturn UserInfo(USER_ID, "", 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val mockNetworkPolicyManager = mock<NetworkPolicyManager> {
|
||||||
|
on { getUidsWithPolicy(NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND) } doReturn
|
||||||
|
intArrayOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val mockResources = mock<Resources> {
|
||||||
|
on { getIntArray(R.array.datausage_hiding_carrier_service_carrier_id) } doReturn
|
||||||
|
intArrayOf(HIDING_CARRIER_ID)
|
||||||
|
|
||||||
|
on { getStringArray(R.array.datausage_hiding_carrier_service_package_names) } doReturn
|
||||||
|
arrayOf(HIDING_PACKAGE_NAME)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {
|
||||||
|
on { userManager } doReturn mockUserManager
|
||||||
|
on { getSystemService(NetworkPolicyManager::class.java) } doReturn mockNetworkPolicyManager
|
||||||
|
on { resources } doReturn mockResources
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAppPercent_noAppToHide() {
|
||||||
|
val repository = AppDataUsageRepository(
|
||||||
|
context = context,
|
||||||
|
currentUserId = USER_ID,
|
||||||
|
carrierId = null,
|
||||||
|
getPackageName = { "" },
|
||||||
|
)
|
||||||
|
val buckets = listOf(
|
||||||
|
Bucket(uid = APP_ID_1, bytes = 1),
|
||||||
|
Bucket(uid = APP_ID_2, bytes = 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
val appPercentList = repository.getAppPercent(buckets)
|
||||||
|
|
||||||
|
assertThat(appPercentList).hasSize(2)
|
||||||
|
appPercentList[0].first.apply {
|
||||||
|
assertThat(key).isEqualTo(APP_ID_2)
|
||||||
|
assertThat(category).isEqualTo(AppItem.CATEGORY_APP)
|
||||||
|
assertThat(total).isEqualTo(2)
|
||||||
|
}
|
||||||
|
assertThat(appPercentList[0].second).isEqualTo(100)
|
||||||
|
appPercentList[1].first.apply {
|
||||||
|
assertThat(key).isEqualTo(APP_ID_1)
|
||||||
|
assertThat(category).isEqualTo(AppItem.CATEGORY_APP)
|
||||||
|
assertThat(total).isEqualTo(1)
|
||||||
|
}
|
||||||
|
assertThat(appPercentList[1].second).isEqualTo(50)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun getAppPercent_hasAppToHide() {
|
||||||
|
val repository = AppDataUsageRepository(
|
||||||
|
context = context,
|
||||||
|
currentUserId = USER_ID,
|
||||||
|
carrierId = HIDING_CARRIER_ID,
|
||||||
|
getPackageName = { if (it.key == APP_ID_1) HIDING_PACKAGE_NAME else "" },
|
||||||
|
)
|
||||||
|
val buckets = listOf(
|
||||||
|
Bucket(uid = APP_ID_1, bytes = 1),
|
||||||
|
Bucket(uid = APP_ID_2, bytes = 2),
|
||||||
|
)
|
||||||
|
|
||||||
|
val appPercentList = repository.getAppPercent(buckets)
|
||||||
|
|
||||||
|
assertThat(appPercentList).hasSize(1)
|
||||||
|
appPercentList[0].first.apply {
|
||||||
|
assertThat(key).isEqualTo(APP_ID_2)
|
||||||
|
assertThat(category).isEqualTo(AppItem.CATEGORY_APP)
|
||||||
|
assertThat(total).isEqualTo(2)
|
||||||
|
}
|
||||||
|
assertThat(appPercentList[0].second).isEqualTo(100)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val USER_ID = 1
|
||||||
|
const val APP_ID_1 = 110001
|
||||||
|
const val APP_ID_2 = 110002
|
||||||
|
const val HIDING_CARRIER_ID = 4
|
||||||
|
const val HIDING_PACKAGE_NAME = "hiding.package.name"
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user