Merge changes from topic "cherrypicker-L19600000954488498:N18900001264327475" into tm-dev
* changes:
Address leftover comments
Test [set|get]AppExclusionList
diff --git a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
index ecb6478..c403548 100644
--- a/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
+++ b/Tethering/src/com/android/networkstack/tethering/BpfCoordinator.java
@@ -77,6 +77,7 @@
import com.android.networkstack.tethering.apishim.common.BpfCoordinatorShim;
import com.android.networkstack.tethering.util.TetheringUtils.ForwardedStats;
+import java.io.IOException;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
@@ -1024,7 +1025,7 @@
map.forEach((k, v) -> {
pw.println(String.format("%s: %s", k, v));
});
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping BPF stats map: " + e);
}
}
@@ -1072,7 +1073,7 @@
return;
}
map.forEach((k, v) -> pw.println(ipv6UpstreamRuletoString(k, v)));
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping IPv6 upstream map: " + e);
}
}
@@ -1116,7 +1117,7 @@
if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_STATS)) {
try (BpfMap<TetherStatsKey, TetherStatsValue> statsMap = mDeps.getBpfStatsMap()) {
dumpRawMap(statsMap, pw);
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping stats map: " + e);
}
return;
@@ -1124,7 +1125,7 @@
if (CollectionUtils.contains(args, DUMPSYS_RAWMAP_ARG_UPSTREAM4)) {
try (BpfMap<Tether4Key, Tether4Value> upstreamMap = mDeps.getBpfUpstream4Map()) {
dumpRawMap(upstreamMap, pw);
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping IPv4 map: " + e);
}
return;
@@ -1195,7 +1196,7 @@
pw.increaseIndent();
dumpIpv4ForwardingRuleMap(now, DOWNSTREAM, downstreamMap, pw);
pw.decreaseIndent();
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping IPv4 map: " + e);
}
}
@@ -1220,7 +1221,7 @@
}
if (v.val > 0) pw.println(String.format("%s: %d", counterName, v.val));
});
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping counter map: " + e);
}
}
@@ -1244,7 +1245,7 @@
pw.println(String.format("%d (%s) -> %d (%s)", k.ifIndex, getIfName(k.ifIndex),
v.ifIndex, getIfName(v.ifIndex)));
});
- } catch (ErrnoException e) {
+ } catch (ErrnoException | IOException e) {
pw.println("Error dumping dev map: " + e);
}
pw.decreaseIndent();
diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
index ad2faa0..75c2ad1 100644
--- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
+++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/BpfMapTest.java
@@ -358,8 +358,8 @@
try {
mTestMap.clear();
fail("clearing already-closed map should throw");
- } catch (ErrnoException expected) {
- assertEquals(OsConstants.EBADF, expected.errno);
+ } catch (IllegalStateException expected) {
+ // ParcelFileDescriptor.getFd throws IllegalStateException: Already closed.
}
}
diff --git a/framework/src/android/net/LinkProperties.java b/framework/src/android/net/LinkProperties.java
index 4ce2593..a8f707e 100644
--- a/framework/src/android/net/LinkProperties.java
+++ b/framework/src/android/net/LinkProperties.java
@@ -64,7 +64,7 @@
* @hide
*/
@ChangeId
- @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S) // Switch to S_V2 when it is available.
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2)
@VisibleForTesting
public static final long EXCLUDED_ROUTES = 186082280;
diff --git a/nearby/framework/java/android/nearby/INearbyManager.aidl b/nearby/framework/java/android/nearby/INearbyManager.aidl
index 3fd5ecc..0291fff 100644
--- a/nearby/framework/java/android/nearby/INearbyManager.aidl
+++ b/nearby/framework/java/android/nearby/INearbyManager.aidl
@@ -31,10 +31,10 @@
int registerScanListener(in ScanRequest scanRequest, in IScanListener listener,
String packageName, @nullable String attributionTag);
- void unregisterScanListener(in IScanListener listener);
+ void unregisterScanListener(in IScanListener listener, String packageName, @nullable String attributionTag);
void startBroadcast(in BroadcastRequestParcelable broadcastRequest,
in IBroadcastListener callback, String packageName, @nullable String attributionTag);
- void stopBroadcast(in IBroadcastListener callback);
+ void stopBroadcast(in IBroadcastListener callback, String packageName, @nullable String attributionTag);
}
\ No newline at end of file
diff --git a/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java b/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
index a9d7cf7..8f44091 100644
--- a/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
+++ b/nearby/framework/java/android/nearby/NearbyDeviceParcelable.java
@@ -46,6 +46,7 @@
@Override
public NearbyDeviceParcelable createFromParcel(Parcel in) {
Builder builder = new Builder();
+ builder.setScanType(in.readInt());
if (in.readInt() == 1) {
builder.setName(in.readString());
}
@@ -69,6 +70,12 @@
in.readByteArray(data);
builder.setData(data);
}
+ if (in.readInt() == 1) {
+ int saltLength = in.readInt();
+ byte[] salt = new byte[saltLength];
+ in.readByteArray(salt);
+ builder.setData(salt);
+ }
return builder.build();
}
@@ -129,6 +136,7 @@
*/
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mScanType);
dest.writeInt(mName == null ? 0 : 1);
if (mName != null) {
dest.writeString(mName);
@@ -162,7 +170,9 @@
@Override
public String toString() {
return "NearbyDeviceParcelable["
- + "name="
+ + "scanType="
+ + mScanType
+ + ", name="
+ mName
+ ", medium="
+ NearbyDevice.mediumToString(mMedium)
@@ -187,7 +197,8 @@
public boolean equals(Object other) {
if (other instanceof NearbyDeviceParcelable) {
NearbyDeviceParcelable otherNearbyDeviceParcelable = (NearbyDeviceParcelable) other;
- return Objects.equals(mName, otherNearbyDeviceParcelable.mName)
+ return mScanType == otherNearbyDeviceParcelable.mScanType
+ && (Objects.equals(mName, otherNearbyDeviceParcelable.mName))
&& (mMedium == otherNearbyDeviceParcelable.mMedium)
&& (mTxPower == otherNearbyDeviceParcelable.mTxPower)
&& (mRssi == otherNearbyDeviceParcelable.mRssi)
@@ -207,6 +218,7 @@
@Override
public int hashCode() {
return Objects.hash(
+ mScanType,
mName,
mMedium,
mRssi,
diff --git a/nearby/framework/java/android/nearby/NearbyManager.java b/nearby/framework/java/android/nearby/NearbyManager.java
index 9073f78..106c290 100644
--- a/nearby/framework/java/android/nearby/NearbyManager.java
+++ b/nearby/framework/java/android/nearby/NearbyManager.java
@@ -70,6 +70,8 @@
int ERROR = 2;
}
+ private static final String TAG = "NearbyManager";
+
/**
* Whether allows Fast Pair to scan.
*
@@ -204,7 +206,11 @@
ScanListenerTransport transport = reference != null ? reference.get() : null;
if (transport != null) {
transport.unregister();
- mService.unregisterScanListener(transport);
+ mService.unregisterScanListener(transport, mContext.getPackageName(),
+ mContext.getAttributionTag());
+ } else {
+ Log.e(TAG, "Cannot stop scan with this callback "
+ + "because it is never registered.");
}
}
} catch (RemoteException e) {
@@ -259,7 +265,11 @@
BroadcastListenerTransport transport = reference != null ? reference.get() : null;
if (transport != null) {
transport.unregister();
- mService.stopBroadcast(transport);
+ mService.stopBroadcast(transport, mContext.getPackageName(),
+ mContext.getAttributionTag());
+ } else {
+ Log.e(TAG, "Cannot stop broadcast with this callback "
+ + "because it is never registered.");
}
}
} catch (RemoteException e) {
diff --git a/nearby/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java
index 2dee835..5ebf1e5 100644
--- a/nearby/service/java/com/android/server/nearby/NearbyService.java
+++ b/nearby/service/java/com/android/server/nearby/NearbyService.java
@@ -43,7 +43,6 @@
import com.android.server.nearby.fastpair.FastPairManager;
import com.android.server.nearby.injector.ContextHubManagerAdapter;
import com.android.server.nearby.injector.Injector;
-import com.android.server.nearby.presence.PresenceManager;
import com.android.server.nearby.provider.BroadcastProviderManager;
import com.android.server.nearby.provider.DiscoveryProviderManager;
import com.android.server.nearby.provider.FastPairDataProvider;
@@ -58,7 +57,6 @@
private final Context mContext;
private Injector mInjector;
private final FastPairManager mFastPairManager;
- private final PresenceManager mPresenceManager;
private final BroadcastReceiver mBluetoothReceiver =
new BroadcastReceiver() {
@Override
@@ -86,7 +84,6 @@
mBroadcastProviderManager = new BroadcastProviderManager(context, mInjector);
final LocatorContextWrapper lcw = new LocatorContextWrapper(context, null);
mFastPairManager = new FastPairManager(lcw);
- mPresenceManager = new PresenceManager(lcw);
}
@VisibleForTesting
@@ -110,22 +107,36 @@
}
@Override
- public void unregisterScanListener(IScanListener listener) {
+ public void unregisterScanListener(IScanListener listener, String packageName,
+ @Nullable String attributionTag) {
+ // Permissions check
+ enforceBluetoothPrivilegedPermission(mContext);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
+ DiscoveryPermissions.enforceDiscoveryPermission(mContext, identity);
+
mProviderManager.unregisterScanListener(listener);
}
@Override
public void startBroadcast(BroadcastRequestParcelable broadcastRequestParcelable,
IBroadcastListener listener, String packageName, @Nullable String attributionTag) {
+ // Permissions check
enforceBluetoothPrivilegedPermission(mContext);
BroadcastPermissions.enforceBroadcastPermission(
mContext, CallerIdentity.fromBinder(mContext, packageName, attributionTag));
+
mBroadcastProviderManager.startBroadcast(
broadcastRequestParcelable.getBroadcastRequest(), listener);
}
@Override
- public void stopBroadcast(IBroadcastListener listener) {
+ public void stopBroadcast(IBroadcastListener listener, String packageName,
+ @Nullable String attributionTag) {
+ // Permissions check
+ enforceBluetoothPrivilegedPermission(mContext);
+ CallerIdentity identity = CallerIdentity.fromBinder(mContext, packageName, attributionTag);
+ BroadcastPermissions.enforceBroadcastPermission(mContext, identity);
+
mBroadcastProviderManager.stopBroadcast(listener);
}
@@ -156,7 +167,6 @@
mBluetoothReceiver,
new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
mFastPairManager.initiate();
- mPresenceManager.initiate();
break;
}
}
diff --git a/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java b/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java
deleted file mode 100644
index 382c47a..0000000
--- a/nearby/service/java/com/android/server/nearby/presence/PresenceManager.java
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.nearby.presence;
-
-import static com.android.server.nearby.NearbyService.TAG;
-
-import android.annotation.Nullable;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.nearby.NearbyDevice;
-import android.nearby.NearbyManager;
-import android.nearby.PresenceScanFilter;
-import android.nearby.PublicCredential;
-import android.nearby.ScanCallback;
-import android.nearby.ScanRequest;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.android.server.nearby.common.locator.Locator;
-import com.android.server.nearby.common.locator.LocatorContextWrapper;
-
-import java.util.Locale;
-import java.util.concurrent.Executors;
-
-/** PresenceManager is the class initiated in nearby service to handle presence related work. */
-public class PresenceManager {
-
- final LocatorContextWrapper mLocatorContextWrapper;
- final Locator mLocator;
- private final IntentFilter mIntentFilter;
-
- private final ScanCallback mScanCallback =
- new ScanCallback() {
- @Override
- public void onDiscovered(@NonNull NearbyDevice device) {
- Log.i(TAG, "[PresenceManager] discovered Device.");
- }
-
- @Override
- public void onUpdated(@NonNull NearbyDevice device) {}
-
- @Override
- public void onLost(@NonNull NearbyDevice device) {}
- };
-
- private final BroadcastReceiver mScreenBroadcastReceiver =
- new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- NearbyManager manager = getNearbyManager();
- if (manager == null) {
- Log.e(TAG, "Nearby Manager is null");
- return;
- }
- if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
- Log.d(TAG, "Start CHRE scan.");
- byte[] secreteId = {1, 0, 0, 0};
- byte[] authenticityKey = {2, 0, 0, 0};
- byte[] publicKey = {3, 0, 0, 0};
- byte[] encryptedMetaData = {4, 0, 0, 0};
- byte[] encryptedMetaDataTag = {5, 0, 0, 0};
- PublicCredential publicCredential =
- new PublicCredential.Builder(
- secreteId,
- authenticityKey,
- publicKey,
- encryptedMetaData,
- encryptedMetaDataTag)
- .build();
- PresenceScanFilter presenceScanFilter =
- new PresenceScanFilter.Builder()
- .setMaxPathLoss(3)
- .addCredential(publicCredential)
- .addPresenceAction(1)
- .build();
- ScanRequest scanRequest =
- new ScanRequest.Builder()
- .setScanType(ScanRequest.SCAN_TYPE_NEARBY_PRESENCE)
- .addScanFilter(presenceScanFilter)
- .build();
- Log.i(
- TAG,
- String.format(
- Locale.getDefault(),
- "[PresenceManager] Start Presence scan with request: %s",
- scanRequest.toString()));
- manager.startScan(
- scanRequest, Executors.newSingleThreadExecutor(), mScanCallback);
- } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
- Log.d(TAG, "Stop CHRE scan.");
- manager.stopScan(mScanCallback);
- }
- }
- };
-
- public PresenceManager(LocatorContextWrapper contextWrapper) {
- mLocatorContextWrapper = contextWrapper;
- mLocator = mLocatorContextWrapper.getLocator();
- mIntentFilter = new IntentFilter();
- }
-
- /** Null when the Nearby Service is not available. */
- @Nullable
- private NearbyManager getNearbyManager() {
- return (NearbyManager)
- mLocatorContextWrapper
- .getApplicationContext()
- .getSystemService(Context.NEARBY_SERVICE);
- }
-
- /** Function called when nearby service start. */
- public void initiate() {
- mIntentFilter.addAction(Intent.ACTION_SCREEN_ON);
- mIntentFilter.addAction(Intent.ACTION_SCREEN_OFF);
- mLocatorContextWrapper
- .getContext()
- .registerReceiver(mScreenBroadcastReceiver, mIntentFilter);
- }
-}
diff --git a/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java b/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java
index 4cb6d8d..e8aea79 100644
--- a/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java
+++ b/nearby/service/java/com/android/server/nearby/provider/BleDiscoveryProvider.java
@@ -74,13 +74,15 @@
builder.setName(record.getDeviceName());
}
Map<ParcelUuid, byte[]> serviceDataMap = record.getServiceData();
- byte[] fastPairData = serviceDataMap.get(FAST_PAIR_UUID);
- if (fastPairData != null) {
- builder.setData(serviceDataMap.get(FAST_PAIR_UUID));
- } else {
- byte [] presenceData = serviceDataMap.get(PRESENCE_UUID);
- if (presenceData != null) {
- builder.setData(serviceDataMap.get(PRESENCE_UUID));
+ if (serviceDataMap != null) {
+ byte[] fastPairData = serviceDataMap.get(FAST_PAIR_UUID);
+ if (fastPairData != null) {
+ builder.setData(serviceDataMap.get(FAST_PAIR_UUID));
+ } else {
+ byte[] presenceData = serviceDataMap.get(PRESENCE_UUID);
+ if (presenceData != null) {
+ builder.setData(serviceDataMap.get(PRESENCE_UUID));
+ }
}
}
}
diff --git a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
index 6b9bce9..dd9cbb0 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyDeviceParcelableTest.java
@@ -16,6 +16,8 @@
package android.nearby.cts;
+import static android.nearby.ScanRequest.SCAN_TYPE_NEARBY_PRESENCE;
+
import static com.google.common.truth.Truth.assertThat;
import android.nearby.NearbyDevice;
@@ -49,6 +51,7 @@
public void setUp() {
mBuilder =
new NearbyDeviceParcelable.Builder()
+ .setScanType(SCAN_TYPE_NEARBY_PRESENCE)
.setName("testDevice")
.setMedium(NearbyDevice.Medium.BLE)
.setRssi(RSSI)
@@ -77,8 +80,8 @@
assertThat(nearbyDeviceParcelable.toString())
.isEqualTo(
- "NearbyDeviceParcelable[name=testDevice, medium=BLE, txPower=0, rssi=-60,"
- + " action=0, bluetoothAddress="
+ "NearbyDeviceParcelable[scanType=2, name=testDevice, medium=BLE, "
+ + "txPower=0, rssi=-60, action=0, bluetoothAddress="
+ BLUETOOTH_ADDRESS
+ ", fastPairModelId=null, data=null, salt=null]");
}
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 6824ca6..7696a61 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
@@ -128,6 +128,14 @@
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
+ public void test_stopScan_noPrivilegedPermission() {
+ mNearbyManager.startScan(mScanRequest, EXECUTOR, mScanCallback);
+ mUiAutomation.dropShellPermissionIdentity();
+ assertThrows(SecurityException.class, () -> mNearbyManager.stopScan(mScanCallback));
+ }
+
+ @Test
+ @SdkSuppress(minSdkVersion = 32, codeName = "T")
public void testStartStopBroadcast() throws InterruptedException {
PrivateCredential credential = new PrivateCredential.Builder(SECRETE_ID, AUTHENTICITY_KEY,
META_DATA_ENCRYPTION_KEY, DEVICE_NAME)
diff --git a/nearby/tests/integration/untrusted/Android.bp b/nearby/tests/integration/untrusted/Android.bp
index 53dbfb7..57499e4 100644
--- a/nearby/tests/integration/untrusted/Android.bp
+++ b/nearby/tests/integration/untrusted/Android.bp
@@ -21,10 +21,14 @@
defaults: ["mts-target-sdk-version-current"],
sdk_version: "test_current",
- srcs: ["src/**/*.kt"],
+ srcs: [
+ "src/**/*.java",
+ "src/**/*.kt",
+ ],
static_libs: [
"androidx.test.ext.junit",
"androidx.test.rules",
+ "androidx.test.uiautomator_uiautomator",
"junit",
"kotlin-test",
"truth-prebuilt",
diff --git a/nearby/tests/integration/untrusted/src/android/nearby/integration/untrusted/NearbyManagerTest.kt b/nearby/tests/integration/untrusted/src/android/nearby/integration/untrusted/NearbyManagerTest.kt
index 3bfac6d..7bf9f63 100644
--- a/nearby/tests/integration/untrusted/src/android/nearby/integration/untrusted/NearbyManagerTest.kt
+++ b/nearby/tests/integration/untrusted/src/android/nearby/integration/untrusted/NearbyManagerTest.kt
@@ -28,12 +28,14 @@
import android.nearby.ScanRequest
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.uiautomator.LogcatWaitMixin
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertThrows
import org.junit.Before
-import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
+import java.time.Duration
+import java.util.Calendar
@RunWith(AndroidJUnit4::class)
class NearbyManagerTest {
@@ -75,13 +77,9 @@
}
}
- /**
- * Verify untrusted app can't stop scan because it needs BLUETOOTH_PRIVILEGED
- * permission which is not for use by third-party applications.
- */
+ /** Verify untrusted app can't stop scan because it never successfully registers a callback. */
@Test
- @Ignore("Permission check for stopXXX not yet implement: b/229338477#comment24")
- fun testNearbyManagerStopScan_fromUnTrustedApp_throwsException() {
+ fun testNearbyManagerStopScan_fromUnTrustedApp_logsError() {
val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
val scanCallback = object : ScanCallback {
override fun onDiscovered(device: NearbyDevice) {}
@@ -90,10 +88,17 @@
override fun onLost(device: NearbyDevice) {}
}
+ val startTime = Calendar.getInstance().time
- assertThrows(SecurityException::class.java) {
- nearbyManager.stopScan(scanCallback)
- }
+ nearbyManager.stopScan(scanCallback)
+
+ assertThat(
+ LogcatWaitMixin().waitForSpecificLog(
+ "Cannot stop scan with this callback because it is never registered.",
+ startTime,
+ WAIT_INVALID_OPERATIONS_LOGS_TIMEOUT
+ )
+ ).isTrue()
}
/**
@@ -127,17 +132,26 @@
}
/**
- * Verify untrusted app can't stop broadcast because it needs BLUETOOTH_PRIVILEGED
- * permission which is not for use by third-party applications.
+ * Verify untrusted app can't stop broadcast because it never successfully registers a callback.
*/
@Test
- @Ignore("Permission check for stopXXX not yet implement: b/229338477#comment24")
- fun testNearbyManagerStopBroadcast_fromUnTrustedApp_throwsException() {
+ fun testNearbyManagerStopBroadcast_fromUnTrustedApp_logsError() {
val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager
val broadcastCallback = BroadcastCallback { }
+ val startTime = Calendar.getInstance().time
- assertThrows(SecurityException::class.java) {
- nearbyManager.stopBroadcast(broadcastCallback)
- }
+ nearbyManager.stopBroadcast(broadcastCallback)
+
+ assertThat(
+ LogcatWaitMixin().waitForSpecificLog(
+ "Cannot stop broadcast with this callback because it is never registered.",
+ startTime,
+ WAIT_INVALID_OPERATIONS_LOGS_TIMEOUT
+ )
+ ).isTrue()
+ }
+
+ companion object {
+ private val WAIT_INVALID_OPERATIONS_LOGS_TIMEOUT = Duration.ofSeconds(5)
}
}
diff --git a/nearby/tests/integration/untrusted/src/androidx/test/uiautomator/LogcatParser.kt b/nearby/tests/integration/untrusted/src/androidx/test/uiautomator/LogcatParser.kt
new file mode 100644
index 0000000..604e6df
--- /dev/null
+++ b/nearby/tests/integration/untrusted/src/androidx/test/uiautomator/LogcatParser.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.test.uiautomator
+
+import java.text.SimpleDateFormat
+import java.util.Date
+import java.util.Locale
+
+/** A parser for logcat logs processing. */
+object LogcatParser {
+ private val LOGCAT_LOGS_PATTERN = "^\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d{3} ".toRegex()
+ private const val LOGCAT_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss.SSS"
+
+ /**
+ * Filters out the logcat logs which contains specific log and appears not before specific time.
+ *
+ * @param logcatLogs the concatenated logcat logs to filter
+ * @param specificLog the log string expected to appear
+ * @param startTime the time point to start finding the specific log
+ * @return a list of logs that match the condition
+ */
+ fun findSpecificLogAfter(
+ logcatLogs: String,
+ specificLog: String,
+ startTime: Date
+ ): List<String> = logcatLogs.split("\n")
+ .filter { it.contains(specificLog) && !parseLogTime(it)!!.before(startTime) }
+
+ /**
+ * Parses the logcat log string to extract the timestamp.
+ *
+ * @param logString the log string to parse
+ * @return the timestamp of the log
+ */
+ private fun parseLogTime(logString: String): Date? =
+ SimpleDateFormat(LOGCAT_DATE_FORMAT, Locale.US)
+ .parse(LOGCAT_LOGS_PATTERN.find(logString)!!.value)
+}
diff --git a/nearby/tests/integration/untrusted/src/androidx/test/uiautomator/LogcatWaitMixin.java b/nearby/tests/integration/untrusted/src/androidx/test/uiautomator/LogcatWaitMixin.java
new file mode 100644
index 0000000..86e39dc
--- /dev/null
+++ b/nearby/tests/integration/untrusted/src/androidx/test/uiautomator/LogcatWaitMixin.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.test.uiautomator;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.Date;
+
+/** A helper class to wait the specific log appear in the logcat logs. */
+public class LogcatWaitMixin extends WaitMixin<UiDevice> {
+
+ private static final String LOG_TAG = LogcatWaitMixin.class.getSimpleName();
+
+ public LogcatWaitMixin() {
+ this(UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()));
+ }
+
+ public LogcatWaitMixin(UiDevice device) {
+ super(device);
+ }
+
+ /**
+ * Waits the {@code specificLog} appear in the logcat logs after the specific {@code startTime}.
+ *
+ * @param waitTime the maximum time for waiting
+ * @return true if the specific log appear within timeout and after the startTime
+ */
+ public boolean waitForSpecificLog(
+ @NonNull String specificLog, @NonNull Date startTime, @NonNull Duration waitTime) {
+ return wait(createWaitCondition(specificLog, startTime), waitTime.toMillis());
+ }
+
+ @NonNull
+ Condition<UiDevice, Boolean> createWaitCondition(
+ @NonNull String specificLog, @NonNull Date startTime) {
+ return new Condition<UiDevice, Boolean>() {
+ @Override
+ Boolean apply(UiDevice device) {
+ String logcatLogs;
+ try {
+ logcatLogs = device.executeShellCommand("logcat -v time -v year -d");
+ } catch (IOException e) {
+ Log.e(LOG_TAG, "Fail to dump logcat logs on the device!", e);
+ return Boolean.FALSE;
+ }
+ return !LogcatParser.INSTANCE
+ .findSpecificLogAfter(logcatLogs, specificLog, startTime)
+ .isEmpty();
+ }
+ };
+ }
+}
diff --git a/nearby/tests/multidevices/README.md b/nearby/tests/multidevices/README.md
new file mode 100644
index 0000000..1208451
--- /dev/null
+++ b/nearby/tests/multidevices/README.md
@@ -0,0 +1,145 @@
+# Nearby Mainline Fast Pair end-to-end tests
+
+This document refers to the Mainline Fast Pair project source code in the
+packages/modules/Connectivity/nearby. This is not an officially supported Google
+product.
+
+## About the Fast Pair Project
+
+The Connectivity Nearby mainline module is created in the Android T to host
+Better Together related functionality. Fast Pair is one of the main
+functionalities to provide seamless onboarding and integrated experiences for
+peripheral devices (for example, headsets like Google Pixel Buds) in the Nearby
+component.
+
+## Fully automated test
+
+### Prerequisites
+
+The fully automated end-to-end (e2e) tests are host-driven tests (which means
+test logics are in the host test scripts) using Mobly runner in Python. The two
+phones are installed with the test snippet
+`NearbyMultiDevicesClientsSnippets.apk` in the test time to let the host scripts
+control both sides for testing. Here's the overview of the test environment.
+
+Workstation (runs Python test scripts and controls Android devices through USB
+ADB) \
+├── Phone 1: As Fast Pair seeker role, to scan, pair Fast Pair devices nearby \
+└── Phone 2: As Fast Pair provider role, to simulate a Fast Pair device (for
+example, a Bluetooth headset)
+
+Note: These two phones need to be physically within 0.3 m of each other.
+
+### Prepare Phone 1 (Fast Pair seeker role)
+
+This is the phone to scan/pair Fast Pair devices nearby using the Nearby
+Mainline module. Test it by flashing with the Android T ROM.
+
+### Prepare Phone 2 (Fast Pair provider role)
+
+This is the phone to simulate a Fast Pair device (for example, a Bluetooth
+headset). Flash it with a customized ROM with the following changes:
+
+* Adjust Bluetooth profile configurations. \
+ The Fast Pair provider simulator is an opposite role to the seeker. It needs
+ to enable/disable the following Bluetooth profile:
+ * Disable A2DP (profile_supported_a2dp)
+ * Disable the AVRCP controller (profile_supported_avrcp_controller)
+ * Enable A2DP sink (profile_supported_a2dp_sink)
+ * Enable the HFP client connection service (profile_supported_hfpclient,
+ hfp_client_connection_service_enabled)
+ * Enable the AVRCP target (profile_supported_avrcp_target)
+ * Enable the automatic audio focus request
+ (a2dp_sink_automatically_request_audio_focus)
+* Adjust Bluetooth TX power limitation in Bluetooth module and disable the
+ Fast Pair in Google Play service (aka GMS)
+
+```shell
+adb root
+adb shell am broadcast \
+ -a 'com.google.android.gms.phenotype.FLAG_OVERRIDE' \
+ --es package "com.google.android.gms.nearby" \
+ --es user "\*" \
+ --esa flags "enabled" \
+ --esa types "boolean" \
+ --esa values "false" \
+ com.google.android.gms
+```
+
+### Running tests
+
+To run the tests, enter:
+
+```shell
+atest -v CtsNearbyMultiDevicesTestSuite
+```
+
+## Manual testing the seeker side with headsets
+
+Use this testing with headsets such as Google Pixel buds.
+
+The `FastPairTestDataProviderService.apk` is a run-time configurable Fast Pair
+data provider service (`FastPairDataProviderService`):
+
+`packages/modules/Connectivity/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider`
+
+It has a test data manager(`FastPairTestDataManager`) to receive intent
+broadcasts to add or clear the test data cache (`FastPairTestDataCache`). This
+cache provides the data to return to the Fast Pair module for onXXX calls (for
+example, `onLoadFastPairAntispoofKeyDeviceMetadata`) so you can feed the
+metadata for your device.
+
+Here are some sample uses:
+
+* Send FastPairAntispoofKeyDeviceMetadata for PixelBuds-A to
+ FastPairTestDataCache \
+ `./fast_pair_data_provider_shell.sh -m=718c17
+ -a=../test_data/fastpair/pixelbuds-a_antispoofkey_devicemeta_json.txt`
+* Send FastPairAccountDevicesMetadata for PixelBuds-A to FastPairTestDataCache
+ \
+ `./fast_pair_data_provider_shell.sh
+ -d=../test_data/fastpair/pixelbuds-a_account_devicemeta_json.txt`
+* Send FastPairAntispoofKeyDeviceMetadata for Provider Simulator to
+ FastPairTestDataCache \
+ `./fast_pair_data_provider_shell.sh -m=00000c
+ -a=../test_data/fastpair/simulator_antispoofkey_devicemeta_json.txt`
+* Send FastPairAccountDevicesMetadata for Provider Simulator to
+ FastPairTestDataCache \
+ `./fast_pair_data_provider_shell.sh
+ -d=../test_data/fastpair/simulator_account_devicemeta_json.txt`
+* Clear FastPairTestDataCache \
+ `./fast_pair_data_provider_shell.sh -c`
+
+See
+[host/tool/fast_pair_data_provider_shell.sh](host/tool/fast_pair_data_provider_shell.sh)
+for more documentation.
+
+To install the data provider as system private app, consider remounting the
+system partition:
+
+```
+adb root && adb remount
+```
+
+Push it in:
+
+```
+adb push ${ANDROID_PRODUCT_OUT}/system/app/NearbyFastPairSeekerDataProvider
+/system/priv-app/
+```
+
+Then reboot:
+
+```
+adb reboot
+```
+
+## Manual testing the seeker side with provider simulator app
+
+The `NearbyFastPairProviderSimulatorApp.apk` is a simple Android app to let you
+control the state of the Fast Pair provider simulator. Install this app on phone
+2 (Fast Pair provider role) to work correctly.
+
+See
+[clients/test_support/fastpair_provider/simulator_ap/Android.bp](clients/test_support/fastpair_provider/simulator_ap/Android.bp)
+for more documentation.
diff --git a/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java b/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
index e250254..8a18cca 100644
--- a/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
+++ b/nearby/tests/unit/src/com/android/server/nearby/NearbyServiceTest.java
@@ -87,8 +87,19 @@
}
@Test
+ public void test_unregister_noPrivilegedPermission_throwsException() {
+ mUiAutomation.dropShellPermissionIdentity();
+ assertThrows(java.lang.SecurityException.class,
+ () -> mService.unregisterScanListener(mScanListener, PACKAGE_NAME,
+ /* attributionTag= */ null));
+ }
+
+ @Test
public void test_unregister() {
- mService.unregisterScanListener(mScanListener);
+ setMockInjector(/* isMockOpsAllowed= */ true);
+ mService.registerScanListener(mScanRequest, mScanListener, PACKAGE_NAME,
+ /* attributionTag= */ null);
+ mService.unregisterScanListener(mScanListener, PACKAGE_NAME, /* attributionTag= */ null);
}
private ScanRequest createScanRequest() {
diff --git a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java b/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
deleted file mode 100644
index 3b34655..0000000
--- a/nearby/tests/unit/src/com/android/server/nearby/presence/PresenceManagerTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.nearby.presence;
-
-import androidx.test.filters.SdkSuppress;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.mockito.MockitoAnnotations;
-
-public class PresenceManagerTest {
-
- @Before
- public void setup() {
- MockitoAnnotations.initMocks(this);
- }
-
- @Test
- @SdkSuppress(minSdkVersion = 32, codeName = "T")
- public void testInit() {}
-}
diff --git a/service-t/src/com/android/server/net/NetworkStatsService.java b/service-t/src/com/android/server/net/NetworkStatsService.java
index 217a9a6..b2d8b5e 100644
--- a/service-t/src/com/android/server/net/NetworkStatsService.java
+++ b/service-t/src/com/android/server/net/NetworkStatsService.java
@@ -159,8 +159,11 @@
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
@@ -374,9 +377,19 @@
private long mLastStatsSessionPoll;
- /** Map from UID to number of opened sessions */
- @GuardedBy("mOpenSessionCallsPerUid")
+ private final Object mOpenSessionCallsLock = new Object();
+ /**
+ * Map from UID to number of opened sessions. This is used for rate-limt an app to open
+ * session frequently
+ */
+ @GuardedBy("mOpenSessionCallsLock")
private final SparseIntArray mOpenSessionCallsPerUid = new SparseIntArray();
+ /**
+ * Map from key {@code OpenSessionKey} to count of opened sessions. This is for recording
+ * the caller of open session and it is only for debugging.
+ */
+ @GuardedBy("mOpenSessionCallsLock")
+ private final HashMap<OpenSessionKey, Integer> mOpenSessionCallsPerCaller = new HashMap<>();
private final static int DUMP_STATS_SESSION_COUNT = 20;
@@ -407,6 +420,44 @@
Clock.systemUTC());
}
+ /**
+ * This class is a key that used in {@code mOpenSessionCallsPerCaller} to identify the count of
+ * the caller.
+ */
+ private static class OpenSessionKey {
+ public final int uid;
+ public final String packageName;
+
+ OpenSessionKey(int uid, @NonNull String packageName) {
+ this.uid = uid;
+ this.packageName = packageName;
+ }
+
+ @Override
+ public String toString() {
+ final StringBuilder sb = new StringBuilder();
+ sb.append("{");
+ sb.append("uid=").append(uid).append(",");
+ sb.append("package=").append(packageName);
+ sb.append("}");
+ return sb.toString();
+ }
+
+ @Override
+ public boolean equals(@NonNull Object o) {
+ if (this == o) return true;
+ if (o.getClass() != getClass()) return false;
+
+ final OpenSessionKey key = (OpenSessionKey) o;
+ return this.uid == key.uid && TextUtils.equals(this.packageName, key.packageName);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(uid, packageName);
+ }
+ }
+
private final class NetworkStatsHandler extends Handler {
NetworkStatsHandler(@NonNull Looper looper) {
super(looper);
@@ -794,16 +845,27 @@
return openSessionInternal(flags, callingPackage);
}
- private boolean isRateLimitedForPoll(int callingUid) {
- if (callingUid == android.os.Process.SYSTEM_UID) {
- return false;
- }
-
+ private boolean isRateLimitedForPoll(@NonNull OpenSessionKey key) {
final long lastCallTime;
final long now = SystemClock.elapsedRealtime();
- synchronized (mOpenSessionCallsPerUid) {
- int calls = mOpenSessionCallsPerUid.get(callingUid, 0);
- mOpenSessionCallsPerUid.put(callingUid, calls + 1);
+
+ synchronized (mOpenSessionCallsLock) {
+ Integer callsPerCaller = mOpenSessionCallsPerCaller.get(key);
+ if (callsPerCaller == null) {
+ mOpenSessionCallsPerCaller.put((key), 1);
+ } else {
+ mOpenSessionCallsPerCaller.put(key, Integer.sum(callsPerCaller, 1));
+ }
+
+ int callsPerUid = mOpenSessionCallsPerUid.get(key.uid, 0);
+ mOpenSessionCallsPerUid.put(key.uid, callsPerUid + 1);
+
+ if (key.uid == android.os.Process.SYSTEM_UID) {
+ return false;
+ }
+
+ // To avoid a non-system user to be rate-limited after system users open sessions,
+ // so update mLastStatsSessionPoll after checked if the uid is SYSTEM_UID.
lastCallTime = mLastStatsSessionPoll;
mLastStatsSessionPoll = now;
}
@@ -811,7 +873,7 @@
return now - lastCallTime < POLL_RATE_LIMIT_MS;
}
- private int restrictFlagsForCaller(int flags) {
+ private int restrictFlagsForCaller(int flags, @NonNull String callingPackage) {
// All non-privileged callers are not allowed to turn off POLL_ON_OPEN.
final boolean isPrivileged = PermissionUtils.checkAnyPermissionOf(mContext,
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
@@ -821,14 +883,15 @@
}
// Non-system uids are rate limited for POLL_ON_OPEN.
final int callingUid = Binder.getCallingUid();
- flags = isRateLimitedForPoll(callingUid)
+ final OpenSessionKey key = new OpenSessionKey(callingUid, callingPackage);
+ flags = isRateLimitedForPoll(key)
? flags & (~NetworkStatsManager.FLAG_POLL_ON_OPEN)
: flags;
return flags;
}
private INetworkStatsSession openSessionInternal(final int flags, final String callingPackage) {
- final int restrictedFlags = restrictFlagsForCaller(flags);
+ final int restrictedFlags = restrictFlagsForCaller(flags, callingPackage);
if ((restrictedFlags & (NetworkStatsManager.FLAG_POLL_ON_OPEN
| NetworkStatsManager.FLAG_POLL_FORCE)) != 0) {
final long ident = Binder.clearCallingIdentity();
@@ -1938,6 +2001,9 @@
for (int uid : uids) {
deleteKernelTagData(uid);
}
+
+ // TODO: Remove the UID's entries from mOpenSessionCallsPerUid and
+ // mOpenSessionCallsPerCaller
}
/**
@@ -2061,25 +2127,21 @@
pw.decreaseIndent();
// Get the top openSession callers
- final SparseIntArray calls;
- synchronized (mOpenSessionCallsPerUid) {
- calls = mOpenSessionCallsPerUid.clone();
+ final HashMap calls;
+ synchronized (mOpenSessionCallsLock) {
+ calls = new HashMap<>(mOpenSessionCallsPerCaller);
}
-
- final int N = calls.size();
- final long[] values = new long[N];
- for (int j = 0; j < N; j++) {
- values[j] = ((long) calls.valueAt(j) << 32) | calls.keyAt(j);
- }
- Arrays.sort(values);
-
- pw.println("Top openSession callers (uid=count):");
+ final List<Map.Entry<OpenSessionKey, Integer>> list = new ArrayList<>(calls.entrySet());
+ Collections.sort(list,
+ (left, right) -> Integer.compare(left.getValue(), right.getValue()));
+ final int num = list.size();
+ final int end = Math.max(0, num - DUMP_STATS_SESSION_COUNT);
+ pw.println("Top openSession callers:");
pw.increaseIndent();
- final int end = Math.max(0, N - DUMP_STATS_SESSION_COUNT);
- for (int j = N - 1; j >= end; j--) {
- final int uid = (int) (values[j] & 0xffffffff);
- final int count = (int) (values[j] >> 32);
- pw.print(uid); pw.print("="); pw.println(count);
+ for (int j = num - 1; j >= end; j--) {
+ final Map.Entry<OpenSessionKey, Integer> entry = list.get(j);
+ pw.print(entry.getKey()); pw.print("="); pw.println(entry.getValue());
+
}
pw.decreaseIndent();
pw.println();
diff --git a/service/ServiceConnectivityResources/Android.bp b/service/ServiceConnectivityResources/Android.bp
index f491cc7..02b2875 100644
--- a/service/ServiceConnectivityResources/Android.bp
+++ b/service/ServiceConnectivityResources/Android.bp
@@ -23,6 +23,7 @@
name: "ServiceConnectivityResources",
sdk_version: "module_30",
min_sdk_version: "30",
+ target_sdk_version: "33",
resource_dirs: [
"res",
],
diff --git a/tests/common/java/android/net/LinkPropertiesTest.java b/tests/common/java/android/net/LinkPropertiesTest.java
index 345a78d..b66a979 100644
--- a/tests/common/java/android/net/LinkPropertiesTest.java
+++ b/tests/common/java/android/net/LinkPropertiesTest.java
@@ -1262,13 +1262,14 @@
}
@Test @IgnoreUpTo(Build.VERSION_CODES.S_V2)
+ @EnableCompatChanges({LinkProperties.EXCLUDED_ROUTES})
public void testHasExcludeRoute() {
LinkProperties lp = new LinkProperties();
- lp.setInterfaceName("VPN");
- lp.addRoute(new RouteInfo(new IpPrefix(ADDRV4, 2), RTN_UNICAST));
+ lp.setInterfaceName("tun0");
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV4, 24), RTN_UNICAST));
lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 0), RTN_UNICAST));
assertFalse(lp.hasExcludeRoute());
- lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 2), RTN_THROW));
+ lp.addRoute(new RouteInfo(new IpPrefix(ADDRV6, 32), RTN_THROW));
assertTrue(lp.hasExcludeRoute());
}
diff --git a/tests/cts/hostside/Android.bp b/tests/cts/hostside/Android.bp
index b684068..c47ccbf 100644
--- a/tests/cts/hostside/Android.bp
+++ b/tests/cts/hostside/Android.bp
@@ -26,6 +26,7 @@
"tradefed",
],
static_libs: [
+ "CompatChangeGatingTestBase",
"modules-utils-build-testing",
],
// Tag this module as a cts test artifact
diff --git a/tests/cts/hostside/app3/Android.bp b/tests/cts/hostside/app3/Android.bp
new file mode 100644
index 0000000..69667ce
--- /dev/null
+++ b/tests/cts/hostside/app3/Android.bp
@@ -0,0 +1,50 @@
+//
+// Copyright (C) 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+java_defaults {
+ name: "CtsHostsideNetworkTestsApp3Defaults",
+ srcs: ["src/**/*.java"],
+ libs: [
+ "junit",
+ ],
+ static_libs: [
+ "ctstestrunner-axt",
+ "truth-prebuilt",
+ ],
+
+ // Tag this module as a cts test artifact
+ test_suites: [
+ "cts",
+ "general-tests",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkTestsApp3",
+ defaults: [
+ "cts_support_defaults",
+ "CtsHostsideNetworkTestsApp3Defaults",
+ ],
+}
+
+android_test_helper_app {
+ name: "CtsHostsideNetworkTestsApp3PreT",
+ target_sdk_version: "31",
+ defaults: [
+ "cts_support_defaults",
+ "CtsHostsideNetworkTestsApp3Defaults",
+ ],
+}
diff --git a/tests/cts/hostside/app3/AndroidManifest.xml b/tests/cts/hostside/app3/AndroidManifest.xml
new file mode 100644
index 0000000..eabcacb
--- /dev/null
+++ b/tests/cts/hostside/app3/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2022 The Android Open Source Project
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.cts.net.hostside.app3">
+
+ <application android:debuggable="true">
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.cts.net.hostside.app3" />
+
+</manifest>
diff --git a/tests/cts/hostside/app3/src/com/android/cts/net/hostside/app3/ExcludedRoutesGatingTest.java b/tests/cts/hostside/app3/src/com/android/cts/net/hostside/app3/ExcludedRoutesGatingTest.java
new file mode 100644
index 0000000..a1a8209
--- /dev/null
+++ b/tests/cts/hostside/app3/src/com/android/cts/net/hostside/app3/ExcludedRoutesGatingTest.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net.hostside.app3;
+
+import static org.junit.Assert.assertEquals;
+
+import android.Manifest;
+import android.net.IpPrefix;
+import android.net.LinkProperties;
+import android.net.RouteInfo;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Tests to verify {@link LinkProperties#getRoutes} behavior, depending on
+ * {@LinkProperties#EXCLUDED_ROUTES} change state.
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExcludedRoutesGatingTest {
+ @Before
+ public void setUp() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .adoptShellPermissionIdentity(Manifest.permission.LOG_COMPAT_CHANGE,
+ Manifest.permission.READ_COMPAT_CHANGE_CONFIG);
+ }
+
+ @After
+ public void tearDown() {
+ InstrumentationRegistry.getInstrumentation().getUiAutomation()
+ .dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void testExcludedRoutesChangeEnabled() {
+ final LinkProperties lp = makeLinkPropertiesWithExcludedRoutes();
+
+ // Excluded routes change is enabled: non-RTN_UNICAST routes are visible.
+ assertEquals(2, lp.getRoutes().size());
+ assertEquals(2, lp.getAllRoutes().size());
+ }
+
+ @Test
+ public void testExcludedRoutesChangeDisabled() {
+ final LinkProperties lp = makeLinkPropertiesWithExcludedRoutes();
+
+ // Excluded routes change is disabled: non-RTN_UNICAST routes are filtered out.
+ assertEquals(0, lp.getRoutes().size());
+ assertEquals(0, lp.getAllRoutes().size());
+ }
+
+ private LinkProperties makeLinkPropertiesWithExcludedRoutes() {
+ final LinkProperties lp = new LinkProperties();
+
+ lp.addRoute(new RouteInfo(new IpPrefix("10.0.0.0/8"), null, null, RouteInfo.RTN_THROW));
+ lp.addRoute(new RouteInfo(new IpPrefix("2001:db8::/64"), null, null,
+ RouteInfo.RTN_UNREACHABLE));
+
+ return lp;
+ }
+}
diff --git a/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java b/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java
new file mode 100644
index 0000000..b65fb6b
--- /dev/null
+++ b/tests/cts/hostside/src/com/android/cts/net/HostsideLinkPropertiesGatingTests.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.net;
+
+import android.compat.cts.CompatChangeGatingTestCase;
+
+import java.util.Set;
+
+/**
+ * Tests for the {@link android.net.LinkProperties#EXCLUDED_ROUTES} compatibility change.
+ */
+public class HostsideLinkPropertiesGatingTests extends CompatChangeGatingTestCase {
+ private static final String TEST_APK = "CtsHostsideNetworkTestsApp3.apk";
+ private static final String TEST_APK_PRE_T = "CtsHostsideNetworkTestsApp3PreT.apk";
+ private static final String TEST_PKG = "com.android.cts.net.hostside.app3";
+ private static final String TEST_CLASS = ".ExcludedRoutesGatingTest";
+
+ private static final long EXCLUDED_ROUTES_CHANGE_ID = 186082280;
+
+ protected void tearDown() throws Exception {
+ uninstallPackage(TEST_PKG, true);
+ }
+
+ public void testExcludedRoutesChangeEnabled() throws Exception {
+ installPackage(TEST_APK, true);
+ runDeviceCompatTest("testExcludedRoutesChangeEnabled");
+ }
+
+ public void testExcludedRoutesChangeDisabledPreT() throws Exception {
+ installPackage(TEST_APK_PRE_T, true);
+ runDeviceCompatTest("testExcludedRoutesChangeDisabled");
+ }
+
+ public void testExcludedRoutesChangeDisabledByOverride() throws Exception {
+ installPackage(TEST_APK, true);
+ runDeviceCompatTestWithChangeDisabled("testExcludedRoutesChangeDisabled");
+ }
+
+ public void testExcludedRoutesChangeEnabledByOverridePreT() throws Exception {
+ installPackage(TEST_APK_PRE_T, true);
+ runDeviceCompatTestWithChangeEnabled("testExcludedRoutesChangeEnabled");
+ }
+
+ private void runDeviceCompatTest(String methodName) throws Exception {
+ runDeviceCompatTest(TEST_PKG, TEST_CLASS, methodName, Set.of(), Set.of());
+ }
+
+ private void runDeviceCompatTestWithChangeEnabled(String methodName) throws Exception {
+ runDeviceCompatTest(TEST_PKG, TEST_CLASS, methodName, Set.of(EXCLUDED_ROUTES_CHANGE_ID),
+ Set.of());
+ }
+
+ private void runDeviceCompatTestWithChangeDisabled(String methodName) throws Exception {
+ runDeviceCompatTest(TEST_PKG, TEST_CLASS, methodName, Set.of(),
+ Set.of(EXCLUDED_ROUTES_CHANGE_ID));
+ }
+}
diff --git a/tests/cts/net/AndroidTestTemplate.xml b/tests/cts/net/AndroidTestTemplate.xml
index 48a1c79..33f3af5 100644
--- a/tests/cts/net/AndroidTestTemplate.xml
+++ b/tests/cts/net/AndroidTestTemplate.xml
@@ -38,4 +38,20 @@
<option name="hidden-api-checks" value="false" />
<option name="isolated-storage" value="false" />
</test>
+ <!-- When this test is run in a Mainline context (e.g. with `mts-tradefed`), only enable it if
+ one of the Mainline modules below is present on the device used for testing. -->
+ <object type="module_controller" class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController">
+ <!-- Tethering Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.tethering" />
+ <!-- Tethering Module (AOSP version). -->
+ <option name="mainline-module-package-name" value="com.android.tethering" />
+ <!-- NetworkStack Module (internal version). Should always be installed with CaptivePortalLogin. -->
+ <option name="mainline-module-package-name" value="com.google.android.networkstack" />
+ <!-- NetworkStack Module (AOSP version). Should always be installed with CaptivePortalLogin. -->
+ <option name="mainline-module-package-name" value="com.android.networkstack" />
+ <!-- Resolver Module (internal version). -->
+ <option name="mainline-module-package-name" value="com.google.android.resolv" />
+ <!-- Resolver Module (AOSP version). -->
+ <option name="mainline-module-package-name" value="com.android.resolv" />
+ </object>
</configuration>
diff --git a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
index de4f41b..d618915 100644
--- a/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
+++ b/tests/cts/net/src/android/net/cts/NetworkStatsManagerTest.java
@@ -35,7 +35,14 @@
import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import android.app.AppOpsManager;
+import android.app.Instrumentation;
import android.app.usage.NetworkStats;
import android.app.usage.NetworkStatsManager;
import android.content.Context;
@@ -56,15 +63,24 @@
import android.os.SystemClock;
import android.platform.test.annotations.AppModeFull;
import android.telephony.TelephonyManager;
-import android.test.InstrumentationTestCase;
import android.text.TextUtils;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
import com.android.compatibility.common.util.ShellIdentityUtils;
import com.android.compatibility.common.util.SystemUtil;
import com.android.modules.utils.build.SdkLevel;
+import com.android.testutils.ConnectivityModuleTest;
import com.android.testutils.DevSdkIgnoreRule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -78,7 +94,13 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
-public class NetworkStatsManagerTest extends InstrumentationTestCase {
+@ConnectivityModuleTest
+@AppModeFull(reason = "instant apps cannot be granted USAGE_STATS")
+@RunWith(AndroidJUnit4.class)
+public class NetworkStatsManagerTest {
+ @Rule
+ public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(SC_V2 /* ignoreClassUpTo */);
+
private static final String LOG_TAG = "NetworkStatsManagerTest";
private static final String APPOPS_SET_SHELL_COMMAND = "appops set {0} {1} {2}";
private static final String APPOPS_GET_SHELL_COMMAND = "appops get {0} {1}";
@@ -179,9 +201,11 @@
};
private String mPkg;
+ private Context mContext;
private NetworkStatsManager mNsm;
private ConnectivityManager mCm;
private PackageManager mPm;
+ private Instrumentation mInstrumentation;
private long mStartTime;
private long mEndTime;
@@ -239,44 +263,40 @@
}
}
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mNsm = (NetworkStatsManager) getInstrumentation().getContext()
- .getSystemService(Context.NETWORK_STATS_SERVICE);
+ @Before
+ public void setUp() throws Exception {
+ mContext = InstrumentationRegistry.getContext();
+ mNsm = mContext.getSystemService(NetworkStatsManager.class);
mNsm.setPollForce(true);
- mCm = (ConnectivityManager) getInstrumentation().getContext()
- .getSystemService(Context.CONNECTIVITY_SERVICE);
+ mCm = mContext.getSystemService(ConnectivityManager.class);
+ mPm = mContext.getPackageManager();
+ mPkg = mContext.getPackageName();
- mPm = getInstrumentation().getContext().getPackageManager();
-
- mPkg = getInstrumentation().getContext().getPackageName();
-
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
mWriteSettingsMode = getAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS);
setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, "allow");
mUsageStatsMode = getAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS);
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
if (mWriteSettingsMode != null) {
setAppOpsMode(AppOpsManager.OPSTR_WRITE_SETTINGS, mWriteSettingsMode);
}
if (mUsageStatsMode != null) {
setAppOpsMode(AppOpsManager.OPSTR_GET_USAGE_STATS, mUsageStatsMode);
}
- super.tearDown();
}
private void setAppOpsMode(String appop, String mode) throws Exception {
final String command = MessageFormat.format(APPOPS_SET_SHELL_COMMAND, mPkg, appop, mode);
- SystemUtil.runShellCommand(command);
+ SystemUtil.runShellCommand(mInstrumentation, command);
}
private String getAppOpsMode(String appop) throws Exception {
final String command = MessageFormat.format(APPOPS_GET_SHELL_COMMAND, mPkg, appop);
- String result = SystemUtil.runShellCommand(command);
+ String result = SystemUtil.runShellCommand(mInstrumentation, command);
if (result == null) {
Log.w(LOG_TAG, "App op " + appop + " could not be read.");
}
@@ -284,7 +304,7 @@
}
private boolean isInForeground() throws IOException {
- String result = SystemUtil.runShellCommand(getInstrumentation(),
+ String result = SystemUtil.runShellCommand(mInstrumentation,
"cmd activity get-uid-state " + Process.myUid());
return result.contains("FOREGROUND");
}
@@ -381,15 +401,14 @@
private String getSubscriberId(int networkIndex) {
int networkType = mNetworkInterfacesToTest[networkIndex].getNetworkType();
if (ConnectivityManager.TYPE_MOBILE == networkType) {
- TelephonyManager tm = (TelephonyManager) getInstrumentation().getContext()
- .getSystemService(Context.TELEPHONY_SERVICE);
+ TelephonyManager tm = mContext.getSystemService(TelephonyManager.class);
return ShellIdentityUtils.invokeMethodWithShellPermissions(tm,
(telephonyManager) -> telephonyManager.getSubscriberId());
}
return "";
}
- @AppModeFull
+ @Test
public void testDeviceSummary() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
if (!shouldTestThisNetworkType(i, MINUTE / 2)) {
@@ -425,7 +444,7 @@
}
}
- @AppModeFull
+ @Test
public void testUserSummary() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
if (!shouldTestThisNetworkType(i, MINUTE / 2)) {
@@ -461,7 +480,7 @@
}
}
- @AppModeFull
+ @Test
public void testAppSummary() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Use tolerance value that large enough to make sure stats of at
@@ -537,7 +556,7 @@
}
}
- @AppModeFull
+ @Test
public void testAppDetails() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -580,7 +599,7 @@
}
}
- @AppModeFull
+ @Test
public void testUidDetails() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -634,7 +653,7 @@
}
}
- @AppModeFull
+ @Test
public void testTagDetails() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -741,7 +760,7 @@
bucket.getRxBytes(), bucket.getTxBytes()));
}
- @AppModeFull
+ @Test
public void testUidTagStateDetails() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -818,7 +837,7 @@
}
}
- @AppModeFull
+ @Test
public void testCallback() throws Exception {
for (int i = 0; i < mNetworkInterfacesToTest.length; ++i) {
// Relatively large tolerance to accommodate for history bucket size.
@@ -851,9 +870,10 @@
}
}
- @AppModeFull
- @DevSdkIgnoreRule.IgnoreUpTo(SC_V2)
+ @Test
public void testDataMigrationUtils() throws Exception {
+ if (!SdkLevel.isAtLeastT()) return;
+
final List<String> prefixes = List.of(PREFIX_UID, PREFIX_XT, PREFIX_UID_TAG);
for (final String prefix : prefixes) {
final long duration = TextUtils.equals(PREFIX_XT, prefix) ? TimeUnit.HOURS.toMillis(1)