Relax permissions around NetworkStatsManager APIs.
Currently, access to network usage history and statistics requires a signature|privileged permission, an AppOps bit (associated with the PACKAGE_USAGE_STATS permission), or device/profile ownership. Once access is granted via one of these mechanisms, it generally applies to any UID running in the same user as the caller. This CL expands access as follows: -Any app can access its own usage history with no extra requirements. -Carrier-privileged applications can access usage history for the entire device. -Device owners can access per-UID breakdowns for usage. Previously they could access the summary for the whole device, but not the individual breakdowns. We simplify the permission model by defining three access levels - DEFAULT (own app only), USER (all apps in the same user), and DEVICE (all apps on the device), and propagate these levels throughout. Finally, this CL fixes an apparent bug in NetworkStatsSerice#hasAppOpsPermissions - if the AppOp bit was in MODE_DEFAULT, hasAppOpsPermission would always return false instead of falling back to the PackageManager permission check. Bug: 25812859 Bug: 25813856 Change-Id: Ic96e0776e2a4215a400163872acea1ededfaced9
This commit is contained in:
@@ -0,0 +1,178 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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.net;
|
||||||
|
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.Manifest.permission;
|
||||||
|
import android.app.AppOpsManager;
|
||||||
|
import android.app.admin.DeviceAdminInfo;
|
||||||
|
import android.app.admin.DevicePolicyManagerInternal;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
|
|
||||||
|
import com.android.server.LocalServices;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
public class NetworkStatsAccessTest extends TestCase {
|
||||||
|
private static final String TEST_PKG = "com.example.test";
|
||||||
|
private static final int TEST_UID = 12345;
|
||||||
|
|
||||||
|
@Mock private Context mContext;
|
||||||
|
@Mock private DevicePolicyManagerInternal mDpmi;
|
||||||
|
@Mock private TelephonyManager mTm;
|
||||||
|
@Mock private AppOpsManager mAppOps;
|
||||||
|
|
||||||
|
// Hold the real service so we can restore it when tearing down the test.
|
||||||
|
private DevicePolicyManagerInternal mSystemDpmi;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
super.setUp();
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
|
||||||
|
mSystemDpmi = LocalServices.getService(DevicePolicyManagerInternal.class);
|
||||||
|
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
|
||||||
|
LocalServices.addService(DevicePolicyManagerInternal.class, mDpmi);
|
||||||
|
|
||||||
|
when(mContext.getSystemService(Context.TELEPHONY_SERVICE)).thenReturn(mTm);
|
||||||
|
when(mContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tearDown() throws Exception {
|
||||||
|
LocalServices.removeServiceForTest(DevicePolicyManagerInternal.class);
|
||||||
|
LocalServices.addService(DevicePolicyManagerInternal.class, mSystemDpmi);
|
||||||
|
super.tearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCheckAccessLevel_hasCarrierPrivileges() throws Exception {
|
||||||
|
setHasCarrierPrivileges(true);
|
||||||
|
setIsDeviceOwner(false);
|
||||||
|
setIsProfileOwner(false);
|
||||||
|
setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
|
||||||
|
setHasReadHistoryPermission(false);
|
||||||
|
assertEquals(NetworkStatsAccess.Level.DEVICE,
|
||||||
|
NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCheckAccessLevel_isDeviceOwner() throws Exception {
|
||||||
|
setHasCarrierPrivileges(false);
|
||||||
|
setIsDeviceOwner(true);
|
||||||
|
setIsProfileOwner(false);
|
||||||
|
setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
|
||||||
|
setHasReadHistoryPermission(false);
|
||||||
|
assertEquals(NetworkStatsAccess.Level.DEVICE,
|
||||||
|
NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCheckAccessLevel_isProfileOwner() throws Exception {
|
||||||
|
setHasCarrierPrivileges(false);
|
||||||
|
setIsDeviceOwner(false);
|
||||||
|
setIsProfileOwner(true);
|
||||||
|
setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
|
||||||
|
setHasReadHistoryPermission(false);
|
||||||
|
assertEquals(NetworkStatsAccess.Level.USER,
|
||||||
|
NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCheckAccessLevel_hasAppOpsBitAllowed() throws Exception {
|
||||||
|
setHasCarrierPrivileges(false);
|
||||||
|
setIsDeviceOwner(false);
|
||||||
|
setIsProfileOwner(true);
|
||||||
|
setHasAppOpsPermission(AppOpsManager.MODE_ALLOWED, false);
|
||||||
|
setHasReadHistoryPermission(false);
|
||||||
|
assertEquals(NetworkStatsAccess.Level.USER,
|
||||||
|
NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCheckAccessLevel_hasAppOpsBitDefault_grantedPermission() throws Exception {
|
||||||
|
setHasCarrierPrivileges(false);
|
||||||
|
setIsDeviceOwner(false);
|
||||||
|
setIsProfileOwner(true);
|
||||||
|
setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, true);
|
||||||
|
setHasReadHistoryPermission(false);
|
||||||
|
assertEquals(NetworkStatsAccess.Level.USER,
|
||||||
|
NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCheckAccessLevel_hasReadHistoryPermission() throws Exception {
|
||||||
|
setHasCarrierPrivileges(false);
|
||||||
|
setIsDeviceOwner(false);
|
||||||
|
setIsProfileOwner(true);
|
||||||
|
setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
|
||||||
|
setHasReadHistoryPermission(true);
|
||||||
|
assertEquals(NetworkStatsAccess.Level.USER,
|
||||||
|
NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCheckAccessLevel_deniedAppOpsBit() throws Exception {
|
||||||
|
setHasCarrierPrivileges(false);
|
||||||
|
setIsDeviceOwner(false);
|
||||||
|
setIsProfileOwner(false);
|
||||||
|
setHasAppOpsPermission(AppOpsManager.MODE_ERRORED, true);
|
||||||
|
setHasReadHistoryPermission(false);
|
||||||
|
assertEquals(NetworkStatsAccess.Level.DEFAULT,
|
||||||
|
NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testCheckAccessLevel_deniedAppOpsBit_deniedPermission() throws Exception {
|
||||||
|
setHasCarrierPrivileges(false);
|
||||||
|
setIsDeviceOwner(false);
|
||||||
|
setIsProfileOwner(false);
|
||||||
|
setHasAppOpsPermission(AppOpsManager.MODE_DEFAULT, false);
|
||||||
|
setHasReadHistoryPermission(false);
|
||||||
|
assertEquals(NetworkStatsAccess.Level.DEFAULT,
|
||||||
|
NetworkStatsAccess.checkAccessLevel(mContext, TEST_UID, TEST_PKG));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setHasCarrierPrivileges(boolean hasPrivileges) {
|
||||||
|
when(mTm.checkCarrierPrivilegesForPackage(TEST_PKG)).thenReturn(
|
||||||
|
hasPrivileges ? TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS
|
||||||
|
: TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setIsDeviceOwner(boolean isOwner) {
|
||||||
|
when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_DEVICE_OWNER))
|
||||||
|
.thenReturn(isOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setIsProfileOwner(boolean isOwner) {
|
||||||
|
when(mDpmi.isActiveAdminWithPolicy(TEST_UID, DeviceAdminInfo.USES_POLICY_PROFILE_OWNER))
|
||||||
|
.thenReturn(isOwner);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setHasAppOpsPermission(int appOpsMode, boolean hasPermission) {
|
||||||
|
when(mAppOps.checkOp(AppOpsManager.OP_GET_USAGE_STATS, TEST_UID, TEST_PKG))
|
||||||
|
.thenReturn(appOpsMode);
|
||||||
|
when(mContext.checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)).thenReturn(
|
||||||
|
hasPermission ? PackageManager.PERMISSION_GRANTED
|
||||||
|
: PackageManager.PERMISSION_DENIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setHasReadHistoryPermission(boolean hasPermission) {
|
||||||
|
when(mContext.checkCallingOrSelfPermission(permission.READ_NETWORK_USAGE_HISTORY))
|
||||||
|
.thenReturn(hasPermission ? PackageManager.PERMISSION_GRANTED
|
||||||
|
: PackageManager.PERMISSION_DENIED);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package com.android.server.net;
|
package com.android.server.net;
|
||||||
|
|
||||||
|
import static android.net.ConnectivityManager.TYPE_MOBILE;
|
||||||
import static android.net.NetworkStats.SET_DEFAULT;
|
import static android.net.NetworkStats.SET_DEFAULT;
|
||||||
import static android.net.NetworkStats.TAG_NONE;
|
import static android.net.NetworkStats.TAG_NONE;
|
||||||
import static android.net.NetworkStats.UID_ALL;
|
import static android.net.NetworkStats.UID_ALL;
|
||||||
@@ -24,9 +25,14 @@ import static android.text.format.DateUtils.HOUR_IN_MILLIS;
|
|||||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||||
|
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.net.NetworkIdentity;
|
||||||
import android.net.NetworkStats;
|
import android.net.NetworkStats;
|
||||||
import android.net.NetworkTemplate;
|
import android.net.NetworkTemplate;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
import android.test.AndroidTestCase;
|
import android.test.AndroidTestCase;
|
||||||
|
import android.test.MoreAsserts;
|
||||||
import android.test.suitebuilder.annotation.MediumTest;
|
import android.test.suitebuilder.annotation.MediumTest;
|
||||||
|
|
||||||
import com.android.frameworks.servicestests.R;
|
import com.android.frameworks.servicestests.R;
|
||||||
@@ -68,7 +74,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase {
|
|||||||
|
|
||||||
// verify that history read correctly
|
// verify that history read correctly
|
||||||
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
||||||
636016770L, 709306L, 88038768L, 518836L);
|
636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
|
||||||
|
|
||||||
// now export into a unified format
|
// now export into a unified format
|
||||||
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
@@ -77,12 +83,12 @@ public class NetworkStatsCollectionTest extends AndroidTestCase {
|
|||||||
// clear structure completely
|
// clear structure completely
|
||||||
collection.reset();
|
collection.reset();
|
||||||
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
||||||
0L, 0L, 0L, 0L);
|
0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE);
|
||||||
|
|
||||||
// and read back into structure, verifying that totals are same
|
// and read back into structure, verifying that totals are same
|
||||||
collection.read(new ByteArrayInputStream(bos.toByteArray()));
|
collection.read(new ByteArrayInputStream(bos.toByteArray()));
|
||||||
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
||||||
636016770L, 709306L, 88038768L, 518836L);
|
636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadLegacyUid() throws Exception {
|
public void testReadLegacyUid() throws Exception {
|
||||||
@@ -94,7 +100,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase {
|
|||||||
|
|
||||||
// verify that history read correctly
|
// verify that history read correctly
|
||||||
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
||||||
637076152L, 711413L, 88343717L, 521022L);
|
637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
|
||||||
|
|
||||||
// now export into a unified format
|
// now export into a unified format
|
||||||
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
@@ -103,12 +109,12 @@ public class NetworkStatsCollectionTest extends AndroidTestCase {
|
|||||||
// clear structure completely
|
// clear structure completely
|
||||||
collection.reset();
|
collection.reset();
|
||||||
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
||||||
0L, 0L, 0L, 0L);
|
0L, 0L, 0L, 0L, NetworkStatsAccess.Level.DEVICE);
|
||||||
|
|
||||||
// and read back into structure, verifying that totals are same
|
// and read back into structure, verifying that totals are same
|
||||||
collection.read(new ByteArrayInputStream(bos.toByteArray()));
|
collection.read(new ByteArrayInputStream(bos.toByteArray()));
|
||||||
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI),
|
||||||
637076152L, 711413L, 88343717L, 521022L);
|
637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testReadLegacyUidTags() throws Exception {
|
public void testReadLegacyUidTags() throws Exception {
|
||||||
@@ -151,6 +157,66 @@ public class NetworkStatsCollectionTest extends AndroidTestCase {
|
|||||||
assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis());
|
assertEquals(2 * HOUR_IN_MILLIS, collection.getEndMillis());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testAccessLevels() throws Exception {
|
||||||
|
final NetworkStatsCollection collection = new NetworkStatsCollection(HOUR_IN_MILLIS);
|
||||||
|
final NetworkStats.Entry entry = new NetworkStats.Entry();
|
||||||
|
final NetworkIdentitySet identSet = new NetworkIdentitySet();
|
||||||
|
identSet.add(new NetworkIdentity(TYPE_MOBILE, TelephonyManager.NETWORK_TYPE_UNKNOWN,
|
||||||
|
TEST_IMSI, null, false));
|
||||||
|
|
||||||
|
int myUid = Process.myUid();
|
||||||
|
int otherUidInSameUser = Process.myUid() + 1;
|
||||||
|
int uidInDifferentUser = Process.myUid() + UserHandle.PER_USER_RANGE;
|
||||||
|
|
||||||
|
// Record one entry for the current UID.
|
||||||
|
entry.rxBytes = 32;
|
||||||
|
collection.recordData(identSet, myUid, SET_DEFAULT, TAG_NONE, 0, 60 * MINUTE_IN_MILLIS,
|
||||||
|
entry);
|
||||||
|
|
||||||
|
// Record one entry for another UID in this user.
|
||||||
|
entry.rxBytes = 64;
|
||||||
|
collection.recordData(identSet, otherUidInSameUser, SET_DEFAULT, TAG_NONE, 0,
|
||||||
|
60 * MINUTE_IN_MILLIS, entry);
|
||||||
|
|
||||||
|
// Record one entry for the system UID.
|
||||||
|
entry.rxBytes = 128;
|
||||||
|
collection.recordData(identSet, Process.SYSTEM_UID, SET_DEFAULT, TAG_NONE, 0,
|
||||||
|
60 * MINUTE_IN_MILLIS, entry);
|
||||||
|
|
||||||
|
// Record one entry for a UID in a different user.
|
||||||
|
entry.rxBytes = 256;
|
||||||
|
collection.recordData(identSet, uidInDifferentUser, SET_DEFAULT, TAG_NONE, 0,
|
||||||
|
60 * MINUTE_IN_MILLIS, entry);
|
||||||
|
|
||||||
|
// Verify the set of relevant UIDs for each access level.
|
||||||
|
MoreAsserts.assertEquals(new int[] { myUid },
|
||||||
|
collection.getRelevantUids(NetworkStatsAccess.Level.DEFAULT));
|
||||||
|
MoreAsserts.assertEquals(new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser },
|
||||||
|
collection.getRelevantUids(NetworkStatsAccess.Level.USER));
|
||||||
|
MoreAsserts.assertEquals(
|
||||||
|
new int[] { Process.SYSTEM_UID, myUid, otherUidInSameUser, uidInDifferentUser },
|
||||||
|
collection.getRelevantUids(NetworkStatsAccess.Level.DEVICE));
|
||||||
|
|
||||||
|
// Verify security check in getHistory.
|
||||||
|
assertNotNull(collection.getHistory(buildTemplateMobileAll(TEST_IMSI), myUid, SET_DEFAULT,
|
||||||
|
TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT));
|
||||||
|
try {
|
||||||
|
collection.getHistory(buildTemplateMobileAll(TEST_IMSI), otherUidInSameUser,
|
||||||
|
SET_DEFAULT, TAG_NONE, 0, NetworkStatsAccess.Level.DEFAULT);
|
||||||
|
fail("Should have thrown SecurityException for accessing different UID");
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify appropriate aggregation in getSummary.
|
||||||
|
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32, 0, 0, 0,
|
||||||
|
NetworkStatsAccess.Level.DEFAULT);
|
||||||
|
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128, 0, 0, 0,
|
||||||
|
NetworkStatsAccess.Level.USER);
|
||||||
|
assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), 32 + 64 + 128 + 256, 0, 0,
|
||||||
|
0, NetworkStatsAccess.Level.DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy a {@link Resources#openRawResource(int)} into {@link File} for
|
* Copy a {@link Resources#openRawResource(int)} into {@link File} for
|
||||||
* testing purposes.
|
* testing purposes.
|
||||||
@@ -170,16 +236,19 @@ public class NetworkStatsCollectionTest extends AndroidTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void assertSummaryTotal(NetworkStatsCollection collection,
|
private static void assertSummaryTotal(NetworkStatsCollection collection,
|
||||||
NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
|
NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets,
|
||||||
|
@NetworkStatsAccess.Level int accessLevel) {
|
||||||
final NetworkStats.Entry entry = collection.getSummary(
|
final NetworkStats.Entry entry = collection.getSummary(
|
||||||
template, Long.MIN_VALUE, Long.MAX_VALUE).getTotal(null);
|
template, Long.MIN_VALUE, Long.MAX_VALUE, accessLevel)
|
||||||
|
.getTotal(null);
|
||||||
assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
|
assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection,
|
private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection,
|
||||||
NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
|
NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) {
|
||||||
final NetworkStats.Entry entry = collection.getSummary(
|
final NetworkStats.Entry entry = collection.getSummary(
|
||||||
template, Long.MIN_VALUE, Long.MAX_VALUE).getTotalIncludingTags(null);
|
template, Long.MIN_VALUE, Long.MAX_VALUE, NetworkStatsAccess.Level.DEVICE)
|
||||||
|
.getTotalIncludingTags(null);
|
||||||
assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
|
assertEntry(entry, rxBytes, rxPackets, txBytes, txPackets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user