Merge ab/7633965
Bug: 169893837 Merged-In: I592dd5f1d6e13b020beadb11b9d913857a82e524 Change-Id: I4e01d58ed61c595b704bae6c935d0da1714ee398
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -30,6 +30,7 @@ android_test {
|
||||
],
|
||||
libs: [
|
||||
"android.test.mock",
|
||||
"ServiceConnectivityResources",
|
||||
],
|
||||
static_libs: [
|
||||
"NetworkStackApiStableLib",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user