Merge ab/7633965

Bug: 169893837
Merged-In: I592dd5f1d6e13b020beadb11b9d913857a82e524
Change-Id: I4e01d58ed61c595b704bae6c935d0da1714ee398
This commit is contained in:
The Android Open Source Project
2021-08-12 12:03:38 -07:00
committed by Xin Li
16 changed files with 413 additions and 77 deletions

View File

@@ -65,7 +65,7 @@ java_library {
"ServiceConnectivityResources",
],
static_libs: [
"dnsresolver_aidl_interface-V8-java",
"dnsresolver_aidl_interface-V9-java",
"modules-utils-os",
"net-utils-device-common",
"net-utils-framework-common",

View File

@@ -114,4 +114,15 @@
<!-- Whether to cancel network notifications automatically when tapped -->
<bool name="config_autoCancelNetworkNotifications">true</bool>
<!-- When no internet or partial connectivity is detected on a network, and a high priority
(heads up) notification would be shown due to the network being explicitly selected,
directly show the dialog that would normally be shown when tapping the notification
instead of showing the notification. -->
<bool name="config_notifyNoInternetAsDialogWhenHighPriority">false</bool>
<!-- When showing notifications indicating partial connectivity, display the same notifications
as no connectivity instead. This may be easier to understand for users but offers less
details on what is happening. -->
<bool name="config_partialConnectivityNotifiedAsNoInternet">false</bool>
</resources>

View File

@@ -32,6 +32,8 @@
<item type="array" name="config_networkNotifySwitches"/>
<item type="bool" name="config_ongoingSignInNotification"/>
<item type="bool" name="config_autoCancelNetworkNotifications"/>
<item type="bool" name="config_notifyNoInternetAsDialogWhenHighPriority"/>
<item type="bool" name="config_partialConnectivityNotifiedAsNoInternet"/>
<item type="drawable" name="stat_notify_wifi_in_range"/>
<item type="drawable" name="stat_notify_rssi_in_range"/>
</policy>

View File

@@ -324,7 +324,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
private static final int DEFAULT_NASCENT_DELAY_MS = 5_000;
// The maximum number of network request allowed per uid before an exception is thrown.
private static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
@VisibleForTesting
static final int MAX_NETWORK_REQUESTS_PER_UID = 100;
// The maximum number of network request allowed for system UIDs before an exception is thrown.
@VisibleForTesting
@@ -344,7 +345,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
@VisibleForTesting
protected final PermissionMonitor mPermissionMonitor;
private final PerUidCounter mNetworkRequestCounter;
@VisibleForTesting
final PerUidCounter mNetworkRequestCounter;
@VisibleForTesting
final PerUidCounter mSystemNetworkRequestCounter;
@@ -1154,9 +1156,20 @@ public class ConnectivityService extends IConnectivityManager.Stub
private void incrementCountOrThrow(final int uid, final int numToIncrement) {
final int newRequestCount =
mUidToNetworkRequestCount.get(uid, 0) + numToIncrement;
if (newRequestCount >= mMaxCountPerUid) {
if (newRequestCount >= mMaxCountPerUid
// HACK : the system server is allowed to go over the request count limit
// when it is creating requests on behalf of another app (but not itself,
// so it can still detect its own request leaks). This only happens in the
// per-app API flows in which case the old requests for that particular
// UID will be removed soon.
// TODO : instead of this hack, addPerAppDefaultNetworkRequests and other
// users of transact() should unregister the requests to decrease the count
// before they increase it again by creating a new NRI. Then remove the
// transact() method.
&& (Process.myUid() == uid || Process.myUid() != Binder.getCallingUid())) {
throw new ServiceSpecificException(
ConnectivityManager.Errors.TOO_MANY_REQUESTS);
ConnectivityManager.Errors.TOO_MANY_REQUESTS,
"Uid " + uid + " exceeded its allotted requests limit");
}
mUidToNetworkRequestCount.put(uid, newRequestCount);
}
@@ -5817,7 +5830,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
mUid = nri.mUid;
mAsUid = nri.mAsUid;
mPendingIntent = nri.mPendingIntent;
mPerUidCounter = getRequestCounter(this);
mPerUidCounter = nri.mPerUidCounter;
mPerUidCounter.incrementCountOrThrow(mUid);
mCallbackFlags = nri.mCallbackFlags;
mCallingAttributionTag = nri.mCallingAttributionTag;
@@ -10147,7 +10160,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
final NetworkRequestInfo trackingNri =
getDefaultRequestTrackingUid(callbackRequest.mAsUid);
// If this nri is not being tracked, the change it back to an untracked nri.
// If this nri is not being tracked, then change it back to an untracked nri.
if (trackingNri == mDefaultRequest) {
callbackRequestsToRegister.add(new NetworkRequestInfo(
callbackRequest,

View File

@@ -38,7 +38,6 @@ import android.net.IDnsResolver;
import android.net.InetAddresses;
import android.net.LinkProperties;
import android.net.Network;
import android.net.ResolverOptionsParcel;
import android.net.ResolverParamsParcel;
import android.net.Uri;
import android.net.shared.PrivateDnsConfig;
@@ -384,7 +383,6 @@ public class DnsManager {
.collect(Collectors.toList()))
: useTls ? paramsParcel.servers // Opportunistic
: new String[0]; // Off
paramsParcel.resolverOptions = new ResolverOptionsParcel();
paramsParcel.transportTypes = transportTypes;
// Prepare to track the validation status of the DNS servers in the
// resolver config when private DNS is in opportunistic or strict mode.

View File

@@ -108,10 +108,9 @@ public class FullScore {
// and all bits managed by FullScore unset. As bits are handled from 0 up in NetworkScore and
// from 63 down in FullScore, cut at the 32nd bit for simplicity, but change this if some day
// there are more than 32 bits handled on either side.
// YIELD_TO_BAD_WIFI is temporarily handled by ConnectivityService, but the factory is still
// allowed to set it, so that it's possible to transition from handling it in CS to handling
// it in the factory.
private static final long EXTERNAL_POLICIES_MASK = 0x00000000FFFFFFFFL;
// YIELD_TO_BAD_WIFI is temporarily handled by ConnectivityService.
private static final long EXTERNAL_POLICIES_MASK =
0x00000000FFFFFFFFL & ~(1L << POLICY_YIELD_TO_BAD_WIFI);
@VisibleForTesting
static @NonNull String policyNameOf(final int policy) {

View File

@@ -198,11 +198,22 @@ public class NetworkNotificationManager {
}
final Resources r = mResources.get();
if (highPriority && maybeNotifyViaDialog(r, notifyType, intent)) {
Log.d(TAG, "Notified via dialog for event " + nameOf(eventId));
return;
}
final CharSequence title;
final CharSequence details;
Icon icon = Icon.createWithResource(
mResources.getResourcesContext(), getIcon(transportType));
if (notifyType == NotificationType.NO_INTERNET && transportType == TRANSPORT_WIFI) {
final boolean showAsNoInternet = notifyType == NotificationType.PARTIAL_CONNECTIVITY
&& r.getBoolean(R.bool.config_partialConnectivityNotifiedAsNoInternet);
if (showAsNoInternet) {
Log.d(TAG, "Showing partial connectivity as NO_INTERNET");
}
if ((notifyType == NotificationType.NO_INTERNET || showAsNoInternet)
&& transportType == TRANSPORT_WIFI) {
title = r.getString(R.string.wifi_no_internet, name);
details = r.getString(R.string.wifi_no_internet_detailed);
} else if (notifyType == NotificationType.PRIVATE_DNS_BROKEN) {
@@ -306,6 +317,24 @@ public class NetworkNotificationManager {
}
}
private boolean maybeNotifyViaDialog(Resources res, NotificationType notifyType,
PendingIntent intent) {
if (notifyType != NotificationType.NO_INTERNET
&& notifyType != NotificationType.PARTIAL_CONNECTIVITY) {
return false;
}
if (!res.getBoolean(R.bool.config_notifyNoInternetAsDialogWhenHighPriority)) {
return false;
}
try {
intent.send();
} catch (PendingIntent.CanceledException e) {
Log.e(TAG, "Error sending dialog PendingIntent", e);
}
return true;
}
/**
* Clear the notification with the given id, only if it matches the given type.
*/

View File

@@ -31,6 +31,7 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.Network;
import android.net.cts.util.CtsNetUtils;
import android.net.wifi.WifiManager;
import android.os.BatteryStatsManager;
import android.os.Build;
import android.os.connectivity.CellularBatteryStats;
@@ -70,6 +71,7 @@ public class BatteryStatsManagerTest{
private Context mContext;
private BatteryStatsManager mBsm;
private ConnectivityManager mCm;
private WifiManager mWm;
private CtsNetUtils mCtsNetUtils;
@Before
@@ -77,6 +79,7 @@ public class BatteryStatsManagerTest{
mContext = getContext();
mBsm = mContext.getSystemService(BatteryStatsManager.class);
mCm = mContext.getSystemService(ConnectivityManager.class);
mWm = mContext.getSystemService(WifiManager.class);
mCtsNetUtils = new CtsNetUtils(mContext);
}
@@ -128,6 +131,11 @@ public class BatteryStatsManagerTest{
cellularStatsAfter -> cellularBatteryStatsIncreased(
cellularStatsBefore, cellularStatsAfter)));
if (!mWm.isEnhancedPowerReportingSupported()) {
Log.d(TAG, "Skip wifi stats test because wifi does not support link layer stats.");
return;
}
WifiBatteryStats wifiStatsBefore = runAsShell(UPDATE_DEVICE_STATS,
mBsm::getWifiBatteryStats);

View File

@@ -22,6 +22,7 @@ import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_ATTEMPTED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_PROBES_SUCCEEDED_BITMASK;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.KEY_NETWORK_VALIDATION_RESULT;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.NETWORK_VALIDATION_RESULT_SKIPPED;
import static android.net.ConnectivityDiagnosticsManager.ConnectivityReport.NETWORK_VALIDATION_RESULT_VALID;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport;
import static android.net.ConnectivityDiagnosticsManager.DataStallReport.DETECTION_METHOD_DNS_EVENTS;
@@ -78,6 +79,7 @@ import androidx.test.InstrumentationRegistry;
import com.android.internal.telephony.uicc.IccUtils;
import com.android.internal.util.ArrayUtils;
import com.android.modules.utils.build.SdkLevel;
import com.android.net.module.util.ArrayTrackRecord;
import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import com.android.testutils.DevSdkIgnoreRunner;
@@ -427,6 +429,12 @@ public class ConnectivityDiagnosticsManagerTest {
// revalidated which will trigger another onConnectivityReportAvailable callback.
if (!hasConnectivity) {
cb.expectOnConnectivityReportAvailable(mTestNetwork, interfaceName);
} else if (SdkLevel.isAtLeastS()) {
// All calls to #onNetworkConnectivityReported are expected to be accompanied by a call
// to #onConnectivityReportAvailable after a mainline update in the S timeframe.
// Optionally validate this, but do not fail if it does not exist.
cb.maybeVerifyOnConnectivityReportAvailable(mTestNetwork, interfaceName, TRANSPORT_TEST,
false /* requireCallbackFired */);
}
cb.assertNoCallback();
@@ -479,13 +487,25 @@ public class ConnectivityDiagnosticsManagerTest {
public void expectOnConnectivityReportAvailable(
@NonNull Network network, @NonNull String interfaceName) {
expectOnConnectivityReportAvailable(network, interfaceName, TRANSPORT_TEST);
expectOnConnectivityReportAvailable(
network, interfaceName, TRANSPORT_TEST);
}
public void expectOnConnectivityReportAvailable(
@NonNull Network network, @NonNull String interfaceName, int transportType) {
public void expectOnConnectivityReportAvailable(@NonNull Network network,
@NonNull String interfaceName, int transportType) {
maybeVerifyOnConnectivityReportAvailable(network, interfaceName, transportType,
true /* requireCallbackFired */);
}
public void maybeVerifyOnConnectivityReportAvailable(@NonNull Network network,
@NonNull String interfaceName, int transportType, boolean requireCallbackFired) {
final ConnectivityReport result =
(ConnectivityReport) mHistory.poll(CALLBACK_TIMEOUT_MILLIS, x -> true);
// If callback is not required and there is no report, exit early.
if (!requireCallbackFired && result == null) {
return;
}
assertEquals(network, result.getNetwork());
final NetworkCapabilities nc = result.getNetworkCapabilities();
@@ -496,9 +516,16 @@ public class ConnectivityDiagnosticsManagerTest {
final PersistableBundle extras = result.getAdditionalInfo();
assertTrue(extras.containsKey(KEY_NETWORK_VALIDATION_RESULT));
final int validationResult = extras.getInt(KEY_NETWORK_VALIDATION_RESULT);
assertEquals("Network validation result is not 'valid'",
NETWORK_VALIDATION_RESULT_VALID, validationResult);
final int actualValidationResult = extras.getInt(KEY_NETWORK_VALIDATION_RESULT);
// Allow RESULT_VALID for networks that are expected to be skipped. Android S shipped
// with validation results being reported as VALID, but the behavior will be updated via
// mainline update. Allow both behaviors, and let MTS enforce stricter behavior
if (actualValidationResult != NETWORK_VALIDATION_RESULT_SKIPPED
&& actualValidationResult != NETWORK_VALIDATION_RESULT_VALID) {
fail("Network validation result was incorrect; expected skipped or valid, but "
+ "got " + actualValidationResult);
}
assertTrue(extras.containsKey(KEY_NETWORK_PROBES_SUCCEEDED_BITMASK));
final int probesSucceeded = extras.getInt(KEY_NETWORK_VALIDATION_RESULT);

View File

@@ -30,6 +30,7 @@ android_test {
],
libs: [
"android.test.mock",
"ServiceConnectivityResources",
],
static_libs: [
"NetworkStackApiStableLib",

View File

@@ -23,7 +23,9 @@ import android.content.Context.BIND_AUTO_CREATE
import android.content.Context.BIND_IMPORTANT
import android.content.Intent
import android.content.ServiceConnection
import android.content.res.Resources
import android.net.ConnectivityManager
import android.net.ConnectivityResources
import android.net.IDnsResolver
import android.net.INetd
import android.net.LinkProperties
@@ -35,6 +37,7 @@ import android.net.NetworkRequest
import android.net.TestNetworkStackClient
import android.net.Uri
import android.net.metrics.IpConnectivityLog
import android.net.util.MultinetworkPolicyTracker
import android.os.ConditionVariable
import android.os.IBinder
import android.os.SystemConfigManager
@@ -43,6 +46,7 @@ import android.testing.TestableContext
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.connectivity.resources.R
import com.android.server.ConnectivityService
import com.android.server.NetworkAgentWrapper
import com.android.server.TestNetIdManager
@@ -59,6 +63,7 @@ import org.mockito.ArgumentMatchers.anyString
import org.mockito.Mock
import org.mockito.Mockito.any
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.doNothing
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.eq
@@ -93,6 +98,10 @@ class ConnectivityServiceIntegrationTest {
private lateinit var dnsResolver: IDnsResolver
@Mock
private lateinit var systemConfigManager: SystemConfigManager
@Mock
private lateinit var resources: Resources
@Mock
private lateinit var resourcesContext: Context
@Spy
private var context = TestableContext(realContext)
@@ -110,9 +119,11 @@ class ConnectivityServiceIntegrationTest {
private val realContext get() = InstrumentationRegistry.getInstrumentation().context
private val httpProbeUrl get() =
realContext.getResources().getString(R.string.config_captive_portal_http_url)
realContext.getResources().getString(com.android.server.net.integrationtests.R.string
.config_captive_portal_http_url)
private val httpsProbeUrl get() =
realContext.getResources().getString(R.string.config_captive_portal_https_url)
realContext.getResources().getString(com.android.server.net.integrationtests.R.string
.config_captive_portal_https_url)
private class InstrumentationServiceConnection : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
@@ -156,6 +167,27 @@ class ConnectivityServiceIntegrationTest {
.getSystemService(Context.SYSTEM_CONFIG_SERVICE)
doReturn(IntArray(0)).`when`(systemConfigManager).getSystemPermissionUids(anyString())
doReturn(60000).`when`(resources).getInteger(R.integer.config_networkTransitionTimeout)
doReturn("").`when`(resources).getString(R.string.config_networkCaptivePortalServerUrl)
doReturn(arrayOf<String>("test_wlan_wol")).`when`(resources)
.getStringArray(R.array.config_wakeonlan_supported_interfaces)
doReturn(arrayOf("0,1", "1,3")).`when`(resources)
.getStringArray(R.array.config_networkSupportedKeepaliveCount)
doReturn(emptyArray<String>()).`when`(resources)
.getStringArray(R.array.config_networkNotifySwitches)
doReturn(intArrayOf(10, 11, 12, 14, 15)).`when`(resources)
.getIntArray(R.array.config_protectedNetworks)
// We don't test the actual notification value strings, so just return an empty array.
// It doesn't matter what the values are as long as it's not null.
doReturn(emptyArray<String>()).`when`(resources).getStringArray(
R.array.network_switch_type_name)
doReturn(1).`when`(resources).getInteger(R.integer.config_networkAvoidBadWifi)
doReturn(R.array.config_networkSupportedKeepaliveCount).`when`(resources)
.getIdentifier(eq("config_networkSupportedKeepaliveCount"), eq("array"), any())
doReturn(resources).`when`(resourcesContext).getResources()
ConnectivityResources.setResourcesContextForTest(resourcesContext)
networkStackClient = TestNetworkStackClient(realContext)
networkStackClient.start()
@@ -176,12 +208,19 @@ class ConnectivityServiceIntegrationTest {
doReturn(mock(ProxyTracker::class.java)).`when`(deps).makeProxyTracker(any(), any())
doReturn(mock(MockableSystemProperties::class.java)).`when`(deps).systemProperties
doReturn(TestNetIdManager()).`when`(deps).makeNetIdManager()
doAnswer { inv ->
object : MultinetworkPolicyTracker(inv.getArgument(0), inv.getArgument(1),
inv.getArgument(2)) {
override fun getResourcesForActiveSubId() = resources
}
}.`when`(deps).makeMultinetworkPolicyTracker(any(), any(), any())
return deps
}
@After
fun tearDown() {
nsInstrumentation.clearAllState()
ConnectivityResources.setResourcesContextForTest(null)
}
@Test

View File

@@ -62,6 +62,7 @@ android_library {
jarjar_rules: "jarjar-rules.txt",
static_libs: [
"androidx.test.rules",
"androidx.test.uiautomator",
"bouncycastle-repackaged-unbundled",
"core-tests-support",
"FrameworksNetCommonTests",

View File

@@ -53,6 +53,8 @@
<application>
<uses-library android:name="android.test.runner" />
<uses-library android:name="android.net.ipsec.ike" />
<activity
android:name="com.android.server.connectivity.NetworkNotificationManagerTest$TestDialogActivity"/>
</application>
<instrumentation

View File

@@ -125,6 +125,7 @@ import static android.net.resolv.aidl.IDnsResolverUnsolicitedEventListener.VALID
import static android.os.Process.INVALID_UID;
import static android.system.OsConstants.IPPROTO_TCP;
import static com.android.server.ConnectivityService.MAX_NETWORK_REQUESTS_PER_SYSTEM_UID;
import static com.android.server.ConnectivityService.PREFERENCE_PRIORITY_MOBILE_DATA_PREFERERRED;
import static com.android.server.ConnectivityService.PREFERENCE_PRIORITY_OEM;
import static com.android.server.ConnectivityService.PREFERENCE_PRIORITY_PROFILE;
@@ -539,6 +540,9 @@ public class ConnectivityServiceTest {
private final LinkedBlockingQueue<Intent> mStartedActivities = new LinkedBlockingQueue<>();
// Map of permission name -> PermissionManager.Permission_{GRANTED|DENIED} constant
// For permissions granted across the board, the key is only the permission name.
// For permissions only granted to a combination of uid/pid, the key
// is "<permission name>,<pid>,<uid>". PID+UID permissons have priority over generic ones.
private final HashMap<String, Integer> mMockedPermissions = new HashMap<>();
MockContext(Context base, ContentProvider settingsProvider) {
@@ -640,30 +644,40 @@ public class ConnectivityServiceTest {
return mPackageManager;
}
private int checkMockedPermission(String permission, Supplier<Integer> ifAbsent) {
final Integer granted = mMockedPermissions.get(permission);
return granted != null ? granted : ifAbsent.get();
private int checkMockedPermission(String permission, int pid, int uid,
Supplier<Integer> ifAbsent) {
final Integer granted = mMockedPermissions.get(permission + "," + pid + "," + uid);
if (null != granted) {
return granted;
}
final Integer allGranted = mMockedPermissions.get(permission);
if (null != allGranted) {
return allGranted;
}
return ifAbsent.get();
}
@Override
public int checkPermission(String permission, int pid, int uid) {
return checkMockedPermission(
permission, () -> super.checkPermission(permission, pid, uid));
return checkMockedPermission(permission, pid, uid,
() -> super.checkPermission(permission, pid, uid));
}
@Override
public int checkCallingOrSelfPermission(String permission) {
return checkMockedPermission(
permission, () -> super.checkCallingOrSelfPermission(permission));
return checkMockedPermission(permission, Process.myPid(), Process.myUid(),
() -> super.checkCallingOrSelfPermission(permission));
}
@Override
public void enforceCallingOrSelfPermission(String permission, String message) {
final Integer granted = mMockedPermissions.get(permission);
if (granted == null) {
super.enforceCallingOrSelfPermission(permission, message);
return;
}
final Integer granted = checkMockedPermission(permission,
Process.myPid(), Process.myUid(),
() -> {
super.enforceCallingOrSelfPermission(permission, message);
// enforce will crash if the permission is not granted
return PERMISSION_GRANTED;
});
if (!granted.equals(PERMISSION_GRANTED)) {
throw new SecurityException("[Test] permission denied: " + permission);
@@ -673,6 +687,8 @@ public class ConnectivityServiceTest {
/**
* Mock checks for the specified permission, and have them behave as per {@code granted}.
*
* This will apply across the board no matter what the checked UID and PID are.
*
* <p>Passing null reverts to default behavior, which does a real permission check on the
* test package.
* @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
@@ -682,6 +698,21 @@ public class ConnectivityServiceTest {
mMockedPermissions.put(permission, granted);
}
/**
* Mock checks for the specified permission, and have them behave as per {@code granted}.
*
* This will only apply to the passed UID and PID.
*
* <p>Passing null reverts to default behavior, which does a real permission check on the
* test package.
* @param granted One of {@link PackageManager#PERMISSION_GRANTED} or
* {@link PackageManager#PERMISSION_DENIED}.
*/
public void setPermission(String permission, int pid, int uid, Integer granted) {
final String key = permission + "," + pid + "," + uid;
mMockedPermissions.put(key, granted);
}
@Override
public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
@NonNull IntentFilter filter, @Nullable String broadcastPermission,
@@ -1569,15 +1600,21 @@ public class ConnectivityServiceTest {
}
private void withPermission(String permission, ExceptionalRunnable r) throws Exception {
if (mServiceContext.checkCallingOrSelfPermission(permission) == PERMISSION_GRANTED) {
r.run();
return;
}
try {
mServiceContext.setPermission(permission, PERMISSION_GRANTED);
r.run();
} finally {
mServiceContext.setPermission(permission, PERMISSION_DENIED);
mServiceContext.setPermission(permission, null);
}
}
private void withPermission(String permission, int pid, int uid, ExceptionalRunnable r)
throws Exception {
try {
mServiceContext.setPermission(permission, pid, uid, PERMISSION_GRANTED);
r.run();
} finally {
mServiceContext.setPermission(permission, pid, uid, null);
}
}
@@ -9509,6 +9546,19 @@ public class ConnectivityServiceTest {
assertContainsExactly(uidCaptor.getValue(), APP1_UID, APP2_UID);
}
@Test
public void testStartVpnProfileFromDiffPackage() throws Exception {
final String notMyVpnPkg = "com.not.my.vpn";
assertThrows(
SecurityException.class, () -> mVpnManagerService.startVpnProfile(notMyVpnPkg));
}
@Test
public void testStopVpnProfileFromDiffPackage() throws Exception {
final String notMyVpnPkg = "com.not.my.vpn";
assertThrows(SecurityException.class, () -> mVpnManagerService.stopVpnProfile(notMyVpnPkg));
}
@Test
public void testUidUpdateChangesInterfaceFilteringRule() throws Exception {
LinkProperties lp = new LinkProperties();
@@ -13368,17 +13418,45 @@ public class ConnectivityServiceTest {
@Test
public void testProfileNetworkPrefCountsRequestsCorrectlyOnSet() throws Exception {
final UserHandle testHandle = setupEnterpriseNetwork();
testRequestCountLimits(() -> {
// Set initially to test the limit prior to having existing requests.
final TestOnCompleteListener listener = new TestOnCompleteListener();
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
Runnable::run, listener);
final TestOnCompleteListener listener = new TestOnCompleteListener();
// Leave one request available so the profile preference can be set.
testRequestCountLimits(1 /* countToLeaveAvailable */, () -> {
withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
Process.myPid(), Process.myUid(), () -> {
// Set initially to test the limit prior to having existing requests.
mCm.setProfileNetworkPreference(testHandle,
PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
Runnable::run, listener);
});
listener.expectOnComplete();
// re-set so as to test the limit as part of replacing existing requests.
mCm.setProfileNetworkPreference(testHandle, PROFILE_NETWORK_PREFERENCE_ENTERPRISE,
Runnable::run, listener);
// Simulate filing requests as some app on the work profile
final int otherAppUid = UserHandle.getUid(TEST_WORK_PROFILE_USER_ID,
UserHandle.getAppId(Process.myUid() + 1));
final int remainingCount = ConnectivityService.MAX_NETWORK_REQUESTS_PER_UID
- mService.mNetworkRequestCounter.mUidToNetworkRequestCount.get(otherAppUid)
- 1;
final NetworkCallback[] callbacks = new NetworkCallback[remainingCount];
doAsUid(otherAppUid, () -> {
for (int i = 0; i < remainingCount; ++i) {
callbacks[i] = new TestableNetworkCallback();
mCm.registerDefaultNetworkCallback(callbacks[i]);
}
});
withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
Process.myPid(), Process.myUid(), () -> {
// re-set so as to test the limit as part of replacing existing requests.
mCm.setProfileNetworkPreference(testHandle,
PROFILE_NETWORK_PREFERENCE_ENTERPRISE, Runnable::run, listener);
});
listener.expectOnComplete();
doAsUid(otherAppUid, () -> {
for (final NetworkCallback callback : callbacks) {
mCm.unregisterNetworkCallback(callback);
}
});
});
}
@@ -13390,39 +13468,45 @@ public class ConnectivityServiceTest {
mockHasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, true);
@OemNetworkPreferences.OemNetworkPreference final int networkPref =
OEM_NETWORK_PREFERENCE_OEM_PRIVATE_ONLY;
testRequestCountLimits(() -> {
// Set initially to test the limit prior to having existing requests.
final TestOemListenerCallback listener = new TestOemListenerCallback();
mService.setOemNetworkPreference(
createDefaultOemNetworkPreferences(networkPref), listener);
listener.expectOnComplete();
// Leave one request available so the OEM preference can be set.
testRequestCountLimits(1 /* countToLeaveAvailable */, () ->
withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> {
// Set initially to test the limit prior to having existing requests.
final TestOemListenerCallback listener = new TestOemListenerCallback();
mService.setOemNetworkPreference(
createDefaultOemNetworkPreferences(networkPref), listener);
listener.expectOnComplete();
// re-set so as to test the limit as part of replacing existing requests.
mService.setOemNetworkPreference(
createDefaultOemNetworkPreferences(networkPref), listener);
listener.expectOnComplete();
});
// re-set so as to test the limit as part of replacing existing requests.
mService.setOemNetworkPreference(
createDefaultOemNetworkPreferences(networkPref), listener);
listener.expectOnComplete();
}));
}
private void testRequestCountLimits(@NonNull final Runnable r) throws Exception {
private void testRequestCountLimits(final int countToLeaveAvailable,
@NonNull final ExceptionalRunnable r) throws Exception {
final ArraySet<TestNetworkCallback> callbacks = new ArraySet<>();
try {
final int requestCount = mService.mSystemNetworkRequestCounter
.mUidToNetworkRequestCount.get(Process.myUid());
// The limit is hit when total requests <= limit.
final int maxCount =
ConnectivityService.MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - requestCount;
// The limit is hit when total requests = limit - 1, and exceeded with a crash when
// total requests >= limit.
final int countToFile =
MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - requestCount - countToLeaveAvailable;
// Need permission so registerDefaultNetworkCallback uses mSystemNetworkRequestCounter
withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, () -> {
for (int i = 1; i < maxCount - 1; i++) {
for (int i = 1; i < countToFile; i++) {
final TestNetworkCallback cb = new TestNetworkCallback();
mCm.registerDefaultNetworkCallback(cb);
callbacks.add(cb);
}
// Code to run to check if it triggers a max request count limit error.
r.run();
assertEquals(MAX_NETWORK_REQUESTS_PER_SYSTEM_UID - 1 - countToLeaveAvailable,
mService.mSystemNetworkRequestCounter
.mUidToNetworkRequestCount.get(Process.myUid()));
});
// Code to run to check if it triggers a max request count limit error.
r.run();
} finally {
for (final TestNetworkCallback cb : callbacks) {
mCm.unregisterNetworkCallback(cb);
@@ -13667,15 +13751,18 @@ public class ConnectivityServiceTest {
public void testMobileDataPreferredUidsChangedCountsRequestsCorrectlyOnSet() throws Exception {
ConnectivitySettingsManager.setMobileDataPreferredUids(mServiceContext,
Set.of(PRIMARY_USER_HANDLE.getUid(TEST_PACKAGE_UID)));
testRequestCountLimits(() -> {
// Set initially to test the limit prior to having existing requests.
mService.updateMobileDataPreferredUids();
waitForIdle();
// Leave one request available so MDO preference set up above can be set.
testRequestCountLimits(1 /* countToLeaveAvailable */, () ->
withPermission(NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
Process.myPid(), Process.myUid(), () -> {
// Set initially to test the limit prior to having existing requests.
mService.updateMobileDataPreferredUids();
waitForIdle();
// re-set so as to test the limit as part of replacing existing requests.
mService.updateMobileDataPreferredUids();
waitForIdle();
});
// re-set so as to test the limit as part of replacing existing requests
mService.updateMobileDataPreferredUids();
waitForIdle();
}));
}
@Test

View File

@@ -43,6 +43,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.net.ConnectivitySettingsManager;
import android.net.IDnsResolver;
@@ -106,8 +107,14 @@ public class DnsManagerTest {
@Mock IDnsResolver mMockDnsResolver;
private void assertResolverOptionsEquals(
@NonNull ResolverOptionsParcel actual,
@NonNull ResolverOptionsParcel expected) {
@Nullable ResolverOptionsParcel actual,
@Nullable ResolverOptionsParcel expected) {
if (actual == null) {
assertNull(expected);
return;
} else {
assertNotNull(expected);
}
assertEquals(actual.hosts, expected.hosts);
assertEquals(actual.tcMode, expected.tcMode);
assertEquals(actual.enforceDnsUid, expected.enforceDnsUid);
@@ -365,7 +372,7 @@ public class DnsManagerTest {
expectedParams.tlsName = "";
expectedParams.tlsServers = new String[]{"3.3.3.3", "4.4.4.4"};
expectedParams.transportTypes = TEST_TRANSPORT_TYPES;
expectedParams.resolverOptions = new ResolverOptionsParcel();
expectedParams.resolverOptions = null;
assertResolverParamsEquals(actualParams, expectedParams);
}

View File

@@ -27,6 +27,7 @@ import static com.android.server.connectivity.NetworkNotificationManager.Notific
import static com.android.server.connectivity.NetworkNotificationManager.NotificationType.SIGN_IN;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.clearInvocations;
@@ -39,9 +40,14 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.app.Instrumentation;
import android.app.KeyguardManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources;
@@ -49,11 +55,19 @@ import android.net.ConnectivityResources;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject;
import androidx.test.uiautomator.UiSelector;
import com.android.connectivity.resources.R;
import com.android.server.connectivity.NetworkNotificationManager.NotificationType;
@@ -84,6 +98,7 @@ public class NetworkNotificationManagerTest {
private static final String TEST_EXTRA_INFO = "extra";
private static final int TEST_NOTIF_ID = 101;
private static final String TEST_NOTIF_TAG = NetworkNotificationManager.tagFor(TEST_NOTIF_ID);
private static final long TEST_TIMEOUT_MS = 10_000L;
static final NetworkCapabilities CELL_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities WIFI_CAPABILITIES = new NetworkCapabilities();
static final NetworkCapabilities VPN_CAPABILITIES = new NetworkCapabilities();
@@ -102,6 +117,25 @@ public class NetworkNotificationManagerTest {
VPN_CAPABILITIES.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN);
}
/**
* Test activity that shows the action it was started with on screen, and dismisses when the
* text is tapped.
*/
public static class TestDialogActivity extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTurnScreenOn(true);
getSystemService(KeyguardManager.class).requestDismissKeyguard(
this, null /* callback */);
final TextView txt = new TextView(this);
txt.setText(getIntent().getAction());
txt.setOnClickListener(e -> finish());
setContentView(txt);
}
}
@Mock Context mCtx;
@Mock Resources mResources;
@Mock DisplayMetrics mDisplayMetrics;
@@ -345,4 +379,82 @@ public class NetworkNotificationManagerTest {
mManager.clearNotification(id, PARTIAL_CONNECTIVITY);
verify(mNotificationManager, never()).cancel(eq(tag), eq(PARTIAL_CONNECTIVITY.eventId));
}
@Test
public void testNotifyNoInternetAsDialogWhenHighPriority() throws Exception {
doReturn(true).when(mResources).getBoolean(
R.bool.config_notifyNoInternetAsDialogWhenHighPriority);
mManager.showNotification(TEST_NOTIF_ID, NETWORK_SWITCH, mWifiNai, mCellNai, null, false);
// Non-"no internet" notifications are not affected
verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(NETWORK_SWITCH.eventId), any());
final Instrumentation instr = InstrumentationRegistry.getInstrumentation();
final Context ctx = instr.getContext();
final String testAction = "com.android.connectivity.coverage.TEST_DIALOG";
final Intent intent = new Intent(testAction)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setClassName(ctx.getPackageName(), TestDialogActivity.class.getName());
final PendingIntent pendingIntent = PendingIntent.getActivity(ctx, 0 /* requestCode */,
intent, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_IMMUTABLE);
mManager.showNotification(TEST_NOTIF_ID, NO_INTERNET, mWifiNai, null /* switchToNai */,
pendingIntent, true /* highPriority */);
// Previous notifications are still dismissed
verify(mNotificationManager).cancel(TEST_NOTIF_TAG, NETWORK_SWITCH.eventId);
// Verify that the activity is shown (the activity shows the action on screen)
final UiObject actionText = UiDevice.getInstance(instr).findObject(
new UiSelector().text(testAction));
assertTrue("Activity not shown", actionText.waitForExists(TEST_TIMEOUT_MS));
// Tapping the text should dismiss the dialog
actionText.click();
assertTrue("Activity not dismissed", actionText.waitUntilGone(TEST_TIMEOUT_MS));
// Verify no NO_INTERNET notification was posted
verify(mNotificationManager, never()).notify(any(), eq(NO_INTERNET.eventId), any());
}
private void doNotificationTextTest(NotificationType type, @StringRes int expectedTitleRes,
String expectedTitleArg, @StringRes int expectedContentRes) {
final String expectedTitle = "title " + expectedTitleArg;
final String expectedContent = "expected content";
doReturn(expectedTitle).when(mResources).getString(expectedTitleRes, expectedTitleArg);
doReturn(expectedContent).when(mResources).getString(expectedContentRes);
mManager.showNotification(TEST_NOTIF_ID, type, mWifiNai, mCellNai, null, false);
final ArgumentCaptor<Notification> notifCap = ArgumentCaptor.forClass(Notification.class);
verify(mNotificationManager).notify(eq(TEST_NOTIF_TAG), eq(type.eventId),
notifCap.capture());
final Notification notif = notifCap.getValue();
assertEquals(expectedTitle, notif.extras.getString(Notification.EXTRA_TITLE));
assertEquals(expectedContent, notif.extras.getString(Notification.EXTRA_TEXT));
}
@Test
public void testNotificationText_NoInternet() {
doNotificationTextTest(NO_INTERNET,
R.string.wifi_no_internet, TEST_EXTRA_INFO,
R.string.wifi_no_internet_detailed);
}
@Test
public void testNotificationText_Partial() {
doNotificationTextTest(PARTIAL_CONNECTIVITY,
R.string.network_partial_connectivity, TEST_EXTRA_INFO,
R.string.network_partial_connectivity_detailed);
}
@Test
public void testNotificationText_PartialAsNoInternet() {
doReturn(true).when(mResources).getBoolean(
R.bool.config_partialConnectivityNotifiedAsNoInternet);
doNotificationTextTest(PARTIAL_CONNECTIVITY,
R.string.wifi_no_internet, TEST_EXTRA_INFO,
R.string.wifi_no_internet_detailed);
}
}