Merge "Fix the link in README file." into tm-dev
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-t/src/android/net/NetworkStatsCollection.java b/framework-t/src/android/net/NetworkStatsCollection.java
index b59a890..29ea772 100644
--- a/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/framework-t/src/android/net/NetworkStatsCollection.java
@@ -694,6 +694,26 @@
}
}
+ /**
+ * Remove histories which contains or is before the cutoff timestamp.
+ * @hide
+ */
+ public void removeHistoryBefore(long cutoffMillis) {
+ final ArrayList<Key> knownKeys = new ArrayList<>();
+ knownKeys.addAll(mStats.keySet());
+
+ for (Key key : knownKeys) {
+ final NetworkStatsHistory history = mStats.get(key);
+ if (history.getStart() > cutoffMillis) continue;
+
+ history.removeBucketsStartingBefore(cutoffMillis);
+ if (history.size() == 0) {
+ mStats.remove(key);
+ }
+ mDirty = true;
+ }
+ }
+
private void noteRecordedHistory(long startMillis, long endMillis, long totalBytes) {
if (startMillis < mStartMillis) mStartMillis = startMillis;
if (endMillis > mEndMillis) mEndMillis = endMillis;
diff --git a/framework-t/src/android/net/NetworkStatsHistory.java b/framework-t/src/android/net/NetworkStatsHistory.java
index 301fef9..b45d44d 100644
--- a/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/framework-t/src/android/net/NetworkStatsHistory.java
@@ -680,19 +680,21 @@
}
/**
- * Remove buckets older than requested cutoff.
+ * Remove buckets that start older than requested cutoff.
+ *
+ * This method will remove any bucket that contains any data older than the requested
+ * cutoff, even if that same bucket includes some data from after the cutoff.
+ *
* @hide
*/
- public void removeBucketsBefore(long cutoff) {
+ public void removeBucketsStartingBefore(final long cutoff) {
// TODO: Consider use getIndexBefore.
int i;
for (i = 0; i < bucketCount; i++) {
final long curStart = bucketStart[i];
- final long curEnd = curStart + bucketDuration;
- // cutoff happens before or during this bucket; everything before
- // this bucket should be removed.
- if (curEnd > cutoff) break;
+ // This bucket starts after or at the cutoff, so it should be kept.
+ if (curStart >= cutoff) break;
}
if (i > 0) {
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/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/service/java/com/android/server/nearby/NearbyService.java b/nearby/service/java/com/android/server/nearby/NearbyService.java
index b9e98a2..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
@@ -170,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 483bfe8..7696a61 100644
--- a/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
+++ b/nearby/tests/cts/fastpair/src/android/nearby/cts/NearbyManagerTest.java
@@ -129,6 +129,7 @@
@Test
@SdkSuppress(minSdkVersion = 32, codeName = "T")
public void test_stopScan_noPrivilegedPermission() {
+ mNearbyManager.startScan(mScanRequest, EXECUTOR, mScanCallback);
mUiAutomation.dropShellPermissionIdentity();
assertThrows(SecurityException.class, () -> mNearbyManager.stopScan(mScanCallback));
}
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/NetworkStatsRecorder.java b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
index f62765d..6f070d7 100644
--- a/service-t/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/service-t/src/com/android/server/net/NetworkStatsRecorder.java
@@ -455,6 +455,73 @@
}
}
+ /**
+ * Rewriter that will remove any histories or persisted data points before the
+ * specified cutoff time, only writing data back when modified.
+ */
+ public static class RemoveDataBeforeRewriter implements FileRotator.Rewriter {
+ private final NetworkStatsCollection mTemp;
+ private final long mCutoffMills;
+
+ public RemoveDataBeforeRewriter(long bucketDuration, long cutoffMills) {
+ mTemp = new NetworkStatsCollection(bucketDuration);
+ mCutoffMills = cutoffMills;
+ }
+
+ @Override
+ public void reset() {
+ mTemp.reset();
+ }
+
+ @Override
+ public void read(InputStream in) throws IOException {
+ mTemp.read(in);
+ mTemp.clearDirty();
+ mTemp.removeHistoryBefore(mCutoffMills);
+ }
+
+ @Override
+ public boolean shouldWrite() {
+ return mTemp.isDirty();
+ }
+
+ @Override
+ public void write(OutputStream out) throws IOException {
+ mTemp.write(out);
+ }
+ }
+
+ /**
+ * Remove persisted data which contains or is before the cutoff timestamp.
+ */
+ public void removeDataBefore(long cutoffMillis) throws IOException {
+ if (mRotator != null) {
+ try {
+ mRotator.rewriteAll(new RemoveDataBeforeRewriter(
+ mBucketDuration, cutoffMillis));
+ } catch (IOException e) {
+ Log.wtf(TAG, "problem importing netstats", e);
+ recoverFromWtf();
+ } catch (OutOfMemoryError e) {
+ Log.wtf(TAG, "problem importing netstats", e);
+ recoverFromWtf();
+ }
+ }
+
+ // Clean up any pending stats
+ if (mPending != null) {
+ mPending.removeHistoryBefore(cutoffMillis);
+ }
+ if (mSinceBoot != null) {
+ mSinceBoot.removeHistoryBefore(cutoffMillis);
+ }
+
+ final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null;
+ if (complete != null) {
+ complete.removeHistoryBefore(cutoffMillis);
+ }
+ }
+
public void dumpLocked(IndentingPrintWriter pw, boolean fullHistory) {
if (mPending != null) {
pw.print("Pending bytes: "); pw.println(mPending.getTotalBytes());
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/unit/java/android/net/NetworkStatsCollectionTest.java b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
index 0f02850..b518a61 100644
--- a/tests/unit/java/android/net/NetworkStatsCollectionTest.java
+++ b/tests/unit/java/android/net/NetworkStatsCollectionTest.java
@@ -37,12 +37,15 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
+import android.annotation.NonNull;
import android.content.res.Resources;
+import android.net.NetworkStatsCollection.Key;
import android.os.Process;
import android.os.UserHandle;
import android.telephony.SubscriptionPlan;
import android.telephony.TelephonyManager;
import android.text.format.DateUtils;
+import android.util.ArrayMap;
import android.util.RecurrenceRule;
import androidx.test.InstrumentationRegistry;
@@ -73,6 +76,8 @@
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
/**
* Tests for {@link NetworkStatsCollection}.
@@ -531,6 +536,86 @@
assertThrows(ArithmeticException.class, () -> multiplySafeByRational(30, 3, 0));
}
+ private static void assertCollectionEntries(
+ @NonNull Map<Key, NetworkStatsHistory> expectedEntries,
+ @NonNull NetworkStatsCollection collection) {
+ final Map<Key, NetworkStatsHistory> actualEntries = collection.getEntries();
+ assertEquals(expectedEntries.size(), actualEntries.size());
+ for (Key expectedKey : expectedEntries.keySet()) {
+ final NetworkStatsHistory expectedHistory = expectedEntries.get(expectedKey);
+ final NetworkStatsHistory actualHistory = actualEntries.get(expectedKey);
+ assertNotNull(actualHistory);
+ assertEquals(expectedHistory.getEntries(), actualHistory.getEntries());
+ actualEntries.remove(expectedKey);
+ }
+ assertEquals(0, actualEntries.size());
+ }
+
+ @Test
+ public void testRemoveHistoryBefore() {
+ final NetworkIdentity testIdent = new NetworkIdentity.Builder()
+ .setSubscriberId(TEST_IMSI).build();
+ final Key key1 = new Key(Set.of(testIdent), 0, 0, 0);
+ final Key key2 = new Key(Set.of(testIdent), 1, 0, 0);
+ final long bucketDuration = 10;
+
+ // Prepare entries for testing, with different bucket start timestamps.
+ final NetworkStatsHistory.Entry entry1 = new NetworkStatsHistory.Entry(10, 10, 40,
+ 4, 50, 5, 60);
+ final NetworkStatsHistory.Entry entry2 = new NetworkStatsHistory.Entry(20, 10, 3,
+ 41, 7, 1, 0);
+ final NetworkStatsHistory.Entry entry3 = new NetworkStatsHistory.Entry(30, 10, 1,
+ 21, 70, 4, 1);
+
+ NetworkStatsHistory history1 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry1)
+ .addEntry(entry2)
+ .build();
+ NetworkStatsHistory history2 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry2)
+ .addEntry(entry3)
+ .build();
+ NetworkStatsCollection collection = new NetworkStatsCollection.Builder(bucketDuration)
+ .addEntry(key1, history1)
+ .addEntry(key2, history2)
+ .build();
+
+ // Verify nothing is removed if the cutoff time is equal to bucketStart.
+ collection.removeHistoryBefore(10);
+ final Map<Key, NetworkStatsHistory> expectedEntries = new ArrayMap<>();
+ expectedEntries.put(key1, history1);
+ expectedEntries.put(key2, history2);
+ assertCollectionEntries(expectedEntries, collection);
+
+ // Verify entry1 will be removed if its bucket start before to cutoff timestamp.
+ collection.removeHistoryBefore(11);
+ history1 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry2)
+ .build();
+ history2 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry2)
+ .addEntry(entry3)
+ .build();
+ final Map<Key, NetworkStatsHistory> cutoff1Entries1 = new ArrayMap<>();
+ cutoff1Entries1.put(key1, history1);
+ cutoff1Entries1.put(key2, history2);
+ assertCollectionEntries(cutoff1Entries1, collection);
+
+ // Verify entry2 will be removed if its bucket start covers by cutoff timestamp.
+ collection.removeHistoryBefore(22);
+ history2 = new NetworkStatsHistory.Builder(10, 5)
+ .addEntry(entry3)
+ .build();
+ final Map<Key, NetworkStatsHistory> cutoffEntries2 = new ArrayMap<>();
+ // History1 is not expected since the collection will omit empty entries.
+ cutoffEntries2.put(key2, history2);
+ assertCollectionEntries(cutoffEntries2, collection);
+
+ // Verify all entries will be removed if cutoff timestamp covers all.
+ collection.removeHistoryBefore(Long.MAX_VALUE);
+ assertEquals(0, collection.getEntries().size());
+ }
+
/**
* Copy a {@link Resources#openRawResource(int)} into {@link File} for
* testing purposes.
diff --git a/tests/unit/java/android/net/NetworkStatsHistoryTest.java b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
index c5f8c00..26079a2 100644
--- a/tests/unit/java/android/net/NetworkStatsHistoryTest.java
+++ b/tests/unit/java/android/net/NetworkStatsHistoryTest.java
@@ -270,7 +270,7 @@
}
@Test
- public void testRemove() throws Exception {
+ public void testRemoveStartingBefore() throws Exception {
stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
// record some data across 24 buckets
@@ -278,28 +278,28 @@
assertEquals(24, stats.size());
// try removing invalid data; should be no change
- stats.removeBucketsBefore(0 - DAY_IN_MILLIS);
+ stats.removeBucketsStartingBefore(0 - DAY_IN_MILLIS);
assertEquals(24, stats.size());
// try removing far before buckets; should be no change
- stats.removeBucketsBefore(TEST_START - YEAR_IN_MILLIS);
+ stats.removeBucketsStartingBefore(TEST_START - YEAR_IN_MILLIS);
assertEquals(24, stats.size());
// try removing just moments into first bucket; should be no change
- // since that bucket contains data beyond the cutoff
- stats.removeBucketsBefore(TEST_START + SECOND_IN_MILLIS);
+ // since that bucket doesn't contain data starts before the cutoff
+ stats.removeBucketsStartingBefore(TEST_START);
assertEquals(24, stats.size());
// try removing single bucket
- stats.removeBucketsBefore(TEST_START + HOUR_IN_MILLIS);
+ stats.removeBucketsStartingBefore(TEST_START + HOUR_IN_MILLIS);
assertEquals(23, stats.size());
// try removing multiple buckets
- stats.removeBucketsBefore(TEST_START + (4 * HOUR_IN_MILLIS));
+ stats.removeBucketsStartingBefore(TEST_START + (4 * HOUR_IN_MILLIS));
assertEquals(20, stats.size());
// try removing all buckets
- stats.removeBucketsBefore(TEST_START + YEAR_IN_MILLIS);
+ stats.removeBucketsStartingBefore(TEST_START + YEAR_IN_MILLIS);
assertEquals(0, stats.size());
}
@@ -349,7 +349,7 @@
stats.recordData(start, end, entry);
} else {
// trim something
- stats.removeBucketsBefore(r.nextLong());
+ stats.removeBucketsStartingBefore(r.nextLong());
}
}
assertConsistent(stats);
diff --git a/tests/unit/java/com/android/server/connectivity/VpnTest.java b/tests/unit/java/com/android/server/connectivity/VpnTest.java
index 46e7dac..59d7378 100644
--- a/tests/unit/java/com/android/server/connectivity/VpnTest.java
+++ b/tests/unit/java/com/android/server/connectivity/VpnTest.java
@@ -27,9 +27,11 @@
import static android.net.ConnectivityManager.NetworkCallback;
import static android.net.INetd.IF_STATE_DOWN;
import static android.net.INetd.IF_STATE_UP;
+import static android.os.Build.VERSION_CODES.S_V2;
import static android.os.UserHandle.PER_USER_RANGE;
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
+import static com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo;
import static com.android.testutils.MiscAsserts.assertThrows;
import static org.junit.Assert.assertArrayEquals;
@@ -83,8 +85,11 @@
import android.net.LinkProperties;
import android.net.LocalSocket;
import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkAgentConfig;
import android.net.NetworkCapabilities;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkProvider;
import android.net.RouteInfo;
import android.net.UidRangeParcel;
import android.net.VpnManager;
@@ -96,6 +101,7 @@
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.INetworkManagementService;
+import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.UserHandle;
@@ -113,12 +119,15 @@
import com.android.internal.net.LegacyVpnInfo;
import com.android.internal.net.VpnConfig;
import com.android.internal.net.VpnProfile;
+import com.android.internal.util.HexDump;
import com.android.modules.utils.build.SdkLevel;
import com.android.server.IpSecService;
+import com.android.server.vcn.util.PersistableBundleUtils;
import com.android.testutils.DevSdkIgnoreRule;
import com.android.testutils.DevSdkIgnoreRunner;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.AdditionalAnswers;
@@ -154,10 +163,13 @@
*/
@RunWith(DevSdkIgnoreRunner.class)
@SmallTest
-@DevSdkIgnoreRule.IgnoreUpTo(VERSION_CODES.S_V2)
+@IgnoreUpTo(VERSION_CODES.S_V2)
public class VpnTest {
private static final String TAG = "VpnTest";
+ @Rule
+ public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule();
+
// Mock users
static final UserInfo primaryUser = new UserInfo(27, "Primary", FLAG_ADMIN | FLAG_PRIMARY);
static final UserInfo secondaryUser = new UserInfo(15, "Secondary", FLAG_ADMIN);
@@ -181,13 +193,15 @@
private static final String TEST_IFACE_NAME = "TEST_IFACE";
private static final int TEST_TUNNEL_RESOURCE_ID = 0x2345;
private static final long TEST_TIMEOUT_MS = 500L;
-
+ private static final String PRIMARY_USER_APP_EXCLUDE_KEY =
+ "VPN_APP_EXCLUDED_27_com.testvpn.vpn";
/**
* Names and UIDs for some fake packages. Important points:
* - UID is ordered increasing.
* - One pair of packages have consecutive UIDs.
*/
static final String[] PKGS = {"com.example", "org.example", "net.example", "web.vpn"};
+ static final String PKGS_BYTES = getPackageByteString(List.of(PKGS));
static final int[] PKG_UIDS = {10066, 10077, 10078, 10400};
// Mock packages
@@ -296,6 +310,17 @@
return new Range<Integer>(start, stop);
}
+ private static String getPackageByteString(List<String> packages) {
+ try {
+ return HexDump.toHexString(
+ PersistableBundleUtils.toDiskStableBytes(PersistableBundleUtils.fromList(
+ packages, PersistableBundleUtils.STRING_SERIALIZER)),
+ true /* upperCase */);
+ } catch (IOException e) {
+ return null;
+ }
+ }
+
@Test
public void testRestrictedProfilesAreAddedToVpn() {
setMockedUsers(primaryUser, secondaryUser, restrictedProfileA, restrictedProfileB);
@@ -749,6 +774,47 @@
}
}
+ private Vpn prepareVpnForVerifyAppExclusionList() throws Exception {
+ final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);
+ when(mVpnProfileStore.get(vpn.getProfileNameForPackage(TEST_VPN_PKG)))
+ .thenReturn(mVpnProfile.encode());
+ when(mVpnProfileStore.get(PRIMARY_USER_APP_EXCLUDE_KEY))
+ .thenReturn(HexDump.hexStringToByteArray(PKGS_BYTES));
+
+ vpn.startVpnProfile(TEST_VPN_PKG);
+ verify(mVpnProfileStore).get(eq(vpn.getProfileNameForPackage(TEST_VPN_PKG)));
+ vpn.mNetworkAgent = new NetworkAgent(mContext, Looper.getMainLooper(), TAG,
+ new NetworkCapabilities.Builder().build(), new LinkProperties(), 10 /* score */,
+ new NetworkAgentConfig.Builder().build(),
+ new NetworkProvider(mContext, Looper.getMainLooper(), TAG)) {};
+ return vpn;
+ }
+
+ @Test @IgnoreUpTo(S_V2)
+ public void testSetAndGetAppExclusionList() throws Exception {
+ final Vpn vpn = prepareVpnForVerifyAppExclusionList();
+ verify(mVpnProfileStore, never()).put(eq(PRIMARY_USER_APP_EXCLUDE_KEY), any());
+ vpn.setAppExclusionList(TEST_VPN_PKG, Arrays.asList(PKGS));
+ verify(mVpnProfileStore)
+ .put(eq(PRIMARY_USER_APP_EXCLUDE_KEY),
+ eq(HexDump.hexStringToByteArray(PKGS_BYTES)));
+ assertEquals(vpn.createUserAndRestrictedProfilesRanges(
+ primaryUser.id, null, Arrays.asList(PKGS)),
+ vpn.mNetworkCapabilities.getUids());
+ assertEquals(Arrays.asList(PKGS), vpn.getAppExclusionList(TEST_VPN_PKG));
+ }
+
+ @Test @IgnoreUpTo(S_V2)
+ public void testSetAndGetAppExclusionListRestrictedUser() throws Exception {
+ final Vpn vpn = prepareVpnForVerifyAppExclusionList();
+ // Mock it to restricted profile
+ when(mUserManager.getUserInfo(anyInt())).thenReturn(restrictedProfileA);
+ // Restricted users cannot configure VPNs
+ assertThrows(SecurityException.class,
+ () -> vpn.setAppExclusionList(TEST_VPN_PKG, new ArrayList<>()));
+ assertThrows(SecurityException.class, () -> vpn.getAppExclusionList(TEST_VPN_PKG));
+ }
+
@Test
public void testProvisionVpnProfilePreconsented() throws Exception {
final Vpn vpn = createVpnAndSetupUidChecks(AppOpsManager.OPSTR_ACTIVATE_PLATFORM_VPN);