diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java new file mode 100644 index 0000000000..bb8f9d1a7b --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsAccessTest.java @@ -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); + } +} diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java index 1a6c289316..6026644593 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkStatsCollectionTest.java @@ -16,6 +16,7 @@ package com.android.server.net; +import static android.net.ConnectivityManager.TYPE_MOBILE; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; 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 android.content.res.Resources; +import android.net.NetworkIdentity; import android.net.NetworkStats; import android.net.NetworkTemplate; +import android.os.Process; +import android.os.UserHandle; +import android.telephony.TelephonyManager; import android.test.AndroidTestCase; +import android.test.MoreAsserts; import android.test.suitebuilder.annotation.MediumTest; import com.android.frameworks.servicestests.R; @@ -68,7 +74,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { // verify that history read correctly assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 636016770L, 709306L, 88038768L, 518836L); + 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE); // now export into a unified format final ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -77,12 +83,12 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { // clear structure completely collection.reset(); 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 collection.read(new ByteArrayInputStream(bos.toByteArray())); assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 636016770L, 709306L, 88038768L, 518836L); + 636016770L, 709306L, 88038768L, 518836L, NetworkStatsAccess.Level.DEVICE); } public void testReadLegacyUid() throws Exception { @@ -94,7 +100,7 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { // verify that history read correctly assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 637076152L, 711413L, 88343717L, 521022L); + 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE); // now export into a unified format final ByteArrayOutputStream bos = new ByteArrayOutputStream(); @@ -103,12 +109,12 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { // clear structure completely collection.reset(); 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 collection.read(new ByteArrayInputStream(bos.toByteArray())); assertSummaryTotal(collection, buildTemplateMobileAll(TEST_IMSI), - 637076152L, 711413L, 88343717L, 521022L); + 637076152L, 711413L, 88343717L, 521022L, NetworkStatsAccess.Level.DEVICE); } public void testReadLegacyUidTags() throws Exception { @@ -151,6 +157,66 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { 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 * testing purposes. @@ -170,16 +236,19 @@ public class NetworkStatsCollectionTest extends AndroidTestCase { } 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( - 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); } private static void assertSummaryTotalIncludingTags(NetworkStatsCollection collection, NetworkTemplate template, long rxBytes, long rxPackets, long txBytes, long txPackets) { 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); }