Merge "Report isKnownService in discovery stop metrics" into main
diff --git a/TEST_MAPPING b/TEST_MAPPING
index 375397b..4cf93a8 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -77,18 +77,6 @@
"name": "libnetworkstats_test"
},
{
- "name": "NetHttpCoverageTests",
- "options": [
- {
- "exclude-annotation": "com.android.testutils.SkipPresubmit"
- },
- {
- // These sometimes take longer than 1 min which is the presubmit timeout
- "exclude-annotation": "androidx.test.filters.LargeTest"
- }
- ]
- },
- {
"name": "CtsTetheringTestLatestSdk",
"options": [
{
diff --git a/framework-t/src/android/net/nsd/NsdManager.java b/framework-t/src/android/net/nsd/NsdManager.java
index 48d40e6..b21e22a 100644
--- a/framework-t/src/android/net/nsd/NsdManager.java
+++ b/framework-t/src/android/net/nsd/NsdManager.java
@@ -1782,16 +1782,16 @@
if (!hasServiceName && !hasServiceType && serviceInfo.getPort() == 0) {
return ServiceValidationType.NO_SERVICE;
}
- if (hasServiceName && hasServiceType) {
- if (serviceInfo.getPort() < 0) {
- throw new IllegalArgumentException("Invalid port");
- }
- if (serviceInfo.getPort() == 0) {
- return ServiceValidationType.HAS_SERVICE_ZERO_PORT;
- }
- return ServiceValidationType.HAS_SERVICE;
+ if (!hasServiceName || !hasServiceType) {
+ throw new IllegalArgumentException("The service name or the service type is missing");
}
- throw new IllegalArgumentException("The service name or the service type is missing");
+ if (serviceInfo.getPort() < 0) {
+ throw new IllegalArgumentException("Invalid port");
+ }
+ if (serviceInfo.getPort() == 0) {
+ return ServiceValidationType.HAS_SERVICE_ZERO_PORT;
+ }
+ return ServiceValidationType.HAS_SERVICE;
}
/**
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
index 832ac03..576e806 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
@@ -28,9 +28,7 @@
import static org.junit.Assume.assumeTrue;
import android.app.UiAutomation;
-import android.bluetooth.BluetoothAdapter;
-import android.bluetooth.BluetoothManager;
-import android.bluetooth.cts.BTAdapterUtils;
+import android.bluetooth.test_utils.EnableBluetoothRule;
import android.content.Context;
import android.location.LocationManager;
import android.nearby.BroadcastCallback;
@@ -58,6 +56,7 @@
import com.android.modules.utils.build.SdkLevel;
import org.junit.Before;
+import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -76,6 +75,9 @@
@RunWith(AndroidJUnit4.class)
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public class NearbyManagerTest {
+
+ @ClassRule static final EnableBluetoothRule sEnableBluetooth = new EnableBluetoothRule();
+
private static final byte[] SALT = new byte[]{1, 2};
private static final byte[] SECRET_ID = new byte[]{1, 2, 3, 4};
private static final byte[] META_DATA_ENCRYPTION_KEY = new byte[14];
@@ -128,8 +130,6 @@
mContext = InstrumentationRegistry.getContext();
mNearbyManager = mContext.getSystemService(NearbyManager.class);
-
- enableBluetooth();
}
@Test
@@ -281,14 +281,6 @@
assertThrows(SecurityException.class, () -> mNearbyManager.getPoweredOffFindingMode());
}
- private void enableBluetooth() {
- BluetoothManager manager = mContext.getSystemService(BluetoothManager.class);
- BluetoothAdapter bluetoothAdapter = manager.getAdapter();
- if (!bluetoothAdapter.isEnabled()) {
- assertThat(BTAdapterUtils.enableAdapter(bluetoothAdapter, mContext)).isTrue();
- }
- }
-
private void enableLocation() {
LocationManager locationManager = mContext.getSystemService(LocationManager.class);
UserHandle user = Process.myUserHandle();
diff --git a/netbpfload/NetBpfLoad.cpp b/netbpfload/NetBpfLoad.cpp
index dc67f83..59986d4 100644
--- a/netbpfload/NetBpfLoad.cpp
+++ b/netbpfload/NetBpfLoad.cpp
@@ -306,7 +306,6 @@
if (bad && !isGSI()) {
ALOGE("Unsupported kernel version (%07x).", kernelVersion());
- sleep(60);
}
}
diff --git a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
index 50d6e76..e88c105 100755
--- a/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
+++ b/tests/cts/hostside/app/src/com/android/cts/net/hostside/VpnTest.java
@@ -22,6 +22,7 @@
import static android.Manifest.permission.WRITE_DEVICE_CONFIG;
import static android.content.pm.PackageManager.FEATURE_TELEPHONY;
import static android.content.pm.PackageManager.FEATURE_WIFI;
+import static android.net.ConnectivityManager.FIREWALL_CHAIN_BACKGROUND;
import static android.net.ConnectivityManager.TYPE_VPN;
import static android.net.NetworkCapabilities.TRANSPORT_TEST;
import static android.net.NetworkCapabilities.TRANSPORT_VPN;
@@ -51,6 +52,7 @@
import static com.android.testutils.Cleanup.testAndCleanup;
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
import static com.android.testutils.RecorderCallback.CallbackEntry.BLOCKED_STATUS_INT;
+import static com.android.testutils.TestPermissionUtil.runAsShell;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -977,11 +979,18 @@
registerDefaultNetworkCallbackForUid(otherUid, otherUidCallback, h);
registerDefaultNetworkCallbackForUid(Process.myUid(), myUidCallback, h);
}, NETWORK_SETTINGS);
- for (TestableNetworkCallback callback :
- List.of(systemDefaultCallback, otherUidCallback, myUidCallback)) {
+ for (TestableNetworkCallback callback : List.of(systemDefaultCallback, myUidCallback)) {
callback.expectAvailableCallbacks(defaultNetwork, false /* suspended */,
true /* validated */, false /* blocked */, TIMEOUT_MS);
}
+ // On V+, ConnectivityService generates blockedReasons based on bpf map contents even if
+ // the otherUid does not exist on device. So if the background chain is enabled,
+ // otherUid is blocked.
+ final boolean isOtherUidBlocked = SdkLevel.isAtLeastV()
+ && runAsShell(NETWORK_SETTINGS, () -> mCM.getFirewallChainEnabled(
+ FIREWALL_CHAIN_BACKGROUND));
+ otherUidCallback.expectAvailableCallbacks(defaultNetwork, false /* suspended */,
+ true /* validated */, isOtherUidBlocked, TIMEOUT_MS);
}
FileDescriptor fd = openSocketFdInOtherApp(TEST_HOST, 80, TIMEOUT_MS);
diff --git a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
index a029a54..90fb7a9 100644
--- a/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
+++ b/tests/cts/net/src/android/net/cts/ApfIntegrationTest.kt
@@ -192,8 +192,8 @@
futureReply!!.complete(recvbuf.sliceArray(8..<length))
}
- fun sendPing(data: ByteArray) {
- require(data.size == 56)
+ fun sendPing(data: ByteArray, payloadSize: Int) {
+ require(data.size == payloadSize)
// rfc4443#section-4.1: Echo Request Message
// 0 1 2 3
@@ -373,7 +373,15 @@
// Compare entire memory region.
val readResult = readProgram()
- assertWithMessage("read/write $i byte prog failed").that(readResult).isEqualTo(program)
+ val errMsg = """
+ read/write $i byte prog failed.
+ In APFv4, the APF memory region MUST NOT be modified or cleared except by APF
+ instructions executed by the interpreter or by Android OS calls to the HAL. If this
+ requirement cannot be met, the firmware cannot declare that it supports APFv4 and
+ it should declare that it only supports APFv3(if counter is partially supported) or
+ APFv2.
+ """.trimIndent()
+ assertWithMessage(errMsg).that(readResult).isEqualTo(program)
}
}
@@ -407,8 +415,13 @@
readProgram() // wait for install completion
// Assert that initial ping does not get filtered.
- val data = ByteArray(56).also { Random.nextBytes(it) }
- packetReader.sendPing(data)
+ val payloadSize = if (getFirstApiLevel() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ 68
+ } else {
+ 4
+ }
+ val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
+ packetReader.sendPing(data, payloadSize)
assertThat(packetReader.expectPingReply()).isEqualTo(data)
// Generate an APF program that drops the next ping
@@ -428,7 +441,7 @@
installProgram(program)
readProgram() // wait for install completion
- packetReader.sendPing(data)
+ packetReader.sendPing(data, payloadSize)
packetReader.expectPingDropped()
}
@@ -470,8 +483,13 @@
readProgram() // wait for install completion
// Trigger the program by sending a ping and waiting on the reply.
- val data = ByteArray(56).also { Random.nextBytes(it) }
- packetReader.sendPing(data)
+ val payloadSize = if (getFirstApiLevel() >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
+ 68
+ } else {
+ 4
+ }
+ val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
+ packetReader.sendPing(data, payloadSize)
packetReader.expectPingReply()
val readResult = readProgram()
@@ -479,8 +497,51 @@
expect.withMessage("PROGRAM_SIZE").that(buffer.getInt()).isEqualTo(program.size)
expect.withMessage("RAM_LEN").that(buffer.getInt()).isEqualTo(caps.maximumApfProgramSize)
expect.withMessage("IPV4_HEADER_SIZE").that(buffer.getInt()).isEqualTo(0)
- // Ping packet (64) + IPv6 header (40) + ethernet header (14)
- expect.withMessage("PACKET_SIZE").that(buffer.getInt()).isEqualTo(64 + 40 + 14)
+ // Ping packet payload + ICMPv6 header (8) + IPv6 header (40) + ethernet header (14)
+ expect.withMessage("PACKET_SIZE").that(buffer.getInt()).isEqualTo(payloadSize + 8 + 40 + 14)
expect.withMessage("FILTER_AGE_SECONDS").that(buffer.getInt()).isLessThan(5)
}
+
+ // APF integration is mostly broken before V
+ @IgnoreUpTo(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
+ @Test
+ fun testFilterAgeIncreasesBetweenPackets() {
+ // VSR-14 mandates APF to be turned on when the screen is off and the Wi-Fi link
+ // is idle or traffic is less than 10 Mbps. Before that, we don't mandate when the APF
+ // should be turned on.
+ assume().that(getVsrApiLevel()).isAtLeast(34)
+ assumeApfVersionSupportAtLeast(4)
+ clearApfMemory()
+ val gen = ApfV4Generator(4)
+
+ // If not ICMPv6 Echo Reply -> PASS
+ gen.addPassIfNotIcmpv6EchoReply()
+
+ // Store all prefilled memory slots in counter region [500, 520)
+ val counterRegion = 500
+ gen.addLoadImmediate(R1, counterRegion)
+ gen.addLoadFromMemory(R0, MemorySlot.FILTER_AGE_SECONDS)
+ gen.addStoreData(R0, 0)
+
+ installProgram(gen.generate())
+ readProgram() // wait for install completion
+
+ val payloadSize = 56
+ val data = ByteArray(payloadSize).also { Random.nextBytes(it) }
+ packetReader.sendPing(data, payloadSize)
+ packetReader.expectPingReply()
+
+ var buffer = ByteBuffer.wrap(readProgram(), counterRegion, 4 /* length */)
+ val filterAgeSecondsOrig = buffer.getInt()
+
+ Thread.sleep(5100)
+
+ packetReader.sendPing(data, payloadSize)
+ packetReader.expectPingReply()
+
+ buffer = ByteBuffer.wrap(readProgram(), counterRegion, 4 /* length */)
+ val filterAgeSeconds = buffer.getInt()
+ // Assert that filter age has increased, but not too much.
+ assertThat(filterAgeSeconds - filterAgeSecondsOrig).isEqualTo(5)
+ }
}
diff --git a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
index 6a9ae71..af9abdf 100644
--- a/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
+++ b/thread/service/java/com/android/server/thread/ThreadNetworkControllerService.java
@@ -1387,14 +1387,6 @@
}
}
- private void notifyThreadEnabledUpdated(IStateCallback callback, int enabledState) {
- try {
- callback.onThreadEnableStateChanged(enabledState);
- } catch (RemoteException ignored) {
- // do nothing if the client is dead
- }
- }
-
public void unregisterStateCallback(IStateCallback callback) {
checkOnHandlerThread();
if (!mStateCallbacks.containsKey(callback)) {
@@ -1461,15 +1453,19 @@
}
}
- @Override
- public void onThreadEnabledChanged(int state) {
- mHandler.post(() -> onThreadEnabledChangedInternal(state));
- }
-
- private void onThreadEnabledChangedInternal(int state) {
+ private void onThreadEnabledChanged(int state, long listenerId) {
checkOnHandlerThread();
- for (IStateCallback callback : mStateCallbacks.keySet()) {
- notifyThreadEnabledUpdated(callback, otStateToAndroidState(state));
+ boolean stateChanged = (mState == null || mState.threadEnabled != state);
+
+ for (var callbackEntry : mStateCallbacks.entrySet()) {
+ if (!stateChanged && callbackEntry.getValue().id != listenerId) {
+ continue;
+ }
+ try {
+ callbackEntry.getKey().onThreadEnableStateChanged(otStateToAndroidState(state));
+ } catch (RemoteException ignored) {
+ // do nothing if the client is dead
+ }
}
}
@@ -1496,6 +1492,7 @@
onInterfaceStateChanged(newState.isInterfaceUp);
onDeviceRoleChanged(newState.deviceRole, listenerId);
onPartitionIdChanged(newState.partitionId, listenerId);
+ onThreadEnabledChanged(newState.threadEnabled, listenerId);
mState = newState;
ActiveOperationalDataset newActiveDataset;
diff --git a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
index 0e76930..996d22d 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ActiveOperationalDatasetTest.java
@@ -30,6 +30,8 @@
import android.net.thread.ActiveOperationalDataset.Builder;
import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
import android.net.thread.OperationalDatasetTimestamp;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import android.util.SparseArray;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -38,6 +40,7 @@
import com.google.common.primitives.Bytes;
import com.google.common.testing.EqualsTester;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,6 +49,7 @@
/** CTS tests for {@link ActiveOperationalDataset}. */
@SmallTest
+@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public final class ActiveOperationalDatasetTest {
private static final int TYPE_ACTIVE_TIMESTAMP = 14;
@@ -81,6 +85,8 @@
private static final ActiveOperationalDataset DEFAULT_DATASET =
ActiveOperationalDataset.fromThreadTlvs(VALID_DATASET_TLVS);
+ @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
private static byte[] removeTlv(byte[] dataset, int type) {
ByteArrayOutputStream os = new ByteArrayOutputStream(dataset.length);
int i = 0;
diff --git a/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java b/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java
index 9be3d56..4d7c7f1 100644
--- a/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/OperationalDatasetTimestampTest.java
@@ -21,12 +21,15 @@
import static org.junit.Assert.assertThrows;
import android.net.thread.OperationalDatasetTimestamp;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import com.google.common.testing.EqualsTester;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -34,8 +37,11 @@
/** Tests for {@link OperationalDatasetTimestamp}. */
@SmallTest
+@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public final class OperationalDatasetTimestampTest {
+ @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
@Test
public void fromInstant_tooLargeInstant_throwsIllegalArgument() {
assertThrows(
diff --git a/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java b/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
index 0bb18ce..76be054 100644
--- a/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/PendingOperationalDatasetTest.java
@@ -28,6 +28,8 @@
import android.net.thread.ActiveOperationalDataset.SecurityPolicy;
import android.net.thread.OperationalDatasetTimestamp;
import android.net.thread.PendingOperationalDataset;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import android.util.SparseArray;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -36,6 +38,7 @@
import com.google.common.primitives.Bytes;
import com.google.common.testing.EqualsTester;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -44,8 +47,11 @@
/** Tests for {@link PendingOperationalDataset}. */
@SmallTest
+@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public final class PendingOperationalDatasetTest {
+ @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
private static ActiveOperationalDataset createActiveDataset() throws Exception {
SparseArray<byte[]> channelMask = new SparseArray<>(1);
channelMask.put(0, new byte[] {0x00, 0x1f, (byte) 0xff, (byte) 0xe0});
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
index dea4279..11c4819 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkControllerTest.java
@@ -43,7 +43,6 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.util.concurrent.MoreExecutors.directExecutor;
-import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
@@ -174,6 +173,17 @@
}
@Test
+ public void subscribeThreadEnableState_getActiveDataset_onThreadEnableStateChangedNotCalled()
+ throws Exception {
+ EnabledStateListener listener = new EnabledStateListener(mController);
+ listener.expectThreadEnabledState(STATE_ENABLED);
+
+ getActiveOperationalDataset(mController);
+
+ listener.expectCallbackNotCalled();
+ }
+
+ @Test
public void registerStateCallback_returnsUpdatedEnabledStates() throws Exception {
CompletableFuture<Void> setFuture1 = new CompletableFuture<>();
CompletableFuture<Void> setFuture2 = new CompletableFuture<>();
@@ -1016,7 +1026,11 @@
}
public void expectThreadEnabledState(int enabled) {
- assertNotNull(mReadHead.poll(ENABLED_TIMEOUT_MILLIS, e -> (e == enabled)));
+ assertThat(mReadHead.poll(ENABLED_TIMEOUT_MILLIS, e -> (e == enabled))).isNotNull();
+ }
+
+ public void expectCallbackNotCalled() {
+ assertThat(mReadHead.poll(CALLBACK_TIMEOUT_MILLIS, e -> true)).isNull();
}
public void unregisterStateCallback() {
diff --git a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java
index 7d9ae81..4de2e13 100644
--- a/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java
+++ b/thread/tests/cts/src/android/net/thread/cts/ThreadNetworkExceptionTest.java
@@ -25,17 +25,23 @@
import static org.junit.Assert.assertThrows;
import android.net.thread.ThreadNetworkException;
+import android.net.thread.utils.ThreadFeatureCheckerRule;
+import android.net.thread.utils.ThreadFeatureCheckerRule.RequiresThreadFeature;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
/** CTS tests for {@link ThreadNetworkException}. */
@SmallTest
+@RequiresThreadFeature
@RunWith(AndroidJUnit4.class)
public final class ThreadNetworkExceptionTest {
+ @Rule public final ThreadFeatureCheckerRule mThreadRule = new ThreadFeatureCheckerRule();
+
@Test
public void constructor_validValues_valuesAreConnectlySet() throws Exception {
ThreadNetworkException errorThreadDisabled =
diff --git a/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java b/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java
index bee9ceb..38a6e90 100644
--- a/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java
+++ b/thread/tests/utils/src/android/net/thread/utils/ThreadFeatureCheckerRule.java
@@ -21,7 +21,6 @@
import static org.junit.Assume.assumeTrue;
import android.content.Context;
-import android.net.thread.ThreadNetworkManager;
import android.os.SystemProperties;
import android.os.VintfRuntimeInfo;
@@ -122,7 +121,10 @@
/** Returns {@code true} if this device has the Thread feature supported. */
private static boolean hasThreadFeature() {
final Context context = ApplicationProvider.getApplicationContext();
- return context.getSystemService(ThreadNetworkManager.class) != null;
+
+ // Use service name rather than `ThreadNetworkManager.class` to avoid
+ // `ClassNotFoundException` on U- devices.
+ return context.getSystemService("thread_network") != null;
}
/**