From b9626ecd404e088e2a62af2b468b4c4de83345cd Mon Sep 17 00:00:00 2001 From: Aaron Huang Date: Fri, 17 Apr 2020 05:11:01 +0000 Subject: [PATCH] This contains a squash of two changes >>>>>>>>>>>>>>>>>>>>>> aosp/1284588 Adjust permission of NetworkProvider related API - Allow an app holds NETWORK_SETTINGS to acess registerNetworkProvier() and unregisterNetworkProvider(). - To access declareNetworkRequestUnfulfillable(), allow an app holds MANAGE_TEST_NETWORKS to declare a unfulfillable request that contains TRANSPORT_TEST transport. This makes easier to write cts to test. >>>>>>>>>>>>>>>>>>>>>> aosp/1285957 Add cts test for NetworkProvider It will skip whole tests on Q device since NetworkProvider class is introduced in R. Result on Q device would be: [1/1] android.net.NetworkProviderTest#skippedClassForDevSdkMismatch: IGNORED (3ms) >>>>>>>>>>>>>>>>>>>>>> Bug: 153614605 Bug: 153613690 Bug: 153612373 Test: atest FrameworksNetTests atest CtsNetTestCases:android.net.NetworkProviderTest Test: atest CtsNetTestCasesLatestSdk:android.net.NetworkProviderTest Change-Id: Ib6f42b8f0e94e8c2715a030587e065864edff25b Merged-In: Ic9809e731aa811a51c2f82d189372169d99a5ed9 Merged-In: If7bfc7fae503e3497c37754697d0b148ff4cab3b (cherry picked from commit 10138d42a8f3892fcdb129a39409efe42873f6fe) --- .../android/server/ConnectivityService.java | 24 ++- .../java/android/net/NetworkProviderTest.kt | 158 ++++++++++++++++++ 2 files changed, 179 insertions(+), 3 deletions(-) create mode 100644 tests/net/common/java/android/net/NetworkProviderTest.kt diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java index 001db3bd6a..f1ea5d0184 100644 --- a/services/core/java/com/android/server/ConnectivityService.java +++ b/services/core/java/com/android/server/ConnectivityService.java @@ -2093,6 +2093,20 @@ public class ConnectivityService extends IConnectivityManager.Stub NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); } + private void enforceNetworkFactoryOrSettingsPermission() { + enforceAnyPermissionOf( + android.Manifest.permission.NETWORK_SETTINGS, + android.Manifest.permission.NETWORK_FACTORY, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); + } + + private void enforceNetworkFactoryOrTestNetworksPermission() { + enforceAnyPermissionOf( + android.Manifest.permission.MANAGE_TEST_NETWORKS, + android.Manifest.permission.NETWORK_FACTORY, + NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK); + } + private boolean checkSettingsPermission() { return checkAnyPermissionOf( android.Manifest.permission.NETWORK_SETTINGS, @@ -5683,7 +5697,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public int registerNetworkProvider(Messenger messenger, String name) { - enforceNetworkFactoryPermission(); + enforceNetworkFactoryOrSettingsPermission(); NetworkProviderInfo npi = new NetworkProviderInfo(name, messenger, null /* asyncChannel */, nextNetworkProviderId(), () -> unregisterNetworkProvider(messenger)); @@ -5693,7 +5707,7 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void unregisterNetworkProvider(Messenger messenger) { - enforceNetworkFactoryPermission(); + enforceNetworkFactoryOrSettingsPermission(); mHandler.sendMessage(mHandler.obtainMessage(EVENT_UNREGISTER_NETWORK_PROVIDER, messenger)); } @@ -5713,7 +5727,11 @@ public class ConnectivityService extends IConnectivityManager.Stub @Override public void declareNetworkRequestUnfulfillable(NetworkRequest request) { - enforceNetworkFactoryPermission(); + if (request.hasTransport(TRANSPORT_TEST)) { + enforceNetworkFactoryOrTestNetworksPermission(); + } else { + enforceNetworkFactoryPermission(); + } mHandler.post(() -> handleReleaseNetworkRequest(request, Binder.getCallingUid(), true)); } diff --git a/tests/net/common/java/android/net/NetworkProviderTest.kt b/tests/net/common/java/android/net/NetworkProviderTest.kt new file mode 100644 index 0000000000..4601c4bf4a --- /dev/null +++ b/tests/net/common/java/android/net/NetworkProviderTest.kt @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2020 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 android.app.Instrumentation +import android.content.Context +import android.net.NetworkCapabilities.TRANSPORT_TEST +import android.os.Build +import android.os.HandlerThread +import android.os.Looper +import android.net.NetworkProviderTest.TestNetworkCallback.CallbackEntry.OnUnavailable +import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequested +import android.net.NetworkProviderTest.TestNetworkProvider.CallbackEntry.OnNetworkRequestWithdrawn +import androidx.test.InstrumentationRegistry +import com.android.testutils.ArrayTrackRecord +import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo +import com.android.testutils.DevSdkIgnoreRunner +import java.util.UUID +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +private const val DEFAULT_TIMEOUT_MS = 5000L +private val instrumentation: Instrumentation + get() = InstrumentationRegistry.getInstrumentation() +private val context: Context get() = InstrumentationRegistry.getContext() +private val PROVIDER_NAME = "NetworkProviderTest" + +@RunWith(DevSdkIgnoreRunner::class) +@IgnoreUpTo(Build.VERSION_CODES.Q) +class NetworkProviderTest { + private val mCm = context.getSystemService(ConnectivityManager::class.java) + private val mHandlerThread = HandlerThread("${javaClass.simpleName} handler thread") + + @Before + fun setUp() { + instrumentation.getUiAutomation().adoptShellPermissionIdentity() + mHandlerThread.start() + } + + @After + fun tearDown() { + mHandlerThread.quitSafely() + instrumentation.getUiAutomation().dropShellPermissionIdentity() + } + + private class TestNetworkProvider(context: Context, looper: Looper) : + NetworkProvider(context, looper, PROVIDER_NAME) { + private val seenEvents = ArrayTrackRecord().newReadHead() + + sealed class CallbackEntry { + data class OnNetworkRequested( + val request: NetworkRequest, + val score: Int, + val id: Int + ) : CallbackEntry() + data class OnNetworkRequestWithdrawn(val request: NetworkRequest) : CallbackEntry() + } + + override fun onNetworkRequested(request: NetworkRequest, score: Int, id: Int) { + seenEvents.add(OnNetworkRequested(request, score, id)) + } + + override fun onNetworkRequestWithdrawn(request: NetworkRequest) { + seenEvents.add(OnNetworkRequestWithdrawn(request)) + } + + inline fun expectCallback( + crossinline predicate: (T) -> Boolean + ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) } + } + + private fun createNetworkProvider(): TestNetworkProvider { + return TestNetworkProvider(context, mHandlerThread.looper) + } + + @Test + fun testOnNetworkRequested() { + val provider = createNetworkProvider() + assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE) + mCm.registerNetworkProvider(provider) + assertNotEquals(provider.getProviderId(), NetworkProvider.ID_NONE) + + val specifier = StringNetworkSpecifier(UUID.randomUUID().toString()) + val nr: NetworkRequest = NetworkRequest.Builder() + .addTransportType(TRANSPORT_TEST) + .setNetworkSpecifier(specifier) + .build() + val cb = ConnectivityManager.NetworkCallback() + mCm.requestNetwork(nr, cb) + provider.expectCallback() { + callback -> callback.request.getNetworkSpecifier() == specifier && + callback.request.hasTransport(TRANSPORT_TEST) + } + + mCm.unregisterNetworkCallback(cb) + provider.expectCallback() { + callback -> callback.request.getNetworkSpecifier() == specifier && + callback.request.hasTransport(TRANSPORT_TEST) + } + mCm.unregisterNetworkProvider(provider) + // Provider id should be ID_NONE after unregister network provider + assertEquals(provider.getProviderId(), NetworkProvider.ID_NONE) + // unregisterNetworkProvider should not crash even if it's called on an + // already unregistered provider. + mCm.unregisterNetworkProvider(provider) + } + + private class TestNetworkCallback : ConnectivityManager.NetworkCallback() { + private val seenEvents = ArrayTrackRecord().newReadHead() + sealed class CallbackEntry { + object OnUnavailable : CallbackEntry() + } + + override fun onUnavailable() { + seenEvents.add(OnUnavailable) + } + + inline fun expectCallback( + crossinline predicate: (T) -> Boolean + ) = seenEvents.poll(DEFAULT_TIMEOUT_MS) { it is T && predicate(it) } + } + + @Test + fun testDeclareNetworkRequestUnfulfillable() { + val provider = createNetworkProvider() + mCm.registerNetworkProvider(provider) + + val specifier = StringNetworkSpecifier(UUID.randomUUID().toString()) + val nr: NetworkRequest = NetworkRequest.Builder() + .addTransportType(TRANSPORT_TEST) + .setNetworkSpecifier(specifier) + .build() + + val cb = TestNetworkCallback() + mCm.requestNetwork(nr, cb) + provider.declareNetworkRequestUnfulfillable(nr) + cb.expectCallback() { nr.getNetworkSpecifier() == specifier } + mCm.unregisterNetworkProvider(provider) + } +} \ No newline at end of file