[connectivity] add NetworkCapabilities.RedactionHelper
Add a new hidden inner class NetworkCapabilities.RedactionHelper for accessing conditionally available NetworkSpecifier methods via reflection. Reflection is necessary because NetworkSpecifier is platform code. So it's possible for a mainline update to define a getApplicableRedactions() or redact(long) method in a subclass of NetworkSpecifier (e.g., EthernetNetworkSpecifier), and then to run that mainline module on an Android release where the NetworkSpecifier base class does not have those methods. On such a device, ConnectivityService must still call those methods if they exist, otherwise it would fail to redact information that needs to be redacted. Another way to do this would be for ConnectivityService to just call the method and catch NoSuchMethodError, but this code runs fairly often and constructing exceptions is expensive. This class is not used in this CL but will be used for NetworkSpecifier redaction in ag/34242202 and ag/34242203 Bug 374037595 Flag: NONE new code nerver used Change-Id: I8f88c6bb105ebf8d485e18cd214bebf2abe67e63
This commit is contained in:
parent
138e10e3ba
commit
89c94195a0
2 changed files with 229 additions and 0 deletions
|
|
@ -19,6 +19,8 @@ package android.net;
|
|||
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
|
||||
import static com.android.net.module.util.BitUtils.appendStringRepresentationOfBitMaskToStringBuilder;
|
||||
import static com.android.net.module.util.BitUtils.describeDifferences;
|
||||
import static com.android.net.module.util.FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED;
|
||||
import static com.android.net.module.util.FrameworkConnectivityStatsLog.CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_SPECIFIER_REDACTION_REFLECTION_ERROR;
|
||||
|
||||
import android.annotation.FlaggedApi;
|
||||
import android.annotation.IntDef;
|
||||
|
|
@ -37,6 +39,7 @@ import android.os.Process;
|
|||
import android.text.TextUtils;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
import android.util.LruCache;
|
||||
import android.util.Range;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
|
|
@ -44,15 +47,19 @@ import com.android.modules.utils.build.SdkLevel;
|
|||
import com.android.net.flags.Flags;
|
||||
import com.android.net.module.util.BitUtils;
|
||||
import com.android.net.module.util.CollectionUtils;
|
||||
import com.android.net.module.util.FrameworkConnectivityStatsLog;
|
||||
import com.android.net.module.util.NetworkCapabilitiesUtils;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.StringJoiner;
|
||||
|
||||
|
|
@ -3519,4 +3526,148 @@ public final class NetworkCapabilities implements Parcelable {
|
|||
return new NetworkCapabilities(mCaps);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper class for providing access to {@link NetworkSpecifier} redaction methods via
|
||||
* reflection.
|
||||
*
|
||||
* Reflection is necessary because NetworkSpecifier is platform code. So it's possible for a
|
||||
* mainline update to define a getApplicableRedactions() or redact(long) method in a subclass
|
||||
* of NetworkSpecifier (e.g., EthernetNetworkSpecifier), and then to run that mainline module
|
||||
* on an Android release where the NetworkSpecifier base class does not have those methods.
|
||||
* On such a device, ConnectivityService must still call those methods if they exist, otherwise
|
||||
* it would fail to redact information that needs to be redacted. Another way to do this would
|
||||
* be for ConnectivityService to just call the method and catch NoSuchMethodError, but this
|
||||
* code runs fairly often and constructing exceptions is expensive.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static final class RedactionHelper {
|
||||
// LruCache is thread-safe so that no locks are used to project it this class
|
||||
private static final LruCache<Class<? extends NetworkSpecifier>, Optional<Method>>
|
||||
sGetApplicableRedactionsMethodCache = new LruCache<>(100);
|
||||
private static final LruCache<Class<? extends NetworkSpecifier>, Optional<Method>>
|
||||
sRedactLongMethodCache = new LruCache<>(100);
|
||||
|
||||
private RedactionHelper() {}
|
||||
|
||||
/**
|
||||
* Returns the {@link Method} of the {@link NetworkSpecifier} class for the given method
|
||||
* name, or {@code null} if the method doesn't exist.
|
||||
* <p>
|
||||
* This is typically used for {@link NetworkSpecifier#getApplicableRedactions()} and {@link
|
||||
* NetworkSpecifier#redact(long)} to work with new NetworkSpecifier subclasses added in new
|
||||
* mainline version but deployed to older platforms which don't have those two
|
||||
* NetworkSpecifier methods.
|
||||
* <p>
|
||||
* See {@link #createWithLocationInfoSanitizedIfNecessaryWhenParceled} and {@link
|
||||
* #ensureSufficientPermissionsForRequest} for how this method should be used.
|
||||
*/
|
||||
@Nullable
|
||||
private static Method getNetworkSpecifierMethod(
|
||||
Class<? extends NetworkSpecifier> clazz, String name, Class<?> returnType,
|
||||
Class<?>... parameterTypes) {
|
||||
// Use {@code getMethods()} rather than {@code getDeclaredMethods()} so that it can
|
||||
// return the inherited NetworkSpecifier methods for those existing subclasses which
|
||||
// don't override {@link NetworkSpecifier#getApplicableRedactions()} and {@link
|
||||
// NetworkSpecifier#redact(long)}.
|
||||
// Note also {@code getMethods()} returns only public methods, which is necessary for
|
||||
// code to call the method later.
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (name.equals(method.getName())
|
||||
&& returnType.equals(method.getReturnType())
|
||||
&& Arrays.equals(parameterTypes, method.getParameterTypes())) {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link NetworkSpecifier#getApplicableRedactions()} method or {@code null} if
|
||||
* it doesn't exist for the given {@link NetworkSpecifier} subclass.
|
||||
*/
|
||||
@Nullable
|
||||
private static Method getApplicableRedactionsMethod(
|
||||
Class<? extends NetworkSpecifier> clazz) {
|
||||
// If this method is called concurrently from two threads, both may see cache missed
|
||||
// and the reflection branch in method will be executed for twice. But this is okay
|
||||
// because getApplicableRedactions() itself has no side-effect (and this is why we can
|
||||
// create caches for it).
|
||||
Optional<Method> cache = sGetApplicableRedactionsMethodCache.get(clazz);
|
||||
if (cache != null) {
|
||||
return cache.orElse(null);
|
||||
}
|
||||
Method method = getNetworkSpecifierMethod(
|
||||
clazz, "getApplicableRedactions", long.class);
|
||||
sGetApplicableRedactionsMethodCache.put(clazz, Optional.ofNullable(method));
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of {@link NetworkSpecifier#getApplicableRedactions()} or
|
||||
* {@link REDACT_NONE} if the platform doesn't support
|
||||
* {@link NetworkSpecifier#getApplicableRedactions()}.
|
||||
*/
|
||||
public static long getSpecifierApplicableRedactions(NetworkSpecifier specifier) {
|
||||
final Method getApplicableRedactions =
|
||||
getApplicableRedactionsMethod(specifier.getClass());
|
||||
if (getApplicableRedactions == null) {
|
||||
return REDACT_NONE;
|
||||
}
|
||||
|
||||
try {
|
||||
return (long) getApplicableRedactions.invoke(specifier);
|
||||
} catch (IllegalAccessException | InvocationTargetException
|
||||
| IllegalArgumentException e) {
|
||||
FrameworkConnectivityStatsLog.write(CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
|
||||
CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_SPECIFIER_REDACTION_REFLECTION_ERROR);
|
||||
Log.wtf(TAG, "Un expected reflection error on specifier " + specifier, e);
|
||||
return REDACT_ALL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link NetworkSpecifier#redact(long)} method or {@code null} if it doesn't
|
||||
* exist for the given {@link NetworkSpecifier} subclass.
|
||||
*/
|
||||
@Nullable
|
||||
private static Method getRedactLongMethod(Class<? extends NetworkSpecifier> clazz) {
|
||||
// If this method is called concurrently from two threads, both may see cache missed
|
||||
// and the reflection branch in method will be executed for twice. But this is okay
|
||||
// because redact(long) itself has no side-effect (and this is why we can create caches
|
||||
// for it).
|
||||
Optional<Method> cache = sRedactLongMethodCache.get(clazz);
|
||||
if (cache != null) {
|
||||
return cache.orElse(null);
|
||||
}
|
||||
Method method = getNetworkSpecifierMethod(
|
||||
clazz, "redact", NetworkSpecifier.class, long.class);
|
||||
sRedactLongMethodCache.put(clazz, Optional.ofNullable(method));
|
||||
return method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of {@link NetworkSpecifier#redact(long)} or {@code specifier} itself
|
||||
* if the platform doesn't support {@link NetworkSpecifier#redact(long)}.
|
||||
*/
|
||||
@Nullable
|
||||
public static NetworkSpecifier getSpecifierRedactResult(
|
||||
NetworkSpecifier specifier, long redactions) {
|
||||
final Method redactLong = getRedactLongMethod(specifier.getClass());
|
||||
if (redactLong == null) {
|
||||
return specifier;
|
||||
}
|
||||
|
||||
try {
|
||||
return (NetworkSpecifier) redactLong.invoke(specifier, redactions);
|
||||
} catch (IllegalAccessException | InvocationTargetException
|
||||
| IllegalArgumentException e) {
|
||||
FrameworkConnectivityStatsLog.write(CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED,
|
||||
CORE_NETWORKING_TERRIBLE_ERROR_OCCURRED__ERROR_TYPE__TYPE_SPECIFIER_REDACTION_REFLECTION_ERROR);
|
||||
Log.wtf(TAG, "Un expected reflection error on specifier " + specifier, e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_5;
|
|||
import static android.net.NetworkCapabilities.REDACT_FOR_ACCESS_FINE_LOCATION;
|
||||
import static android.net.NetworkCapabilities.REDACT_FOR_LOCAL_MAC_ADDRESS;
|
||||
import static android.net.NetworkCapabilities.REDACT_FOR_NETWORK_SETTINGS;
|
||||
import static android.net.NetworkCapabilities.REDACT_FOR_THREAD_NETWORK_PRIVILEGED;
|
||||
import static android.net.NetworkCapabilities.REDACT_NONE;
|
||||
import static android.net.NetworkCapabilities.SIGNAL_STRENGTH_UNSPECIFIED;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
|
||||
import static android.net.NetworkCapabilities.TRANSPORT_ETHERNET;
|
||||
|
|
@ -63,9 +65,11 @@ import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
|
|||
import static android.net.NetworkCapabilities.TRANSPORT_WIFI_AWARE;
|
||||
import static android.os.Process.INVALID_UID;
|
||||
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastB;
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastS;
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastT;
|
||||
import static com.android.modules.utils.build.SdkLevel.isAtLeastV;
|
||||
import static com.android.net.thread.platform.flags.Flags.FLAG_THREAD_MOBILE_ENABLED;
|
||||
import static com.android.testutils.MiscAsserts.assertEmpty;
|
||||
import static com.android.testutils.MiscAsserts.assertThrows;
|
||||
import static com.android.testutils.ParcelUtils.assertParcelingIsLossless;
|
||||
|
|
@ -74,11 +78,15 @@ import static org.junit.Assert.assertArrayEquals;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.junit.Assume.assumeFalse;
|
||||
import static org.junit.Assume.assumeTrue;
|
||||
|
||||
import android.annotation.Nullable;
|
||||
import android.net.NetworkCapabilities.RedactionHelper;
|
||||
import android.net.wifi.aware.DiscoverySession;
|
||||
import android.net.wifi.aware.PeerHandle;
|
||||
import android.net.wifi.aware.WifiAwareNetworkSpecifier;
|
||||
|
|
@ -99,6 +107,7 @@ import org.junit.Test;
|
|||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
|
@ -1235,6 +1244,44 @@ public class NetworkCapabilitiesTest {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redactable NetworkSpecifier subclass for verifying redaction mechanism.
|
||||
*/
|
||||
private static final class RedactableNetworkSpecifier extends NetworkSpecifier {
|
||||
final boolean locationRedacted;
|
||||
final boolean threadNetworkRedacted;
|
||||
|
||||
RedactableNetworkSpecifier() {
|
||||
locationRedacted = false;
|
||||
threadNetworkRedacted = false;
|
||||
}
|
||||
|
||||
RedactableNetworkSpecifier(boolean locationRedacted, boolean threadNetworkRedacted) {
|
||||
this.locationRedacted = locationRedacted;
|
||||
this.threadNetworkRedacted = threadNetworkRedacted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getApplicableRedactions() {
|
||||
return REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_THREAD_NETWORK_PRIVILEGED;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public NetworkSpecifier redact(long redactions) {
|
||||
return new RedactableNetworkSpecifier(
|
||||
(redactions & REDACT_FOR_ACCESS_FINE_LOCATION) != 0,
|
||||
(redactions & REDACT_FOR_THREAD_NETWORK_PRIVILEGED) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unredactable NetworkSpecifier subclass for verifying redaction mechanism.
|
||||
*/
|
||||
private static final class UnredactableNetworkSpecifier extends NetworkSpecifier {
|
||||
// not overriding the getApplicableRedactions() and redact(long) methods
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBuilder() {
|
||||
final int ownerUid = 1001;
|
||||
|
|
@ -1621,4 +1668,35 @@ public class NetworkCapabilitiesTest {
|
|||
assertFalse(wifiRequestNc.satisfiedByNetworkCapabilities(localWifiNc));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedactionHelper() throws Exception {
|
||||
final UnredactableNetworkSpecifier unredactableSpecifier =
|
||||
new UnredactableNetworkSpecifier();
|
||||
final RedactableNetworkSpecifier redactableSpecifier = new RedactableNetworkSpecifier();
|
||||
|
||||
assertEquals(REDACT_NONE,
|
||||
RedactionHelper.getSpecifierApplicableRedactions(unredactableSpecifier));
|
||||
assertEquals(unredactableSpecifier,
|
||||
RedactionHelper.getSpecifierRedactResult(
|
||||
unredactableSpecifier, REDACT_FOR_ACCESS_FINE_LOCATION));
|
||||
assertEquals(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_THREAD_NETWORK_PRIVILEGED,
|
||||
RedactionHelper.getSpecifierApplicableRedactions(redactableSpecifier));
|
||||
// Call getSpecifierApplicableRedactions() twice to make sure the cache works
|
||||
assertEquals(REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_THREAD_NETWORK_PRIVILEGED,
|
||||
RedactionHelper.getSpecifierApplicableRedactions(redactableSpecifier));
|
||||
final RedactableNetworkSpecifier redactedSpecifier =
|
||||
(RedactableNetworkSpecifier) RedactionHelper.getSpecifierRedactResult(
|
||||
redactableSpecifier,
|
||||
REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_THREAD_NETWORK_PRIVILEGED);
|
||||
assertTrue(redactedSpecifier.locationRedacted);
|
||||
assertTrue(redactedSpecifier.threadNetworkRedacted);
|
||||
// Call getSpecifierRedactResult twice to make sure the cache works
|
||||
final RedactableNetworkSpecifier redactedSpecifier2 =
|
||||
(RedactableNetworkSpecifier) RedactionHelper.getSpecifierRedactResult(
|
||||
redactableSpecifier,
|
||||
REDACT_FOR_ACCESS_FINE_LOCATION | REDACT_FOR_THREAD_NETWORK_PRIVILEGED);
|
||||
assertTrue(redactedSpecifier2.locationRedacted);
|
||||
assertTrue(redactedSpecifier2.threadNetworkRedacted);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue