diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index dbb894f92f55e..a46c410bd55ef 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -15,9 +15,16 @@ */ package android.net; +import static android.Manifest.permission.NETWORK_STACK; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; + +import android.annotation.NonNull; import android.annotation.SystemApi; import android.annotation.TestApi; +import android.content.Context; +import java.util.ArrayList; +import java.util.Arrays; /** * * Constants for client code communicating with the network stack service. @@ -37,4 +44,52 @@ public class NetworkStack { "android.permission.MAINLINE_NETWORK_STACK"; private NetworkStack() {} + + /** + * If the NetworkStack, MAINLINE_NETWORK_STACK are not allowed for a particular process, throw a + * {@link SecurityException}. + * + * @param context {@link android.content.Context} for the process. + * + * @hide + */ + public static void checkNetworkStackPermission(final @NonNull Context context) { + checkNetworkStackPermissionOr(context); + } + + /** + * If the NetworkStack, MAINLINE_NETWORK_STACK or other specified permissions are not allowed + * for a particular process, throw a {@link SecurityException}. + * + * @param context {@link android.content.Context} for the process. + * @param otherPermissions The set of permissions that could be the candidate permissions , or + * empty string if none of other permissions needed. + * @hide + */ + public static void checkNetworkStackPermissionOr(final @NonNull Context context, + final @NonNull String... otherPermissions) { + ArrayList permissions = new ArrayList(Arrays.asList(otherPermissions)); + permissions.add(NETWORK_STACK); + permissions.add(PERMISSION_MAINLINE_NETWORK_STACK); + enforceAnyPermissionOf(context, permissions.toArray(new String[0])); + } + + private static void enforceAnyPermissionOf(final @NonNull Context context, + final @NonNull String... permissions) { + if (!checkAnyPermissionOf(context, permissions)) { + throw new SecurityException("Requires one of the following permissions: " + + String.join(", ", permissions) + "."); + } + } + + private static boolean checkAnyPermissionOf(final @NonNull Context context, + final @NonNull String... permissions) { + for (String permission : permissions) { + if (context.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) { + return true; + } + } + return false; + } + } diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index f237c4bca57b1..484efd6bfd748 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -25,6 +25,7 @@ import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.isNetworkTypeMobile; +import static android.net.NetworkStack.checkNetworkStackPermission; import static android.net.NetworkStats.DEFAULT_NETWORK_ALL; import static android.net.NetworkStats.IFACE_ALL; import static android.net.NetworkStats.INTERFACES_ALL; @@ -899,7 +900,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { VpnInfo[] vpnArray, NetworkState[] networkStates, String activeIface) { - mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG); + checkNetworkStackPermission(mContext); assertBandwidthControlEnabled(); final long token = Binder.clearCallingIdentity(); diff --git a/tests/net/java/android/net/NetworkStackTest.java b/tests/net/java/android/net/NetworkStackTest.java new file mode 100644 index 0000000000000..f7c6c99ba6225 --- /dev/null +++ b/tests/net/java/android/net/NetworkStackTest.java @@ -0,0 +1,75 @@ +/* + * 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 android.net; + +import static android.Manifest.permission.NETWORK_STACK; +import static android.content.pm.PackageManager.PERMISSION_DENIED; +import static android.content.pm.PackageManager.PERMISSION_GRANTED; +import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK; +import static android.net.NetworkStack.checkNetworkStackPermission; +import static android.net.NetworkStack.checkNetworkStackPermissionOr; + +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +@RunWith(AndroidJUnit4.class) +public class NetworkStackTest { + private static final String [] OTHER_PERMISSION = {"otherpermission1", "otherpermission2"}; + + @Mock Context mCtx; + + @Before public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + } + + @Test + public void testCheckNetworkStackPermission() throws Exception { + when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_GRANTED); + when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK))) + .thenReturn(PERMISSION_DENIED); + checkNetworkStackPermission(mCtx); + checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); + + when(mCtx.checkCallingOrSelfPermission(eq(NETWORK_STACK))).thenReturn(PERMISSION_DENIED); + when(mCtx.checkCallingOrSelfPermission(eq(PERMISSION_MAINLINE_NETWORK_STACK))) + .thenReturn(PERMISSION_GRANTED); + checkNetworkStackPermission(mCtx); + checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); + + when(mCtx.checkCallingOrSelfPermission(any())).thenReturn(PERMISSION_DENIED); + + try { + checkNetworkStackPermissionOr(mCtx, OTHER_PERMISSION); + } catch (SecurityException e) { + // Expect to get a SecurityException + return; + } + + fail("Expect fail but permission granted."); + } +}