Merge changes I6cc2507d,I187ea15d into main
* changes: Add flag for QUEUE_CALLBACKS_FOR_FROZEN_APPS Add a CallbackQueue class
This commit is contained in:
commit
16828bdeba
9 changed files with 648 additions and 1 deletions
|
|
@ -4489,6 +4489,7 @@ public class ConnectivityManager {
|
|||
public static final int CALLBACK_BLK_CHANGED = 11;
|
||||
/** @hide */
|
||||
public static final int CALLBACK_LOCAL_NETWORK_INFO_CHANGED = 12;
|
||||
// When adding new IDs, note CallbackQueue assumes callback IDs are at most 16 bits.
|
||||
|
||||
|
||||
/** @hide */
|
||||
|
|
|
|||
126
service/src/com/android/server/CallbackQueue.java
Normal file
126
service/src/com/android/server/CallbackQueue.java
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (C) 2024 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;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.ConnectivityManager.NetworkCallback;
|
||||
|
||||
import com.android.net.module.util.GrowingIntArray;
|
||||
|
||||
/**
|
||||
* A utility class to add/remove {@link NetworkCallback}s from a queue.
|
||||
*
|
||||
* <p>This is intended to be used as a temporary builder to create/modify callbacks stored in an int
|
||||
* array for memory efficiency.
|
||||
*
|
||||
* <p>Intended usage:
|
||||
* <pre>
|
||||
* final CallbackQueue queue = new CallbackQueue(storedCallbacks);
|
||||
* queue.forEach(netId, callbackId -> { [...] });
|
||||
* queue.addCallback(netId, callbackId);
|
||||
* [...]
|
||||
* queue.shrinkToLength();
|
||||
* storedCallbacks = queue.getBackingArray();
|
||||
* </pre>
|
||||
*
|
||||
* <p>This class is not thread-safe.
|
||||
*/
|
||||
public class CallbackQueue extends GrowingIntArray {
|
||||
public CallbackQueue(int[] initialCallbacks) {
|
||||
super(initialCallbacks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a callback int from netId and callbackId.
|
||||
*
|
||||
* <p>The first 16 bits of each int is the netId; the last 16 bits are the callback index.
|
||||
*/
|
||||
private static int getCallbackInt(int netId, int callbackId) {
|
||||
return (netId << 16) | (callbackId & 0xffff);
|
||||
}
|
||||
|
||||
private static int getNetId(int callbackInt) {
|
||||
return callbackInt >>> 16;
|
||||
}
|
||||
|
||||
private static int getCallbackId(int callbackInt) {
|
||||
return callbackInt & 0xffff;
|
||||
}
|
||||
|
||||
/**
|
||||
* A consumer interface for {@link #forEach(CallbackConsumer)}.
|
||||
*
|
||||
* <p>This is similar to a BiConsumer<Integer, Integer>, but avoids the boxing cost.
|
||||
*/
|
||||
public interface CallbackConsumer {
|
||||
/**
|
||||
* Method called on each callback in the queue.
|
||||
*/
|
||||
void accept(int netId, int callbackId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all callbacks in the queue.
|
||||
*/
|
||||
public void forEach(@NonNull CallbackConsumer consumer) {
|
||||
forEach(value -> {
|
||||
final int netId = getNetId(value);
|
||||
final int callbackId = getCallbackId(value);
|
||||
consumer.accept(netId, callbackId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the queue contains a callback for the given (netId, callbackId).
|
||||
*/
|
||||
public boolean hasCallback(int netId, int callbackId) {
|
||||
return contains(getCallbackInt(netId, callbackId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all callbacks for the given netId.
|
||||
*
|
||||
* @return true if at least one callback was removed.
|
||||
*/
|
||||
public boolean removeCallbacksForNetId(int netId) {
|
||||
return removeValues(cb -> getNetId(cb) == netId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all callbacks for the given netId and callbackId.
|
||||
* @return true if at least one callback was removed.
|
||||
*/
|
||||
public boolean removeCallbacks(int netId, int callbackId) {
|
||||
final int cbInt = getCallbackInt(netId, callbackId);
|
||||
return removeValues(cb -> cb == cbInt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a callback at the end of the queue.
|
||||
*/
|
||||
public void addCallback(int netId, int callbackId) {
|
||||
add(getCallbackInt(netId, callbackId));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String valueToString(int item) {
|
||||
final int callbackId = getCallbackId(item);
|
||||
final int netId = getNetId(item);
|
||||
return ConnectivityManager.getCallbackName(callbackId) + "(" + netId + ")";
|
||||
}
|
||||
}
|
||||
|
|
@ -145,6 +145,7 @@ import static com.android.net.module.util.PermissionUtils.enforceNetworkStackPer
|
|||
import static com.android.net.module.util.PermissionUtils.hasAnyPermissionOf;
|
||||
import static com.android.server.ConnectivityStatsLog.CONNECTIVITY_STATE_SAMPLE;
|
||||
import static com.android.server.connectivity.ConnectivityFlags.DELAY_DESTROY_SOCKETS;
|
||||
import static com.android.server.connectivity.ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS;
|
||||
import static com.android.server.connectivity.ConnectivityFlags.REQUEST_RESTRICTED_WIFI;
|
||||
import static com.android.server.connectivity.ConnectivityFlags.INGRESS_TO_VPN_ADDRESS_FILTERING;
|
||||
|
||||
|
|
@ -510,6 +511,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
|
||||
private final boolean mUseDeclaredMethodsForCallbacksEnabled;
|
||||
|
||||
// Flag to delay callbacks for frozen apps, suppressing duplicate and stale callbacks.
|
||||
private final boolean mQueueCallbacksForFrozenApps;
|
||||
|
||||
/**
|
||||
* Uids ConnectivityService tracks blocked status of to send blocked status callbacks.
|
||||
* Key is uid based on mAsUid of registered networkRequestInfo
|
||||
|
|
@ -1873,6 +1877,9 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
context, ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN);
|
||||
mUseDeclaredMethodsForCallbacksEnabled = mDeps.isFeatureEnabled(context,
|
||||
ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS);
|
||||
// registerUidFrozenStateChangedCallback is only available on U+
|
||||
mQueueCallbacksForFrozenApps = mDeps.isAtLeastU()
|
||||
&& mDeps.isFeatureEnabled(context, QUEUE_CALLBACKS_FOR_FROZEN_APPS);
|
||||
mCarrierPrivilegeAuthenticator = mDeps.makeCarrierPrivilegeAuthenticator(
|
||||
mContext, mTelephonyManager, mRequestRestrictedWifiEnabled,
|
||||
this::handleUidCarrierPrivilegesLost, mHandler);
|
||||
|
|
@ -2028,7 +2035,7 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
mDelayDestroySockets = mDeps.isFeatureNotChickenedOut(context, DELAY_DESTROY_SOCKETS);
|
||||
mAllowSysUiConnectivityReports = mDeps.isFeatureNotChickenedOut(
|
||||
mContext, ALLOW_SYSUI_CONNECTIVITY_REPORTS);
|
||||
if (mDestroyFrozenSockets) {
|
||||
if (mDestroyFrozenSockets || mQueueCallbacksForFrozenApps) {
|
||||
final UidFrozenStateChangedCallback frozenStateChangedCallback =
|
||||
new UidFrozenStateChangedCallback() {
|
||||
@Override
|
||||
|
|
@ -3464,6 +3471,14 @@ public class ConnectivityService extends IConnectivityManager.Stub
|
|||
|
||||
private void handleFrozenUids(int[] uids, int[] frozenStates) {
|
||||
ensureRunningOnConnectivityServiceThread();
|
||||
handleDestroyFrozenSockets(uids, frozenStates);
|
||||
// TODO: handle freezing NetworkCallbacks
|
||||
}
|
||||
|
||||
private void handleDestroyFrozenSockets(int[] uids, int[] frozenStates) {
|
||||
if (!mDestroyFrozenSockets) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < uids.length; i++) {
|
||||
final int uid = uids[i];
|
||||
final boolean addReason = frozenStates[i] == UID_FROZEN_STATE_FROZEN;
|
||||
|
|
|
|||
|
|
@ -49,6 +49,9 @@ public final class ConnectivityFlags {
|
|||
public static final String USE_DECLARED_METHODS_FOR_CALLBACKS =
|
||||
"use_declared_methods_for_callbacks";
|
||||
|
||||
public static final String QUEUE_CALLBACKS_FOR_FROZEN_APPS =
|
||||
"queue_callbacks_for_frozen_apps";
|
||||
|
||||
private boolean mNoRematchAllRequestsOnRegister;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright (C) 2024 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.net.module.util;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.IntPredicate;
|
||||
|
||||
/**
|
||||
* A growing array of primitive ints.
|
||||
*
|
||||
* <p>This is similar to ArrayList<Integer>, but avoids the cost of boxing (each Integer costs
|
||||
* 16 bytes) and creation / garbage collection of individual Integer objects.
|
||||
*
|
||||
* <p>This class does not use any heuristic for growing capacity, so every call to
|
||||
* {@link #add(int)} may reallocate the backing array. Callers should use
|
||||
* {@link #ensureHasCapacity(int)} to minimize this behavior when they plan to add several values.
|
||||
*/
|
||||
public class GrowingIntArray {
|
||||
private int[] mValues;
|
||||
private int mLength;
|
||||
|
||||
/**
|
||||
* Create an empty GrowingIntArray with the given capacity.
|
||||
*/
|
||||
public GrowingIntArray(int initialCapacity) {
|
||||
mValues = new int[initialCapacity];
|
||||
mLength = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a GrowingIntArray with an initial array of values.
|
||||
*
|
||||
* <p>The array will be used as-is and may be modified, so callers must stop using it after
|
||||
* calling this constructor.
|
||||
*/
|
||||
protected GrowingIntArray(int[] initialValues) {
|
||||
mValues = initialValues;
|
||||
mLength = initialValues.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a value to the array.
|
||||
*/
|
||||
public void add(int value) {
|
||||
ensureHasCapacity(1);
|
||||
mValues[mLength] = value;
|
||||
mLength++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current number of values in the array.
|
||||
*/
|
||||
public int length() {
|
||||
return mLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value at a given index.
|
||||
*
|
||||
* @throws ArrayIndexOutOfBoundsException if the index is out of bounds.
|
||||
*/
|
||||
public int get(int index) {
|
||||
if (index < 0 || index >= mLength) {
|
||||
throw new ArrayIndexOutOfBoundsException(index);
|
||||
}
|
||||
return mValues[index];
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate over all values in the array.
|
||||
*/
|
||||
public void forEach(@NonNull IntConsumer consumer) {
|
||||
for (int i = 0; i < mLength; i++) {
|
||||
consumer.accept(mValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all values matching a predicate.
|
||||
*
|
||||
* @return true if at least one value was removed.
|
||||
*/
|
||||
public boolean removeValues(@NonNull IntPredicate predicate) {
|
||||
int newQueueLength = 0;
|
||||
for (int i = 0; i < mLength; i++) {
|
||||
final int cb = mValues[i];
|
||||
if (!predicate.test(cb)) {
|
||||
mValues[newQueueLength] = cb;
|
||||
newQueueLength++;
|
||||
}
|
||||
}
|
||||
if (mLength != newQueueLength) {
|
||||
mLength = newQueueLength;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether the array contains the given value.
|
||||
*/
|
||||
public boolean contains(int value) {
|
||||
for (int i = 0; i < mLength; i++) {
|
||||
if (mValues[i] == value) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all values from the array.
|
||||
*/
|
||||
public void clear() {
|
||||
mLength = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure at least the given number of values can be added to the array without reallocating.
|
||||
*
|
||||
* @param capacity The minimum number of additional values the array must be able to hold.
|
||||
*/
|
||||
public void ensureHasCapacity(int capacity) {
|
||||
if (mValues.length >= mLength + capacity) {
|
||||
return;
|
||||
}
|
||||
mValues = Arrays.copyOf(mValues, mLength + capacity);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
int getBackingArrayLength() {
|
||||
return mValues.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shrink the array backing this class to the minimum required length.
|
||||
*/
|
||||
public void shrinkToLength() {
|
||||
if (mValues.length != mLength) {
|
||||
mValues = Arrays.copyOf(mValues, mLength);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get values as array by shrinking the internal array to length and returning it.
|
||||
*
|
||||
* <p>This avoids reallocations if the array is already the correct length, but callers should
|
||||
* stop using this instance of {@link GrowingIntArray} if they use the array returned by this
|
||||
* method.
|
||||
*/
|
||||
public int[] getShrinkedBackingArray() {
|
||||
shrinkToLength();
|
||||
return mValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the String representation of an item in the array, for use by {@link #toString()}.
|
||||
*/
|
||||
protected String valueToString(int item) {
|
||||
return String.valueOf(item);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringJoiner joiner = new StringJoiner(",", "[", "]");
|
||||
forEach(item -> joiner.add(valueToString(item)));
|
||||
return joiner.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
/*
|
||||
* Copyright (C) 2024 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.net.module.util
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import kotlin.test.assertContentEquals
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.fail
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@SmallTest
|
||||
class GrowingIntArrayTest {
|
||||
@Test
|
||||
fun testAddAndGet() {
|
||||
val array = GrowingIntArray(1)
|
||||
array.add(-1)
|
||||
array.add(0)
|
||||
array.add(2)
|
||||
|
||||
assertEquals(-1, array.get(0))
|
||||
assertEquals(0, array.get(1))
|
||||
assertEquals(2, array.get(2))
|
||||
assertEquals(3, array.length())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testForEach() {
|
||||
val array = GrowingIntArray(10)
|
||||
array.add(-1)
|
||||
array.add(0)
|
||||
array.add(2)
|
||||
|
||||
val actual = mutableListOf<Int>()
|
||||
array.forEach { actual.add(it) }
|
||||
|
||||
val expected = listOf(-1, 0, 2)
|
||||
assertEquals(expected, actual)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testForEach_EmptyArray() {
|
||||
val array = GrowingIntArray(10)
|
||||
array.forEach {
|
||||
fail("This should not be called")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveValues() {
|
||||
val array = GrowingIntArray(10)
|
||||
array.add(-1)
|
||||
array.add(0)
|
||||
array.add(2)
|
||||
|
||||
array.removeValues { it <= 0 }
|
||||
assertEquals(1, array.length())
|
||||
assertEquals(2, array.get(0))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testContains() {
|
||||
val array = GrowingIntArray(10)
|
||||
array.add(-1)
|
||||
array.add(2)
|
||||
|
||||
assertTrue(array.contains(-1))
|
||||
assertTrue(array.contains(2))
|
||||
|
||||
assertFalse(array.contains(0))
|
||||
assertFalse(array.contains(3))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testClear() {
|
||||
val array = GrowingIntArray(10)
|
||||
array.add(-1)
|
||||
array.add(2)
|
||||
array.clear()
|
||||
|
||||
assertEquals(0, array.length())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testEnsureHasCapacity() {
|
||||
val array = GrowingIntArray(0)
|
||||
array.add(42)
|
||||
array.ensureHasCapacity(2)
|
||||
|
||||
assertEquals(3, array.backingArrayLength)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetShrinkedBackingArray() {
|
||||
val array = GrowingIntArray(10)
|
||||
array.add(-1)
|
||||
array.add(2)
|
||||
|
||||
assertContentEquals(intArrayOf(-1, 2), array.shrinkedBackingArray)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToString() {
|
||||
assertEquals("[]", GrowingIntArray(10).toString())
|
||||
assertEquals("[1,2,3]", GrowingIntArray(3).apply {
|
||||
add(1)
|
||||
add(2)
|
||||
add(3)
|
||||
}.toString())
|
||||
}
|
||||
}
|
||||
181
tests/unit/java/com/android/server/CallbackQueueTest.kt
Normal file
181
tests/unit/java/com/android/server/CallbackQueueTest.kt
Normal file
|
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
* Copyright (C) 2024 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
|
||||
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.CALLBACK_AVAILABLE
|
||||
import android.net.ConnectivityManager.CALLBACK_CAP_CHANGED
|
||||
import android.net.ConnectivityManager.CALLBACK_IP_CHANGED
|
||||
import android.os.Build
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.testutils.DevSdkIgnoreRule
|
||||
import com.android.testutils.DevSdkIgnoreRunner
|
||||
import java.lang.reflect.Modifier
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
private const val TEST_NETID_1 = 123
|
||||
|
||||
// Maximum 16 bits unsigned value
|
||||
private const val TEST_NETID_2 = 0xffff
|
||||
|
||||
@RunWith(DevSdkIgnoreRunner::class)
|
||||
@SmallTest
|
||||
@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.R)
|
||||
class CallbackQueueTest {
|
||||
@Test
|
||||
fun testAddCallback() {
|
||||
val cbs = listOf(
|
||||
TEST_NETID_1 to CALLBACK_AVAILABLE,
|
||||
TEST_NETID_2 to CALLBACK_AVAILABLE,
|
||||
TEST_NETID_1 to CALLBACK_CAP_CHANGED,
|
||||
TEST_NETID_1 to CALLBACK_CAP_CHANGED
|
||||
)
|
||||
val queue = CallbackQueue(intArrayOf()).apply {
|
||||
cbs.forEach { addCallback(it.first, it.second) }
|
||||
}
|
||||
|
||||
assertQueueEquals(cbs, queue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testHasCallback() {
|
||||
val queue = CallbackQueue(intArrayOf()).apply {
|
||||
addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
|
||||
addCallback(TEST_NETID_2, CALLBACK_AVAILABLE)
|
||||
addCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED)
|
||||
addCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED)
|
||||
}
|
||||
|
||||
assertTrue(queue.hasCallback(TEST_NETID_1, CALLBACK_AVAILABLE))
|
||||
assertTrue(queue.hasCallback(TEST_NETID_2, CALLBACK_AVAILABLE))
|
||||
assertTrue(queue.hasCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED))
|
||||
|
||||
assertFalse(queue.hasCallback(TEST_NETID_2, CALLBACK_CAP_CHANGED))
|
||||
assertFalse(queue.hasCallback(1234, CALLBACK_AVAILABLE))
|
||||
assertFalse(queue.hasCallback(TEST_NETID_1, 5678))
|
||||
assertFalse(queue.hasCallback(1234, 5678))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveCallbacks() {
|
||||
val queue = CallbackQueue(intArrayOf()).apply {
|
||||
assertFalse(removeCallbacks(TEST_NETID_1, CALLBACK_AVAILABLE))
|
||||
addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
|
||||
addCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED)
|
||||
addCallback(TEST_NETID_2, CALLBACK_AVAILABLE)
|
||||
addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
|
||||
assertTrue(removeCallbacks(TEST_NETID_1, CALLBACK_AVAILABLE))
|
||||
}
|
||||
assertQueueEquals(listOf(
|
||||
TEST_NETID_1 to CALLBACK_CAP_CHANGED,
|
||||
TEST_NETID_2 to CALLBACK_AVAILABLE
|
||||
), queue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testRemoveCallbacksForNetId() {
|
||||
val queue = CallbackQueue(intArrayOf()).apply {
|
||||
assertFalse(removeCallbacksForNetId(TEST_NETID_2))
|
||||
addCallback(TEST_NETID_2, CALLBACK_AVAILABLE)
|
||||
assertFalse(removeCallbacksForNetId(TEST_NETID_1))
|
||||
addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
|
||||
addCallback(TEST_NETID_1, CALLBACK_CAP_CHANGED)
|
||||
addCallback(TEST_NETID_2, CALLBACK_CAP_CHANGED)
|
||||
addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
|
||||
addCallback(TEST_NETID_2, CALLBACK_IP_CHANGED)
|
||||
assertTrue(removeCallbacksForNetId(TEST_NETID_2))
|
||||
}
|
||||
assertQueueEquals(listOf(
|
||||
TEST_NETID_1 to CALLBACK_AVAILABLE,
|
||||
TEST_NETID_1 to CALLBACK_CAP_CHANGED,
|
||||
TEST_NETID_1 to CALLBACK_AVAILABLE,
|
||||
), queue)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testConstructorFromExistingArray() {
|
||||
val queue1 = CallbackQueue(intArrayOf()).apply {
|
||||
addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
|
||||
addCallback(TEST_NETID_2, CALLBACK_AVAILABLE)
|
||||
}
|
||||
val queue2 = CallbackQueue(queue1.shrinkedBackingArray)
|
||||
assertQueueEquals(listOf(
|
||||
TEST_NETID_1 to CALLBACK_AVAILABLE,
|
||||
TEST_NETID_2 to CALLBACK_AVAILABLE
|
||||
), queue2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testToString() {
|
||||
assertEquals("[]", CallbackQueue(intArrayOf()).toString())
|
||||
assertEquals(
|
||||
"[CALLBACK_AVAILABLE($TEST_NETID_1)]",
|
||||
CallbackQueue(intArrayOf()).apply {
|
||||
addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
|
||||
}.toString()
|
||||
)
|
||||
assertEquals(
|
||||
"[CALLBACK_AVAILABLE($TEST_NETID_1),CALLBACK_CAP_CHANGED($TEST_NETID_2)]",
|
||||
CallbackQueue(intArrayOf()).apply {
|
||||
addCallback(TEST_NETID_1, CALLBACK_AVAILABLE)
|
||||
addCallback(TEST_NETID_2, CALLBACK_CAP_CHANGED)
|
||||
}.toString()
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMaxNetId() {
|
||||
// CallbackQueue assumes netIds are at most 16 bits
|
||||
assertTrue(NetIdManager.MAX_NET_ID <= 0xffff)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testMaxCallbackId() {
|
||||
// CallbackQueue assumes callback IDs are at most 16 bits.
|
||||
val constants = ConnectivityManager::class.java.declaredFields.filter {
|
||||
Modifier.isStatic(it.modifiers) && Modifier.isFinal(it.modifiers) &&
|
||||
it.name.startsWith("CALLBACK_")
|
||||
}
|
||||
constants.forEach {
|
||||
it.isAccessible = true
|
||||
assertTrue(it.get(null) as Int <= 0xffff)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun assertQueueEquals(expected: List<Pair<Int, Int>>, actual: CallbackQueue) {
|
||||
assertEquals(
|
||||
expected.size,
|
||||
actual.length(),
|
||||
"Size mismatch between expected: $expected and actual: $actual"
|
||||
)
|
||||
|
||||
var nextIndex = 0
|
||||
actual.forEach { netId, cbId ->
|
||||
val (expNetId, expCbId) = expected[nextIndex]
|
||||
val msg = "$actual does not match $expected at index $nextIndex"
|
||||
assertEquals(expNetId, netId, msg)
|
||||
assertEquals(expCbId, cbId, msg)
|
||||
nextIndex++
|
||||
}
|
||||
// Ensure forEach iterations and size are consistent
|
||||
assertEquals(expected.size, nextIndex)
|
||||
}
|
||||
|
|
@ -2180,6 +2180,7 @@ public class ConnectivityServiceTest {
|
|||
case ConnectivityFlags.CARRIER_SERVICE_CHANGED_USE_CALLBACK:
|
||||
case ConnectivityFlags.REQUEST_RESTRICTED_WIFI:
|
||||
case ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS:
|
||||
case ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS:
|
||||
case KEY_DESTROY_FROZEN_SOCKETS_VERSION:
|
||||
return true;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -165,6 +165,7 @@ open class CSTest {
|
|||
it[ConnectivityFlags.BACKGROUND_FIREWALL_CHAIN] = true
|
||||
it[ConnectivityFlags.DELAY_DESTROY_SOCKETS] = true
|
||||
it[ConnectivityFlags.USE_DECLARED_METHODS_FOR_CALLBACKS] = true
|
||||
it[ConnectivityFlags.QUEUE_CALLBACKS_FOR_FROZEN_APPS] = true
|
||||
}
|
||||
fun setFeatureEnabled(flag: String, enabled: Boolean) = enabledFeatures.set(flag, enabled)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue