Merge "Change getGroupIdLevel1() to use subId from TelephonyManager Instance"
diff --git a/Android.bp b/Android.bp
index 80df8c5..98e52a9 100644
--- a/Android.bp
+++ b/Android.bp
@@ -738,13 +738,17 @@
"core/tests/utiltests/jni",
]
+// TODO(b/77285514): remove this once the last few hidl interfaces have been
+// updated to use hwbinder.stubs.
java_library {
name: "hwbinder",
no_framework_libs: true,
srcs: [
"core/java/android/os/HidlSupport.java",
+ "core/java/android/annotation/IntDef.java",
"core/java/android/annotation/NonNull.java",
+ "core/java/android/annotation/SystemApi.java",
"core/java/android/os/HwBinder.java",
"core/java/android/os/HwBlob.java",
"core/java/android/os/HwParcel.java",
@@ -956,3 +960,34 @@
exact_api_filename: "test-exact.txt",
args: framework_docs_args + " -referenceonly -showAnnotation android.annotation.TestApi -nodocs",
}
+
+droiddoc {
+ name: "hwbinder-stubs-docs",
+ srcs: [
+ "core/java/android/os/HidlSupport.java",
+ "core/java/android/annotation/IntDef.java",
+ "core/java/android/annotation/NonNull.java",
+ "core/java/android/annotation/SystemApi.java",
+ "core/java/android/os/HwBinder.java",
+ "core/java/android/os/HwBlob.java",
+ "core/java/android/os/HwParcel.java",
+ "core/java/android/os/IHwBinder.java",
+ "core/java/android/os/IHwInterface.java",
+ "core/java/android/os/DeadObjectException.java",
+ "core/java/android/os/DeadSystemException.java",
+ "core/java/android/os/RemoteException.java",
+ "core/java/android/util/AndroidException.java",
+ ],
+ custom_template: "droiddoc-templates-sdk",
+ installable: false,
+ no_framework_libs: true,
+ args: "-showAnnotation android.annotation.SystemApi -nodocs -stubsourceonly",
+}
+
+java_library_static {
+ name: "hwbinder.stubs",
+ sdk_version: "core_current",
+ srcs: [
+ ":hwbinder-stubs-docs",
+ ],
+}
diff --git a/api/system-current.txt b/api/system-current.txt
index 3fc10f7..d6651d9 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3161,6 +3161,135 @@
field public static final java.lang.String ACTION_UPDATE_TZDATA = "android.intent.action.UPDATE_TZDATA";
}
+ public class HidlSupport {
+ method public static boolean deepEquals(java.lang.Object, java.lang.Object);
+ method public static int deepHashCode(java.lang.Object);
+ method public static int getPidIfSharable();
+ method public static boolean interfacesEqual(android.os.IHwInterface, java.lang.Object);
+ }
+
+ public abstract class HwBinder implements android.os.IHwBinder {
+ ctor public HwBinder();
+ method public static final void configureRpcThreadpool(long, boolean);
+ method public static void enableInstrumentation();
+ method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String) throws java.util.NoSuchElementException, android.os.RemoteException;
+ method public static final android.os.IHwBinder getService(java.lang.String, java.lang.String, boolean) throws java.util.NoSuchElementException, android.os.RemoteException;
+ method public static final void joinRpcThreadpool();
+ method public abstract void onTransact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+ method public final void registerService(java.lang.String) throws android.os.RemoteException;
+ method public final void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+ }
+
+ public class HwBlob {
+ ctor public HwBlob(int);
+ method public final void copyToBoolArray(long, boolean[], int);
+ method public final void copyToDoubleArray(long, double[], int);
+ method public final void copyToFloatArray(long, float[], int);
+ method public final void copyToInt16Array(long, short[], int);
+ method public final void copyToInt32Array(long, int[], int);
+ method public final void copyToInt64Array(long, long[], int);
+ method public final void copyToInt8Array(long, byte[], int);
+ method public final boolean getBool(long);
+ method public final double getDouble(long);
+ method public final float getFloat(long);
+ method public final short getInt16(long);
+ method public final int getInt32(long);
+ method public final long getInt64(long);
+ method public final byte getInt8(long);
+ method public final java.lang.String getString(long);
+ method public final long handle();
+ method public final void putBlob(long, android.os.HwBlob);
+ method public final void putBool(long, boolean);
+ method public final void putBoolArray(long, boolean[]);
+ method public final void putDouble(long, double);
+ method public final void putDoubleArray(long, double[]);
+ method public final void putFloat(long, float);
+ method public final void putFloatArray(long, float[]);
+ method public final void putInt16(long, short);
+ method public final void putInt16Array(long, short[]);
+ method public final void putInt32(long, int);
+ method public final void putInt32Array(long, int[]);
+ method public final void putInt64(long, long);
+ method public final void putInt64Array(long, long[]);
+ method public final void putInt8(long, byte);
+ method public final void putInt8Array(long, byte[]);
+ method public final void putString(long, java.lang.String);
+ method public static java.lang.Boolean[] wrapArray(boolean[]);
+ method public static java.lang.Long[] wrapArray(long[]);
+ method public static java.lang.Byte[] wrapArray(byte[]);
+ method public static java.lang.Short[] wrapArray(short[]);
+ method public static java.lang.Integer[] wrapArray(int[]);
+ method public static java.lang.Float[] wrapArray(float[]);
+ method public static java.lang.Double[] wrapArray(double[]);
+ }
+
+ public class HwParcel {
+ ctor public HwParcel();
+ method public final void enforceInterface(java.lang.String);
+ method public final boolean readBool();
+ method public final java.util.ArrayList<java.lang.Boolean> readBoolVector();
+ method public final android.os.HwBlob readBuffer(long);
+ method public final double readDouble();
+ method public final java.util.ArrayList<java.lang.Double> readDoubleVector();
+ method public final android.os.HwBlob readEmbeddedBuffer(long, long, long, boolean);
+ method public final float readFloat();
+ method public final java.util.ArrayList<java.lang.Float> readFloatVector();
+ method public final short readInt16();
+ method public final java.util.ArrayList<java.lang.Short> readInt16Vector();
+ method public final int readInt32();
+ method public final java.util.ArrayList<java.lang.Integer> readInt32Vector();
+ method public final long readInt64();
+ method public final java.util.ArrayList<java.lang.Long> readInt64Vector();
+ method public final byte readInt8();
+ method public final java.util.ArrayList<java.lang.Byte> readInt8Vector();
+ method public final java.lang.String readString();
+ method public final java.util.ArrayList<java.lang.String> readStringVector();
+ method public final android.os.IHwBinder readStrongBinder();
+ method public final void release();
+ method public final void releaseTemporaryStorage();
+ method public final void send();
+ method public final void verifySuccess();
+ method public final void writeBool(boolean);
+ method public final void writeBoolVector(java.util.ArrayList<java.lang.Boolean>);
+ method public final void writeBuffer(android.os.HwBlob);
+ method public final void writeDouble(double);
+ method public final void writeDoubleVector(java.util.ArrayList<java.lang.Double>);
+ method public final void writeFloat(float);
+ method public final void writeFloatVector(java.util.ArrayList<java.lang.Float>);
+ method public final void writeInt16(short);
+ method public final void writeInt16Vector(java.util.ArrayList<java.lang.Short>);
+ method public final void writeInt32(int);
+ method public final void writeInt32Vector(java.util.ArrayList<java.lang.Integer>);
+ method public final void writeInt64(long);
+ method public final void writeInt64Vector(java.util.ArrayList<java.lang.Long>);
+ method public final void writeInt8(byte);
+ method public final void writeInt8Vector(java.util.ArrayList<java.lang.Byte>);
+ method public final void writeInterfaceToken(java.lang.String);
+ method public final void writeStatus(int);
+ method public final void writeString(java.lang.String);
+ method public final void writeStringVector(java.util.ArrayList<java.lang.String>);
+ method public final void writeStrongBinder(android.os.IHwBinder);
+ field public static final int STATUS_SUCCESS = 0; // 0x0
+ }
+
+ public static abstract class HwParcel.Status implements java.lang.annotation.Annotation {
+ }
+
+ public abstract interface IHwBinder {
+ method public abstract boolean linkToDeath(android.os.IHwBinder.DeathRecipient, long);
+ method public abstract android.os.IHwInterface queryLocalInterface(java.lang.String);
+ method public abstract void transact(int, android.os.HwParcel, android.os.HwParcel, int) throws android.os.RemoteException;
+ method public abstract boolean unlinkToDeath(android.os.IHwBinder.DeathRecipient);
+ }
+
+ public static abstract interface IHwBinder.DeathRecipient {
+ method public abstract void serviceDied(long);
+ }
+
+ public abstract interface IHwInterface {
+ method public abstract android.os.IHwBinder asBinder();
+ }
+
public class IncidentManager {
method public void reportIncident(android.os.IncidentReportArgs);
method public void reportIncident(java.lang.String, byte[]);
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index 3b0dc7e..76a781d 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -20,7 +20,7 @@
import android.os.Parcelable;
/**
- * An event logged for an interface with APF capabilities when its IpManager state machine exits.
+ * An event logged for an interface with APF capabilities when its IpClient state machine exits.
* {@hide}
*/
public final class ApfStats implements Parcelable {
diff --git a/core/java/android/os/HidlSupport.java b/core/java/android/os/HidlSupport.java
index 4d7d931..91b796a 100644
--- a/core/java/android/os/HidlSupport.java
+++ b/core/java/android/os/HidlSupport.java
@@ -16,6 +16,8 @@
package android.os;
+import android.annotation.SystemApi;
+
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
@@ -25,6 +27,7 @@
import java.util.stream.IntStream;
/** @hide */
+@SystemApi
public class HidlSupport {
/**
* Similar to Objects.deepEquals, but also take care of lists.
@@ -36,7 +39,9 @@
* 2.3 Both are Lists, elements are checked recursively
* 2.4 (If both are collections other than lists or maps, throw an error)
* 2.5 lft.equals(rgt) returns true
+ * @hide
*/
+ @SystemApi
public static boolean deepEquals(Object lft, Object rgt) {
if (lft == rgt) {
return true;
@@ -91,6 +96,7 @@
* and should be avoided).
*
* @param <E> Inner object type.
+ * @hide
*/
public static final class Mutable<E> {
public E value;
@@ -106,7 +112,9 @@
/**
* Similar to Arrays.deepHashCode, but also take care of lists.
+ * @hide
*/
+ @SystemApi
public static int deepHashCode(Object o) {
if (o == null) {
return 0;
@@ -133,6 +141,7 @@
return o.hashCode();
}
+ /** @hide */
private static void throwErrorIfUnsupportedType(Object o) {
if (o instanceof Collection<?> && !(o instanceof List<?>)) {
throw new UnsupportedOperationException(
@@ -146,6 +155,7 @@
}
}
+ /** @hide */
private static int primitiveArrayHashCode(Object o) {
Class<?> elementType = o.getClass().getComponentType();
if (elementType == boolean.class) {
@@ -185,7 +195,9 @@
* - If both interfaces are stubs, asBinder() returns the object itself. By default,
* auto-generated IFoo.Stub does not override equals(), but an implementation can
* optionally override it, and {@code interfacesEqual} will use it here.
+ * @hide
*/
+ @SystemApi
public static boolean interfacesEqual(IHwInterface lft, Object rgt) {
if (lft == rgt) {
return true;
@@ -200,7 +212,12 @@
}
/**
- * Return PID of process if sharable to clients.
+ * Return PID of process only if on a non-user build. For debugging purposes.
+ * @hide
*/
+ @SystemApi
public static native int getPidIfSharable();
+
+ /** @hide */
+ public HidlSupport() {}
}
diff --git a/core/java/android/os/HwBinder.java b/core/java/android/os/HwBinder.java
index 5e2a081..228fe7a 100644
--- a/core/java/android/os/HwBinder.java
+++ b/core/java/android/os/HwBinder.java
@@ -16,16 +16,26 @@
package android.os;
+import android.annotation.SystemApi;
+
import libcore.util.NativeAllocationRegistry;
import java.util.NoSuchElementException;
/** @hide */
+@SystemApi
public abstract class HwBinder implements IHwBinder {
private static final String TAG = "HwBinder";
private static final NativeAllocationRegistry sNativeRegistry;
+ /**
+ * Create and initialize a HwBinder object and the native objects
+ * used to allow this to participate in hwbinder transactions.
+ *
+ * @hide
+ */
+ @SystemApi
public HwBinder() {
native_setup();
@@ -34,33 +44,90 @@
mNativeContext);
}
+ /** @hide */
@Override
public final native void transact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
+ /**
+ * Process a hwbinder transaction.
+ *
+ * @param code interface specific code for interface.
+ * @param request parceled transaction
+ * @param reply object to parcel reply into
+ * @param flags transaction flags to be chosen by wire protocol
+ *
+ * @hide
+ */
+ @SystemApi
public abstract void onTransact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
+ /**
+ * Registers this service with the hwservicemanager.
+ *
+ * @param serviceName instance name of the service
+ * @hide
+ */
+ @SystemApi
public native final void registerService(String serviceName)
throws RemoteException;
+ /**
+ * Returns the specified service from the hwservicemanager. Does not retry.
+ *
+ * @param iface fully-qualified interface name for example foo.bar@1.3::IBaz
+ * @param serviceName the instance name of the service for example default.
+ * @throws NoSuchElementException when the service is unavailable
+ * @hide
+ */
+ @SystemApi
public static final IHwBinder getService(
String iface,
String serviceName)
throws RemoteException, NoSuchElementException {
return getService(iface, serviceName, false /* retry */);
}
+ /**
+ * Returns the specified service from the hwservicemanager.
+ * @param iface fully-qualified interface name for example foo.bar@1.3::IBaz
+ * @param serviceName the instance name of the service for example default.
+ * @param retry whether to wait for the service to start if it's not already started
+ * @throws NoSuchElementException when the service is unavailable
+ * @hide
+ */
+ @SystemApi
public static native final IHwBinder getService(
String iface,
String serviceName,
boolean retry)
throws RemoteException, NoSuchElementException;
+ /**
+ * Configures how many threads the process-wide hwbinder threadpool
+ * has to process incoming requests.
+ *
+ * @param maxThreads total number of threads to create (includes this thread if
+ * callerWillJoin is true)
+ * @param callerWillJoin whether joinRpcThreadpool will be called in advance
+ * @hide
+ */
+ @SystemApi
public static native final void configureRpcThreadpool(
long maxThreads, boolean callerWillJoin);
+ /**
+ * Current thread will join hwbinder threadpool and process
+ * commands in the pool. Should be called after configuring
+ * a threadpool with callerWillJoin true and then registering
+ * the provided service if this thread doesn't need to do
+ * anything else.
+ *
+ * @hide
+ */
+ @SystemApi
public static native final void joinRpcThreadpool();
// Returns address of the "freeFunction".
@@ -82,7 +149,26 @@
private static native void native_report_sysprop_change();
/**
+ * Enable instrumentation if available.
+ *
+ * On a non-user build, this method:
+ * - tries to enable atracing (if enabled)
+ * - tries to enable coverage dumps (if running in VTS)
+ * - tries to enable record and replay (if running in VTS)
+ *
+ * @hide
+ */
+ @SystemApi
+ public static void enableInstrumentation() {
+ native_report_sysprop_change();
+ }
+
+ /**
* Notifies listeners that a system property has changed
+ *
+ * TODO(b/72480743): remove this method
+ *
+ * @hide
*/
public static void reportSyspropChanged() {
native_report_sysprop_change();
diff --git a/core/java/android/os/HwBlob.java b/core/java/android/os/HwBlob.java
index 5e9b9ae3..405651e 100644
--- a/core/java/android/os/HwBlob.java
+++ b/core/java/android/os/HwBlob.java
@@ -17,10 +17,17 @@
package android.os;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import libcore.util.NativeAllocationRegistry;
-/** @hide */
+/**
+ * Represents fixed sized allocation of marshalled data used. Helper methods
+ * allow for access to the unmarshalled data in a variety of ways.
+ *
+ * @hide
+ */
+@SystemApi
public class HwBlob {
private static final String TAG = "HwBlob";
@@ -34,48 +41,276 @@
mNativeContext);
}
+ /**
+ * @param offset offset to unmarshall a boolean from
+ * @return the unmarshalled boolean value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final boolean getBool(long offset);
+ /**
+ * @param offset offset to unmarshall a byte from
+ * @return the unmarshalled byte value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final byte getInt8(long offset);
+ /**
+ * @param offset offset to unmarshall a short from
+ * @return the unmarshalled short value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final short getInt16(long offset);
+ /**
+ * @param offset offset to unmarshall an int from
+ * @return the unmarshalled int value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final int getInt32(long offset);
+ /**
+ * @param offset offset to unmarshall a long from
+ * @return the unmarshalled long value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final long getInt64(long offset);
+ /**
+ * @param offset offset to unmarshall a float from
+ * @return the unmarshalled float value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final float getFloat(long offset);
+ /**
+ * @param offset offset to unmarshall a double from
+ * @return the unmarshalled double value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final double getDouble(long offset);
+ /**
+ * @param offset offset to unmarshall a string from
+ * @return the unmarshalled string value
+ * @throws IndexOutOfBoundsException when offset is out of this HwBlob
+ */
public native final String getString(long offset);
/**
- The copyTo... methods copy the blob's data, starting from the given
- byte offset, into the array. A total of "size" _elements_ are copied.
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jboolean)] out of the blob.
*/
public native final void copyToBoolArray(long offset, boolean[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jbyte)] out of the blob.
+ */
public native final void copyToInt8Array(long offset, byte[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jshort)] out of the blob.
+ */
public native final void copyToInt16Array(long offset, short[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jint)] out of the blob.
+ */
public native final void copyToInt32Array(long offset, int[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jlong)] out of the blob.
+ */
public native final void copyToInt64Array(long offset, long[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jfloat)] out of the blob.
+ */
public native final void copyToFloatArray(long offset, float[] array, int size);
+ /**
+ * Copy the blobs data starting from the given byte offset into the range, copying
+ * a total of size elements.
+ *
+ * @param offset starting location in blob
+ * @param array destination array
+ * @param size total number of elements to copy
+ * @throws IllegalArgumentException array.length < size
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jdouble)] out of the blob.
+ */
public native final void copyToDoubleArray(long offset, double[] array, int size);
+ /**
+ * Writes a boolean value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jboolean)] is out of range
+ */
public native final void putBool(long offset, boolean x);
+ /**
+ * Writes a byte value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jbyte)] is out of range
+ */
public native final void putInt8(long offset, byte x);
+ /**
+ * Writes a short value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jshort)] is out of range
+ */
public native final void putInt16(long offset, short x);
+ /**
+ * Writes a int value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jint)] is out of range
+ */
public native final void putInt32(long offset, int x);
+ /**
+ * Writes a long value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jlong)] is out of range
+ */
public native final void putInt64(long offset, long x);
+ /**
+ * Writes a float value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jfloat)] is out of range
+ */
public native final void putFloat(long offset, float x);
+ /**
+ * Writes a double value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jdouble)] is out of range
+ */
public native final void putDouble(long offset, double x);
+ /**
+ * Writes a string value at an offset.
+ *
+ * @param offset location to write value
+ * @param x value to write
+ * @throws IndexOutOfBoundsException when [offset, offset + sizeof(jstring)] is out of range
+ */
public native final void putString(long offset, String x);
+ /**
+ * Put a boolean array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jboolean)] out of the blob.
+ */
public native final void putBoolArray(long offset, boolean[] x);
+ /**
+ * Put a byte array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jbyte)] out of the blob.
+ */
public native final void putInt8Array(long offset, byte[] x);
+ /**
+ * Put a short array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jshort)] out of the blob.
+ */
public native final void putInt16Array(long offset, short[] x);
+ /**
+ * Put a int array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jint)] out of the blob.
+ */
public native final void putInt32Array(long offset, int[] x);
+ /**
+ * Put a long array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jlong)] out of the blob.
+ */
public native final void putInt64Array(long offset, long[] x);
+ /**
+ * Put a float array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jfloat)] out of the blob.
+ */
public native final void putFloatArray(long offset, float[] x);
+ /**
+ * Put a double array contiguously at an offset in the blob.
+ *
+ * @param offset location to write values
+ * @param x array to write
+ * @throws IndexOutOfBoundsException [offset, offset + size * sizeof(jdouble)] out of the blob.
+ */
public native final void putDoubleArray(long offset, double[] x);
+ /**
+ * Write another HwBlob into this blob at the specified location.
+ *
+ * @param offset location to write value
+ * @param blob data to write
+ * @throws IndexOutOfBoundsException if [offset, offset + blob's size] outside of the range of
+ * this blob.
+ */
public native final void putBlob(long offset, HwBlob blob);
+ /**
+ * @return current handle of HwBlob for reference in a parcelled binder transaction
+ */
public native final long handle();
+ /**
+ * Convert a primitive to a wrapped array for boolean.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Boolean[] wrapArray(@NonNull boolean[] array) {
final int n = array.length;
Boolean[] wrappedArray = new Boolean[n];
@@ -85,6 +320,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for long.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Long[] wrapArray(@NonNull long[] array) {
final int n = array.length;
Long[] wrappedArray = new Long[n];
@@ -94,6 +335,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for byte.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Byte[] wrapArray(@NonNull byte[] array) {
final int n = array.length;
Byte[] wrappedArray = new Byte[n];
@@ -103,6 +350,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for short.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Short[] wrapArray(@NonNull short[] array) {
final int n = array.length;
Short[] wrappedArray = new Short[n];
@@ -112,6 +365,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for int.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Integer[] wrapArray(@NonNull int[] array) {
final int n = array.length;
Integer[] wrappedArray = new Integer[n];
@@ -121,6 +380,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for float.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Float[] wrapArray(@NonNull float[] array) {
final int n = array.length;
Float[] wrappedArray = new Float[n];
@@ -130,6 +395,12 @@
return wrappedArray;
}
+ /**
+ * Convert a primitive to a wrapped array for double.
+ *
+ * @param array from array
+ * @return transformed array
+ */
public static Double[] wrapArray(@NonNull double[] array) {
final int n = array.length;
Double[] wrappedArray = new Double[n];
diff --git a/core/java/android/os/HwParcel.java b/core/java/android/os/HwParcel.java
index 4ba1144..0eb62c95 100644
--- a/core/java/android/os/HwParcel.java
+++ b/core/java/android/os/HwParcel.java
@@ -16,17 +16,32 @@
package android.os;
-import java.util.ArrayList;
-import java.util.Arrays;
+import android.annotation.IntDef;
+import android.annotation.SystemApi;
import libcore.util.NativeAllocationRegistry;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+
/** @hide */
+@SystemApi
public class HwParcel {
private static final String TAG = "HwParcel";
+ @IntDef(prefix = { "STATUS_" }, value = {
+ STATUS_SUCCESS,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Status {}
+
+ /**
+ * Success return error for a transaction. Written to parcels
+ * using writeStatus.
+ */
public static final int STATUS_SUCCESS = 0;
- public static final int STATUS_ERROR = -1;
private static final NativeAllocationRegistry sNativeRegistry;
@@ -38,6 +53,9 @@
mNativeContext);
}
+ /**
+ * Creates an initialized and empty parcel.
+ */
public HwParcel() {
native_setup(true /* allocate */);
@@ -46,25 +64,106 @@
mNativeContext);
}
+ /**
+ * Writes an interface token into the parcel used to verify that
+ * a transaction has made it to the write type of interface.
+ *
+ * @param interfaceName fully qualified name of interface message
+ * is being sent to.
+ */
public native final void writeInterfaceToken(String interfaceName);
+ /**
+ * Writes a boolean value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeBool(boolean val);
+ /**
+ * Writes a byte value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeInt8(byte val);
+ /**
+ * Writes a short value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeInt16(short val);
+ /**
+ * Writes a int value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeInt32(int val);
+ /**
+ * Writes a long value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeInt64(long val);
+ /**
+ * Writes a float value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeFloat(float val);
+ /**
+ * Writes a double value to the end of the parcel.
+ * @param val to write
+ */
public native final void writeDouble(double val);
+ /**
+ * Writes a String value to the end of the parcel.
+ *
+ * Note, this will be converted to UTF-8 when it is written.
+ *
+ * @param val to write
+ */
public native final void writeString(String val);
+ /**
+ * Writes an array of boolean values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeBoolVector(boolean[] val);
+ /**
+ * Writes an array of byte values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeInt8Vector(byte[] val);
+ /**
+ * Writes an array of short values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeInt16Vector(short[] val);
+ /**
+ * Writes an array of int values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeInt32Vector(int[] val);
+ /**
+ * Writes an array of long values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeInt64Vector(long[] val);
+ /**
+ * Writes an array of float values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeFloatVector(float[] val);
+ /**
+ * Writes an array of double values to the end of the parcel.
+ * @param val to write
+ */
private native final void writeDoubleVector(double[] val);
+ /**
+ * Writes an array of String values to the end of the parcel.
+ *
+ * Note, these will be converted to UTF-8 as they are written.
+ *
+ * @param val to write
+ */
private native final void writeStringVector(String[] val);
+ /**
+ * Helper method to write a list of Booleans to val.
+ * @param val list to write
+ */
public final void writeBoolVector(ArrayList<Boolean> val) {
final int n = val.size();
boolean[] array = new boolean[n];
@@ -75,6 +174,10 @@
writeBoolVector(array);
}
+ /**
+ * Helper method to write a list of Booleans to the end of the parcel.
+ * @param val list to write
+ */
public final void writeInt8Vector(ArrayList<Byte> val) {
final int n = val.size();
byte[] array = new byte[n];
@@ -85,6 +188,10 @@
writeInt8Vector(array);
}
+ /**
+ * Helper method to write a list of Shorts to the end of the parcel.
+ * @param val list to write
+ */
public final void writeInt16Vector(ArrayList<Short> val) {
final int n = val.size();
short[] array = new short[n];
@@ -95,6 +202,10 @@
writeInt16Vector(array);
}
+ /**
+ * Helper method to write a list of Integers to the end of the parcel.
+ * @param val list to write
+ */
public final void writeInt32Vector(ArrayList<Integer> val) {
final int n = val.size();
int[] array = new int[n];
@@ -105,6 +216,10 @@
writeInt32Vector(array);
}
+ /**
+ * Helper method to write a list of Longs to the end of the parcel.
+ * @param val list to write
+ */
public final void writeInt64Vector(ArrayList<Long> val) {
final int n = val.size();
long[] array = new long[n];
@@ -115,6 +230,10 @@
writeInt64Vector(array);
}
+ /**
+ * Helper method to write a list of Floats to the end of the parcel.
+ * @param val list to write
+ */
public final void writeFloatVector(ArrayList<Float> val) {
final int n = val.size();
float[] array = new float[n];
@@ -125,6 +244,10 @@
writeFloatVector(array);
}
+ /**
+ * Helper method to write a list of Doubles to the end of the parcel.
+ * @param val list to write
+ */
public final void writeDoubleVector(ArrayList<Double> val) {
final int n = val.size();
double[] array = new double[n];
@@ -135,93 +258,272 @@
writeDoubleVector(array);
}
+ /**
+ * Helper method to write a list of Strings to the end of the parcel.
+ * @param val list to write
+ */
public final void writeStringVector(ArrayList<String> val) {
writeStringVector(val.toArray(new String[val.size()]));
}
+ /**
+ * Write a hwbinder object to the end of the parcel.
+ * @param binder value to write
+ */
public native final void writeStrongBinder(IHwBinder binder);
+ /**
+ * Checks to make sure that the interface name matches the name written by the parcel
+ * sender by writeInterfaceToken
+ *
+ * @throws SecurityException interface doesn't match
+ */
public native final void enforceInterface(String interfaceName);
+
+ /**
+ * Reads a boolean value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final boolean readBool();
+ /**
+ * Reads a byte value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final byte readInt8();
+ /**
+ * Reads a short value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final short readInt16();
+ /**
+ * Reads a int value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final int readInt32();
+ /**
+ * Reads a long value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final long readInt64();
+ /**
+ * Reads a float value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final float readFloat();
+ /**
+ * Reads a double value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final double readDouble();
+ /**
+ * Reads a String value from the current location in the parcel.
+ * @return value parsed from the parcel
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final String readString();
+ /**
+ * Reads an array of boolean values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final boolean[] readBoolVectorAsArray();
+ /**
+ * Reads an array of byte values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final byte[] readInt8VectorAsArray();
+ /**
+ * Reads an array of short values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final short[] readInt16VectorAsArray();
+ /**
+ * Reads an array of int values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final int[] readInt32VectorAsArray();
+ /**
+ * Reads an array of long values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final long[] readInt64VectorAsArray();
+ /**
+ * Reads an array of float values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final float[] readFloatVectorAsArray();
+ /**
+ * Reads an array of double values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final double[] readDoubleVectorAsArray();
+ /**
+ * Reads an array of String values from the parcel.
+ * @return array of parsed values
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
private native final String[] readStringVectorAsArray();
+ /**
+ * Convenience method to read a Boolean vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Boolean> readBoolVector() {
Boolean[] array = HwBlob.wrapArray(readBoolVectorAsArray());
return new ArrayList<Boolean>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Byte vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Byte> readInt8Vector() {
Byte[] array = HwBlob.wrapArray(readInt8VectorAsArray());
return new ArrayList<Byte>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Short vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Short> readInt16Vector() {
Short[] array = HwBlob.wrapArray(readInt16VectorAsArray());
return new ArrayList<Short>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Integer vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Integer> readInt32Vector() {
Integer[] array = HwBlob.wrapArray(readInt32VectorAsArray());
return new ArrayList<Integer>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Long vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Long> readInt64Vector() {
Long[] array = HwBlob.wrapArray(readInt64VectorAsArray());
return new ArrayList<Long>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Float vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Float> readFloatVector() {
Float[] array = HwBlob.wrapArray(readFloatVectorAsArray());
return new ArrayList<Float>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a Double vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<Double> readDoubleVector() {
Double[] array = HwBlob.wrapArray(readDoubleVectorAsArray());
return new ArrayList<Double>(Arrays.asList(array));
}
+ /**
+ * Convenience method to read a String vector as an ArrayList.
+ * @return array of parsed values.
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public final ArrayList<String> readStringVector() {
return new ArrayList<String>(Arrays.asList(readStringVectorAsArray()));
}
+ /**
+ * Reads a strong binder value from the parcel.
+ * @return binder object read from parcel or null if no binder can be read
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final IHwBinder readStrongBinder();
- // Handle is stored as part of the blob.
+ /**
+ * Read opaque segment of data as a blob.
+ * @return blob of size expectedSize
+ * @throws IllegalArgumentException if the parcel has no more data
+ */
public native final HwBlob readBuffer(long expectedSize);
+ /**
+ * Read a buffer written using scatter gather.
+ *
+ * @param expectedSize size that buffer should be
+ * @param parentHandle handle from which to read the embedded buffer
+ * @param offset offset into parent
+ * @param nullable whether or not to allow for a null return
+ * @return blob of data with size expectedSize
+ * @throws NoSuchElementException if an embedded buffer is not available to read
+ * @throws IllegalArgumentException if expectedSize < 0
+ * @throws NullPointerException if the transaction specified the blob to be null
+ * but nullable is false
+ */
public native final HwBlob readEmbeddedBuffer(
long expectedSize, long parentHandle, long offset,
boolean nullable);
+ /**
+ * Write a buffer into the transaction.
+ * @param blob blob to write into the parcel.
+ */
public native final void writeBuffer(HwBlob blob);
-
+ /**
+ * Write a status value into the blob.
+ * @param status value to write
+ */
public native final void writeStatus(int status);
+ /**
+ * @throws IllegalArgumentException if a success vaue cannot be read
+ * @throws RemoteException if success value indicates a transaction error
+ */
public native final void verifySuccess();
+ /**
+ * Should be called to reduce memory pressure when this object no longer needs
+ * to be written to.
+ */
public native final void releaseTemporaryStorage();
+ /**
+ * Should be called when object is no longer needed to reduce possible memory
+ * pressure if the Java GC does not get to this object in time.
+ */
public native final void release();
+ /**
+ * Sends the parcel to the specified destination.
+ */
public native final void send();
// Returns address of the "freeFunction".
diff --git a/core/java/android/os/IHwBinder.java b/core/java/android/os/IHwBinder.java
index 619f4dc..a565dee 100644
--- a/core/java/android/os/IHwBinder.java
+++ b/core/java/android/os/IHwBinder.java
@@ -16,26 +16,70 @@
package android.os;
+import android.annotation.SystemApi;
+
/** @hide */
+@SystemApi
public interface IHwBinder {
// These MUST match their corresponding libhwbinder/IBinder.h definition !!!
+ /** @hide */
public static final int FIRST_CALL_TRANSACTION = 1;
+ /** @hide */
public static final int FLAG_ONEWAY = 1;
+ /**
+ * Process a hwbinder transaction.
+ *
+ * @param code interface specific code for interface.
+ * @param request parceled transaction
+ * @param reply object to parcel reply into
+ * @param flags transaction flags to be chosen by wire protocol
+ *
+ * @hide
+ */
+ @SystemApi
public void transact(
int code, HwParcel request, HwParcel reply, int flags)
throws RemoteException;
+ /**
+ * Return as IHwInterface instance only if this implements descriptor.
+ *
+ * @param descriptor for example foo.bar@1.0::IBaz
+ * @hide
+ */
+ @SystemApi
public IHwInterface queryLocalInterface(String descriptor);
/**
* Interface for receiving a callback when the process hosting a service
* has gone away.
*/
+ @SystemApi
public interface DeathRecipient {
+ /**
+ * Callback for a registered process dying.
+ *
+ * @param cookie cookie this death recipient was registered with.
+ */
+ @SystemApi
public void serviceDied(long cookie);
}
+ /**
+ * Notifies the death recipient with the cookie when the process containing
+ * this binder dies.
+ *
+ * @param recipient callback object to be called on object death.
+ * @param cookie value to be given to callback on object death.
+ */
+ @SystemApi
public boolean linkToDeath(DeathRecipient recipient, long cookie);
+ /**
+ * Unregisters the death recipient from this binder.
+ *
+ * @param recipient callback to no longer recieve death notifications on this binder.
+ */
+ @SystemApi
public boolean unlinkToDeath(DeathRecipient recipient);
}
diff --git a/core/java/android/os/IHwInterface.java b/core/java/android/os/IHwInterface.java
index 7c5ac6f..1d9e2b0 100644
--- a/core/java/android/os/IHwInterface.java
+++ b/core/java/android/os/IHwInterface.java
@@ -16,7 +16,13 @@
package android.os;
+import android.annotation.SystemApi;
/** @hide */
+@SystemApi
public interface IHwInterface {
+ /**
+ * @return the binder object that corresponds to this interface.
+ */
+ @SystemApi
public IHwBinder asBinder();
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 7a4ac9b..76e3131 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -52,6 +52,8 @@
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.PacketKeepalive;
import android.net.IConnectivityManager;
+import android.net.IIpConnectivityMetrics;
+import android.net.INetdEventCallback;
import android.net.INetworkManagementEventObserver;
import android.net.INetworkPolicyListener;
import android.net.INetworkPolicyManager;
@@ -137,6 +139,7 @@
import com.android.server.connectivity.DataConnectionStats;
import com.android.server.connectivity.DnsManager;
import com.android.server.connectivity.DnsManager.PrivateDnsConfig;
+import com.android.server.connectivity.DnsManager.PrivateDnsValidationUpdate;
import com.android.server.connectivity.IpConnectivityMetrics;
import com.android.server.connectivity.KeepaliveTracker;
import com.android.server.connectivity.LingerMonitor;
@@ -151,6 +154,7 @@
import com.android.server.connectivity.Tethering;
import com.android.server.connectivity.Vpn;
import com.android.server.connectivity.tethering.TetheringDependencies;
+import com.android.server.net.BaseNetdEventCallback;
import com.android.server.net.BaseNetworkObserver;
import com.android.server.net.LockdownVpnTracker;
import com.android.server.net.NetworkPolicyManagerInternal;
@@ -251,6 +255,7 @@
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyManager;
private NetworkPolicyManagerInternal mPolicyManagerInternal;
+ private IIpConnectivityMetrics mIpConnectivityMetrics;
private String mCurrentTcpBufferSizes;
@@ -409,6 +414,9 @@
// Handle changes in Private DNS settings.
private static final int EVENT_PRIVATE_DNS_SETTINGS_CHANGED = 37;
+ // Handle private DNS validation status updates.
+ private static final int EVENT_PRIVATE_DNS_VALIDATION_UPDATE = 38;
+
private static String eventName(int what) {
return sMagicDecoderRing.get(what, Integer.toString(what));
}
@@ -1520,6 +1528,41 @@
return true;
}
+ @VisibleForTesting
+ protected final INetdEventCallback mNetdEventCallback = new BaseNetdEventCallback() {
+ @Override
+ public void onPrivateDnsValidationEvent(int netId, String ipAddress,
+ String hostname, boolean validated) {
+ try {
+ mHandler.sendMessage(mHandler.obtainMessage(
+ EVENT_PRIVATE_DNS_VALIDATION_UPDATE,
+ new PrivateDnsValidationUpdate(netId,
+ InetAddress.parseNumericAddress(ipAddress),
+ hostname, validated)));
+ } catch (IllegalArgumentException e) {
+ loge("Error parsing ip address in validation event");
+ }
+ }
+ };
+
+ @VisibleForTesting
+ protected void registerNetdEventCallback() {
+ mIpConnectivityMetrics =
+ (IIpConnectivityMetrics) IIpConnectivityMetrics.Stub.asInterface(
+ ServiceManager.getService(IpConnectivityLog.SERVICE_NAME));
+ if (mIpConnectivityMetrics == null) {
+ Slog.wtf(TAG, "Missing IIpConnectivityMetrics");
+ }
+
+ try {
+ mIpConnectivityMetrics.addNetdEventCallback(
+ INetdEventCallback.CALLBACK_CALLER_CONNECTIVITY_SERVICE,
+ mNetdEventCallback);
+ } catch (Exception e) {
+ loge("Error registering netd callback: " + e);
+ }
+ }
+
private final INetworkPolicyListener mPolicyListener = new NetworkPolicyManager.Listener() {
@Override
public void onUidRulesChanged(int uid, int uidRules) {
@@ -1704,6 +1747,7 @@
void systemReady() {
loadGlobalProxy();
+ registerNetdEventCallback();
synchronized (this) {
mSystemReady = true;
@@ -2246,6 +2290,9 @@
for (NetworkAgentInfo nai : mNetworkAgentInfos.values()) {
handlePerNetworkPrivateDnsConfig(nai, cfg);
+ if (networkRequiresValidation(nai)) {
+ handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
+ }
}
}
@@ -2270,6 +2317,15 @@
updateDnses(nai.linkProperties, null, nai.network.netId);
}
+ private void handlePrivateDnsValidationUpdate(PrivateDnsValidationUpdate update) {
+ NetworkAgentInfo nai = getNetworkAgentInfoForNetId(update.netId);
+ if (nai == null) {
+ return;
+ }
+ mDnsManager.updatePrivateDnsValidation(update);
+ handleUpdateLinkProperties(nai, new LinkProperties(nai.linkProperties));
+ }
+
private void updateLingerState(NetworkAgentInfo nai, long now) {
// 1. Update the linger timer. If it's changed, reschedule or cancel the alarm.
// 2. If the network was lingering and there are now requests, unlinger it.
@@ -2954,6 +3010,10 @@
case EVENT_PRIVATE_DNS_SETTINGS_CHANGED:
handlePrivateDnsSettingsChanged();
break;
+ case EVENT_PRIVATE_DNS_VALIDATION_UPDATE:
+ handlePrivateDnsValidationUpdate(
+ (PrivateDnsValidationUpdate) msg.obj);
+ break;
}
}
}
@@ -4527,6 +4587,11 @@
updateRoutes(newLp, oldLp, netId);
updateDnses(newLp, oldLp, netId);
+ // Make sure LinkProperties represents the latest private DNS status.
+ // This does not need to be done before updateDnses because the
+ // LinkProperties are not the source of the private DNS configuration.
+ // updateDnses will fetch the private DNS configuration from DnsManager.
+ mDnsManager.updatePrivateDnsStatus(netId, newLp);
// Start or stop clat accordingly to network state.
networkAgent.updateClat(mNetd);
diff --git a/services/core/java/com/android/server/connectivity/DnsManager.java b/services/core/java/com/android/server/connectivity/DnsManager.java
index 2a361a0..7aaac06 100644
--- a/services/core/java/com/android/server/connectivity/DnsManager.java
+++ b/services/core/java/com/android/server/connectivity/DnsManager.java
@@ -37,10 +37,10 @@
import android.net.dns.ResolvUtil;
import android.os.Binder;
import android.os.INetworkManagementService;
-import android.os.Handler;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Pair;
import android.util.Slog;
import com.android.server.connectivity.MockableSystemProperties;
@@ -50,8 +50,12 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
import java.util.Map;
+import java.util.Objects;
import java.util.stream.Collectors;
+import java.util.Set;
import java.util.StringJoiner;
@@ -110,6 +114,7 @@
*/
public class DnsManager {
private static final String TAG = DnsManager.class.getSimpleName();
+ private static final PrivateDnsConfig PRIVATE_DNS_OFF = new PrivateDnsConfig();
/* Defaults for resolver parameters. */
private static final int DNS_RESOLVER_DEFAULT_SAMPLE_VALIDITY_SECONDS = 1800;
@@ -183,11 +188,89 @@
};
}
+ public static class PrivateDnsValidationUpdate {
+ final public int netId;
+ final public InetAddress ipAddress;
+ final public String hostname;
+ final public boolean validated;
+
+ public PrivateDnsValidationUpdate(int netId, InetAddress ipAddress,
+ String hostname, boolean validated) {
+ this.netId = netId;
+ this.ipAddress = ipAddress;
+ this.hostname = hostname;
+ this.validated = validated;
+ }
+ }
+
+ private static class PrivateDnsValidationStatuses {
+ enum ValidationStatus {
+ IN_PROGRESS,
+ FAILED,
+ SUCCEEDED
+ }
+
+ // Validation statuses of <hostname, ipAddress> pairs for a single netId
+ private Map<Pair<String, InetAddress>, ValidationStatus> mValidationMap;
+
+ private PrivateDnsValidationStatuses() {
+ mValidationMap = new HashMap<>();
+ }
+
+ private boolean hasValidatedServer() {
+ for (ValidationStatus status : mValidationMap.values()) {
+ if (status == ValidationStatus.SUCCEEDED) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void updateTrackedDnses(String[] ipAddresses, String hostname) {
+ Set<Pair<String, InetAddress>> latestDnses = new HashSet<>();
+ for (String ipAddress : ipAddresses) {
+ try {
+ latestDnses.add(new Pair(hostname,
+ InetAddress.parseNumericAddress(ipAddress)));
+ } catch (IllegalArgumentException e) {}
+ }
+ // Remove <hostname, ipAddress> pairs that should not be tracked.
+ for (Iterator<Map.Entry<Pair<String, InetAddress>, ValidationStatus>> it =
+ mValidationMap.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry<Pair<String, InetAddress>, ValidationStatus> entry = it.next();
+ if (!latestDnses.contains(entry.getKey())) {
+ it.remove();
+ }
+ }
+ // Add new <hostname, ipAddress> pairs that should be tracked.
+ for (Pair<String, InetAddress> p : latestDnses) {
+ if (!mValidationMap.containsKey(p)) {
+ mValidationMap.put(p, ValidationStatus.IN_PROGRESS);
+ }
+ }
+ }
+
+ private void updateStatus(PrivateDnsValidationUpdate update) {
+ Pair<String, InetAddress> p = new Pair(update.hostname,
+ update.ipAddress);
+ if (!mValidationMap.containsKey(p)) {
+ return;
+ }
+ if (update.validated) {
+ mValidationMap.put(p, ValidationStatus.SUCCEEDED);
+ } else {
+ mValidationMap.put(p, ValidationStatus.FAILED);
+ }
+ }
+ }
+
private final Context mContext;
private final ContentResolver mContentResolver;
private final INetworkManagementService mNMS;
private final MockableSystemProperties mSystemProperties;
+ // TODO: Replace these Maps with SparseArrays.
private final Map<Integer, PrivateDnsConfig> mPrivateDnsMap;
+ private final Map<Integer, PrivateDnsValidationStatuses> mPrivateDnsValidationMap;
private int mNumDnsEntries;
private int mSampleValidity;
@@ -203,6 +286,7 @@
mNMS = nms;
mSystemProperties = sp;
mPrivateDnsMap = new HashMap<>();
+ mPrivateDnsValidationMap = new HashMap<>();
// TODO: Create and register ContentObservers to track every setting
// used herein, posting messages to respond to changes.
@@ -214,6 +298,7 @@
public void removeNetwork(Network network) {
mPrivateDnsMap.remove(network.netId);
+ mPrivateDnsValidationMap.remove(network.netId);
}
public PrivateDnsConfig updatePrivateDns(Network network, PrivateDnsConfig cfg) {
@@ -223,6 +308,40 @@
: mPrivateDnsMap.remove(network.netId);
}
+ public void updatePrivateDnsStatus(int netId, LinkProperties lp) {
+ // Use the PrivateDnsConfig data pushed to this class instance
+ // from ConnectivityService.
+ final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
+ PRIVATE_DNS_OFF);
+
+ final boolean useTls = privateDnsCfg.useTls;
+ final boolean strictMode = privateDnsCfg.inStrictMode();
+ final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
+
+ if (strictMode) {
+ lp.setUsePrivateDns(true);
+ lp.setPrivateDnsServerName(tlsHostname);
+ } else if (useTls) {
+ // We are in opportunistic mode. Private DNS should be used if there
+ // is a known DNS-over-TLS validated server.
+ boolean validated = mPrivateDnsValidationMap.containsKey(netId) &&
+ mPrivateDnsValidationMap.get(netId).hasValidatedServer();
+ lp.setUsePrivateDns(validated);
+ lp.setPrivateDnsServerName(null);
+ } else {
+ // Private DNS is disabled.
+ lp.setUsePrivateDns(false);
+ lp.setPrivateDnsServerName(null);
+ }
+ }
+
+ public void updatePrivateDnsValidation(PrivateDnsValidationUpdate update) {
+ final PrivateDnsValidationStatuses statuses =
+ mPrivateDnsValidationMap.get(update.netId);
+ if (statuses == null) return;
+ statuses.updateStatus(update);
+ }
+
public void setDnsConfigurationForNetwork(
int netId, LinkProperties lp, boolean isDefaultNetwork) {
final String[] assignedServers = NetworkUtils.makeStrings(lp.getDnsServers());
@@ -238,10 +357,11 @@
//
// At this time we do not attempt to enable Private DNS on non-Internet
// networks like IMS.
- final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.get(netId);
+ final PrivateDnsConfig privateDnsCfg = mPrivateDnsMap.getOrDefault(netId,
+ PRIVATE_DNS_OFF);
- final boolean useTls = (privateDnsCfg != null) && privateDnsCfg.useTls;
- final boolean strictMode = (privateDnsCfg != null) && privateDnsCfg.inStrictMode();
+ final boolean useTls = privateDnsCfg.useTls;
+ final boolean strictMode = privateDnsCfg.inStrictMode();
final String tlsHostname = strictMode ? privateDnsCfg.hostname : "";
final String[] tlsServers =
strictMode ? NetworkUtils.makeStrings(
@@ -251,6 +371,17 @@
: useTls ? assignedServers // Opportunistic
: new String[0]; // Off
+ // Prepare to track the validation status of the DNS servers in the
+ // resolver config when private DNS is in opportunistic or strict mode.
+ if (useTls) {
+ if (!mPrivateDnsValidationMap.containsKey(netId)) {
+ mPrivateDnsValidationMap.put(netId, new PrivateDnsValidationStatuses());
+ }
+ mPrivateDnsValidationMap.get(netId).updateTrackedDnses(tlsServers, tlsHostname);
+ } else {
+ mPrivateDnsValidationMap.remove(netId);
+ }
+
Slog.d(TAG, String.format("setDnsConfigurationForNetwork(%d, %s, %s, %s, %s, %s)",
netId, Arrays.toString(assignedServers), Arrays.toString(domainStrs),
Arrays.toString(params), tlsHostname, Arrays.toString(tlsServers)));
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index e520d0c..62fe218 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -249,6 +249,7 @@
"CarrierConfigChangeListener", mContext, smHandler, filter,
(Intent ignored) -> {
mLog.log("OBSERVED carrier config change");
+ updateConfiguration();
reevaluateSimCardProvisioning();
});
// TODO: Remove SimChangeListener altogether. For now, we retain it
@@ -261,28 +262,35 @@
});
mStateReceiver = new StateReceiver();
- filter = new IntentFilter();
+
+ // Load tethering configuration.
+ updateConfiguration();
+
+ startStateMachineUpdaters();
+ }
+
+ private void startStateMachineUpdaters() {
+ mCarrierConfigChange.startListening();
+
+ final Handler handler = mTetherMasterSM.getHandler();
+ IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_STATE);
filter.addAction(CONNECTIVITY_ACTION);
filter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
filter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
- mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
+ mContext.registerReceiver(mStateReceiver, filter, null, handler);
filter = new IntentFilter();
filter.addAction(Intent.ACTION_MEDIA_SHARED);
filter.addAction(Intent.ACTION_MEDIA_UNSHARED);
filter.addDataScheme("file");
- mContext.registerReceiver(mStateReceiver, filter, null, smHandler);
+ mContext.registerReceiver(mStateReceiver, filter, null, handler);
- UserManagerInternal userManager = LocalServices.getService(UserManagerInternal.class);
-
- // this check is useful only for some unit tests; example: ConnectivityServiceTest
- if (userManager != null) {
- userManager.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
+ final UserManagerInternal umi = LocalServices.getService(UserManagerInternal.class);
+ // This check is useful only for some unit tests; example: ConnectivityServiceTest.
+ if (umi != null) {
+ umi.addUserRestrictionsListener(new TetheringUserRestrictionListener(this));
}
-
- // load device config info
- updateConfiguration();
}
private WifiManager getWifiManager() {
@@ -384,17 +392,15 @@
*/
@VisibleForTesting
protected boolean isTetherProvisioningRequired() {
- String[] provisionApp = mContext.getResources().getStringArray(
- com.android.internal.R.array.config_mobile_hotspot_provision_app);
+ final TetheringConfiguration cfg = mConfig;
if (mSystemProperties.getBoolean(DISABLE_PROVISIONING_SYSPROP_KEY, false)
- || provisionApp == null) {
+ || cfg.provisioningApp.length == 0) {
return false;
}
-
if (carrierConfigAffirmsEntitlementCheckNotRequired()) {
return false;
}
- return (provisionApp.length == 2);
+ return (cfg.provisioningApp.length == 2);
}
// The logic here is aimed solely at confirming that a CarrierConfig exists
@@ -417,20 +423,6 @@
return !isEntitlementCheckRequired;
}
- // Used by the SIM card change observation code.
- // TODO: De-duplicate above code.
- private boolean hasMobileHotspotProvisionApp() {
- try {
- if (!mContext.getResources().getString(com.android.internal.R.string.
- config_mobile_hotspot_provision_app_no_ui).isEmpty()) {
- Log.d(TAG, "re-evaluate provisioning");
- return true;
- }
- } catch (Resources.NotFoundException e) {}
- Log.d(TAG, "no prov-check needed for new SIM");
- return false;
- }
-
/**
* Enables or disables tethering for the given type. This should only be called once
* provisioning has succeeded or is not necessary. It will also schedule provisioning rechecks
@@ -1186,7 +1178,7 @@
}
private void reevaluateSimCardProvisioning() {
- if (!hasMobileHotspotProvisionApp()) return;
+ if (!mConfig.hasMobileHotspotProvisionApp()) return;
if (carrierConfigAffirmsEntitlementCheckNotRequired()) return;
ArrayList<Integer> tethered = new ArrayList<>();
@@ -1545,7 +1537,6 @@
return;
}
- mCarrierConfigChange.startListening();
mSimChange.startListening();
mUpstreamNetworkMonitor.start();
@@ -1563,7 +1554,6 @@
mOffload.stop();
mUpstreamNetworkMonitor.stop();
mSimChange.stopListening();
- mCarrierConfigChange.stopListening();
notifyDownstreamsOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
index 09bce7f..454c579 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetheringConfiguration.java
@@ -21,14 +21,23 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_MOBILE_DUN;
import static android.net.ConnectivityManager.TYPE_MOBILE_HIPRI;
+import static com.android.internal.R.array.config_mobile_hotspot_provision_app;
+import static com.android.internal.R.array.config_tether_bluetooth_regexs;
+import static com.android.internal.R.array.config_tether_dhcp_range;
+import static com.android.internal.R.array.config_tether_usb_regexs;
+import static com.android.internal.R.array.config_tether_upstream_types;
+import static com.android.internal.R.array.config_tether_wifi_regexs;
+import static com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui;
import android.content.Context;
import android.content.res.Resources;
import android.net.ConnectivityManager;
-import android.telephony.TelephonyManager;
import android.net.util.SharedLog;
+import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.R;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -51,6 +60,8 @@
public class TetheringConfiguration {
private static final String TAG = TetheringConfiguration.class.getSimpleName();
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
@VisibleForTesting
public static final int DUN_NOT_REQUIRED = 0;
public static final int DUN_REQUIRED = 1;
@@ -79,18 +90,18 @@
public final String[] dhcpRanges;
public final String[] defaultIPv4DNS;
+ public final String[] provisioningApp;
+ public final String provisioningAppNoUi;
+
public TetheringConfiguration(Context ctx, SharedLog log) {
final SharedLog configLog = log.forSubComponent("config");
- tetherableUsbRegexs = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_usb_regexs);
+ tetherableUsbRegexs = getResourceStringArray(ctx, config_tether_usb_regexs);
// TODO: Evaluate deleting this altogether now that Wi-Fi always passes
// us an interface name. Careful consideration needs to be given to
// implications for Settings and for provisioning checks.
- tetherableWifiRegexs = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_wifi_regexs);
- tetherableBluetoothRegexs = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_bluetooth_regexs);
+ tetherableWifiRegexs = getResourceStringArray(ctx, config_tether_wifi_regexs);
+ tetherableBluetoothRegexs = getResourceStringArray(ctx, config_tether_bluetooth_regexs);
dunCheck = checkDunRequired(ctx);
configLog.log("DUN check returned: " + dunCheckString(dunCheck));
@@ -101,6 +112,9 @@
dhcpRanges = getDhcpRanges(ctx);
defaultIPv4DNS = copy(DEFAULT_IPV4_DNS);
+ provisioningApp = getResourceStringArray(ctx, config_mobile_hotspot_provision_app);
+ provisioningAppNoUi = getProvisioningAppNoUi(ctx);
+
configLog.log(toString());
}
@@ -116,6 +130,10 @@
return matchesDownstreamRegexs(iface, tetherableBluetoothRegexs);
}
+ public boolean hasMobileHotspotProvisionApp() {
+ return !TextUtils.isEmpty(provisioningAppNoUi);
+ }
+
public void dump(PrintWriter pw) {
dumpStringArray(pw, "tetherableUsbRegexs", tetherableUsbRegexs);
dumpStringArray(pw, "tetherableWifiRegexs", tetherableWifiRegexs);
@@ -129,6 +147,10 @@
dumpStringArray(pw, "dhcpRanges", dhcpRanges);
dumpStringArray(pw, "defaultIPv4DNS", defaultIPv4DNS);
+
+ dumpStringArray(pw, "provisioningApp", provisioningApp);
+ pw.print("provisioningAppNoUi: ");
+ pw.println(provisioningAppNoUi);
}
public String toString() {
@@ -140,6 +162,8 @@
sj.add(String.format("isDunRequired:%s", isDunRequired));
sj.add(String.format("preferredUpstreamIfaceTypes:%s",
makeString(preferredUpstreamNames(preferredUpstreamIfaceTypes))));
+ sj.add(String.format("provisioningApp:%s", makeString(provisioningApp)));
+ sj.add(String.format("provisioningAppNoUi:%s", provisioningAppNoUi));
return String.format("TetheringConfiguration{%s}", sj.toString());
}
@@ -159,6 +183,7 @@
}
private static String makeString(String[] strings) {
+ if (strings == null) return "null";
final StringJoiner sj = new StringJoiner(",", "[", "]");
for (String s : strings) sj.add(s);
return sj.toString();
@@ -195,8 +220,7 @@
}
private static Collection<Integer> getUpstreamIfaceTypes(Context ctx, int dunCheck) {
- final int ifaceTypes[] = ctx.getResources().getIntArray(
- com.android.internal.R.array.config_tether_upstream_types);
+ final int ifaceTypes[] = ctx.getResources().getIntArray(config_tether_upstream_types);
final ArrayList<Integer> upstreamIfaceTypes = new ArrayList<>(ifaceTypes.length);
for (int i : ifaceTypes) {
switch (i) {
@@ -247,14 +271,30 @@
}
private static String[] getDhcpRanges(Context ctx) {
- final String[] fromResource = ctx.getResources().getStringArray(
- com.android.internal.R.array.config_tether_dhcp_range);
+ final String[] fromResource = getResourceStringArray(ctx, config_tether_dhcp_range);
if ((fromResource.length > 0) && (fromResource.length % 2 == 0)) {
return fromResource;
}
return copy(DHCP_DEFAULT_RANGE);
}
+ private static String getProvisioningAppNoUi(Context ctx) {
+ try {
+ return ctx.getResources().getString(config_mobile_hotspot_provision_app_no_ui);
+ } catch (Resources.NotFoundException e) {
+ return "";
+ }
+ }
+
+ private static String[] getResourceStringArray(Context ctx, int resId) {
+ try {
+ final String[] strArray = ctx.getResources().getStringArray(resId);
+ return (strArray != null) ? strArray : EMPTY_STRING_ARRAY;
+ } catch (Resources.NotFoundException e404) {
+ return EMPTY_STRING_ARRAY;
+ }
+ }
+
private static String[] copy(String[] strarray) {
return Arrays.copyOf(strarray, strarray.length);
}
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index a184b22..87249df 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -40,6 +40,7 @@
import android.net.util.NetdService;
import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
+import android.os.ConditionVariable;
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.RemoteException;
@@ -150,6 +151,28 @@
public void setNeighborDiscoveryOffload(boolean enable) {}
}
+ public static class WaitForProvisioningCallback extends Callback {
+ private final ConditionVariable mCV = new ConditionVariable();
+ private LinkProperties mCallbackLinkProperties;
+
+ public LinkProperties waitForProvisioning() {
+ mCV.block();
+ return mCallbackLinkProperties;
+ }
+
+ @Override
+ public void onProvisioningSuccess(LinkProperties newLp) {
+ mCallbackLinkProperties = newLp;
+ mCV.open();
+ }
+
+ @Override
+ public void onProvisioningFailure(LinkProperties newLp) {
+ mCallbackLinkProperties = null;
+ mCV.open();
+ }
+ }
+
// Use a wrapper class to log in order to ensure complete and detailed
// logging. This method is lighter weight than annotations/reflection
// and has the following benefits:
@@ -281,6 +304,11 @@
return this;
}
+ public Builder withoutMultinetworkPolicyTracker() {
+ mConfig.mUsingMultinetworkPolicyTracker = false;
+ return this;
+ }
+
public Builder withoutIpReachabilityMonitor() {
mConfig.mUsingIpReachabilityMonitor = false;
return this;
@@ -343,6 +371,7 @@
/* package */ boolean mEnableIPv4 = true;
/* package */ boolean mEnableIPv6 = true;
+ /* package */ boolean mUsingMultinetworkPolicyTracker = true;
/* package */ boolean mUsingIpReachabilityMonitor = true;
/* package */ int mRequestedPreDhcpActionMs;
/* package */ InitialConfiguration mInitialConfig;
@@ -374,6 +403,7 @@
return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
.add("mEnableIPv4: " + mEnableIPv4)
.add("mEnableIPv6: " + mEnableIPv6)
+ .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
.add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
.add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
.add("mInitialConfig: " + mInitialConfig)
@@ -559,7 +589,6 @@
private final NetlinkTracker mNetlinkTracker;
private final WakeupMessage mProvisioningTimeoutAlarm;
private final WakeupMessage mDhcpActionTimeoutAlarm;
- private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
private final SharedLog mLog;
private final LocalLog mConnectivityPacketLog;
private final MessageHandlingLogger mMsgStateLogger;
@@ -573,6 +602,7 @@
*/
private LinkProperties mLinkProperties;
private ProvisioningConfiguration mConfiguration;
+ private MultinetworkPolicyTracker mMultinetworkPolicyTracker;
private IpReachabilityMonitor mIpReachabilityMonitor;
private DhcpClient mDhcpClient;
private DhcpResults mDhcpResults;
@@ -685,9 +715,6 @@
mLinkProperties = new LinkProperties();
mLinkProperties.setInterfaceName(mInterfaceName);
- mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(mContext, getHandler(),
- () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
-
mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
mDhcpActionTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
@@ -719,8 +746,6 @@
} catch (RemoteException e) {
logError("Couldn't register NetlinkTracker: %s", e);
}
-
- mMultinetworkPolicyTracker.start();
}
private void stopStateMachineUpdaters() {
@@ -729,8 +754,6 @@
} catch (RemoteException e) {
logError("Couldn't unregister NetlinkTracker: %s", e);
}
-
- mMultinetworkPolicyTracker.shutdown();
}
@Override
@@ -1028,7 +1051,8 @@
// Note that we can still be disconnected by IpReachabilityMonitor
// if the IPv6 default gateway (but not the IPv6 DNS servers; see
// accompanying code in IpReachabilityMonitor) is unreachable.
- final boolean ignoreIPv6ProvisioningLoss = !mMultinetworkPolicyTracker.getAvoidBadWifi();
+ final boolean ignoreIPv6ProvisioningLoss = (mMultinetworkPolicyTracker != null)
+ && !mMultinetworkPolicyTracker.getAvoidBadWifi();
// Additionally:
//
@@ -1520,6 +1544,13 @@
return;
}
+ if (mConfiguration.mUsingMultinetworkPolicyTracker) {
+ mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(
+ mContext, getHandler(),
+ () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
+ mMultinetworkPolicyTracker.start();
+ }
+
if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) {
doImmediateProvisioningFailure(
IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR);
@@ -1537,6 +1568,11 @@
mIpReachabilityMonitor = null;
}
+ if (mMultinetworkPolicyTracker != null) {
+ mMultinetworkPolicyTracker.shutdown();
+ mMultinetworkPolicyTracker = null;
+ }
+
if (mDhcpClient != null) {
mDhcpClient.sendMessage(DhcpClient.CMD_STOP_DHCP);
mDhcpClient.doQuit();
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 508a43d..2eb36a2 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -114,35 +114,6 @@
public static class Callback extends IpClient.Callback {
}
- public static class WaitForProvisioningCallback extends Callback {
- private LinkProperties mCallbackLinkProperties;
-
- public LinkProperties waitForProvisioning() {
- synchronized (this) {
- try {
- wait();
- } catch (InterruptedException e) {}
- return mCallbackLinkProperties;
- }
- }
-
- @Override
- public void onProvisioningSuccess(LinkProperties newLp) {
- synchronized (this) {
- mCallbackLinkProperties = newLp;
- notify();
- }
- }
-
- @Override
- public void onProvisioningFailure(LinkProperties newLp) {
- synchronized (this) {
- mCallbackLinkProperties = null;
- notify();
- }
- }
- }
-
public IpManager(Context context, String ifName, Callback callback) {
super(context, ifName, callback);
}
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index fef702e..9364ec8 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -34,7 +34,7 @@
import android.net.apf.ApfFilter.ApfConfiguration;
import android.net.apf.ApfGenerator.IllegalInstructionException;
import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpManager;
+import android.net.ip.IpClient;
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.RaEvent;
import android.net.util.InterfaceParams;
@@ -606,7 +606,7 @@
}
}
- private class MockIpManagerCallback extends IpManager.Callback {
+ private class MockIpClientCallback extends IpClient.Callback {
private final ConditionVariable mGotApfProgram = new ConditionVariable();
private byte[] mLastApfProgram;
@@ -637,8 +637,8 @@
private final long mFixedTimeMs = SystemClock.elapsedRealtime();
public TestApfFilter(Context context, ApfConfiguration config,
- IpManager.Callback ipManagerCallback, IpConnectivityLog log) throws Exception {
- super(context, config, InterfaceParams.getByName("lo"), ipManagerCallback, log);
+ IpClient.Callback ipClientCallback, IpConnectivityLog log) throws Exception {
+ super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, log);
}
// Pretend an RA packet has been received and show it to ApfFilter.
@@ -761,29 +761,29 @@
private static final byte[] IPV4_ANY_HOST_ADDR = {0, 0, 0, 0};
// Helper to initialize a default apfFilter.
- private ApfFilter setupApfFilter(IpManager.Callback ipManagerCallback, ApfConfiguration config)
+ private ApfFilter setupApfFilter(IpClient.Callback ipClientCallback, ApfConfiguration config)
throws Exception {
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
apfFilter.setLinkProperties(lp);
return apfFilter;
}
@Test
public void testApfFilterIPv4() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
apfFilter.setLinkProperties(lp);
- byte[] program = ipManagerCallback.getApfProgram();
+ byte[] program = ipClientCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
@@ -830,10 +830,10 @@
@Test
public void testApfFilterIPv6() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
- byte[] program = ipManagerCallback.getApfProgram();
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+ byte[] program = ipClientCallback.getApfProgram();
// Verify empty IPv6 packet is passed
ByteBuffer packet = ByteBuffer.wrap(new byte[100]);
@@ -868,17 +868,17 @@
final byte[] multicastIpv4Addr = {(byte)224,0,0,1};
final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
LinkAddress link = new LinkAddress(InetAddress.getByAddress(unicastIpv4Addr), 24);
LinkProperties lp = new LinkProperties();
lp.addLinkAddress(link);
ApfConfiguration config = getDefaultConfig();
config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
apfFilter.setLinkProperties(lp);
- byte[] program = ipManagerCallback.getApfProgram();
+ byte[] program = ipClientCallback.getApfProgram();
// Construct IPv4 and IPv6 multicast packets.
ByteBuffer mcastv4packet = ByteBuffer.wrap(new byte[100]);
@@ -915,9 +915,9 @@
assertPass(program, bcastv4unicastl2packet.array());
// Turn on multicast filter and verify it works
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(true);
- program = ipManagerCallback.getApfProgram();
+ program = ipClientCallback.getApfProgram();
assertDrop(program, mcastv4packet.array());
assertDrop(program, mcastv6packet.array());
assertDrop(program, bcastv4packet1.array());
@@ -925,9 +925,9 @@
assertDrop(program, bcastv4unicastl2packet.array());
// Turn off multicast filter and verify it's off
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.setMulticastFilter(false);
- program = ipManagerCallback.getApfProgram();
+ program = ipClientCallback.getApfProgram();
assertPass(program, mcastv4packet.array());
assertPass(program, mcastv6packet.array());
assertPass(program, bcastv4packet1.array());
@@ -935,13 +935,13 @@
assertPass(program, bcastv4unicastl2packet.array());
// Verify it can be initialized to on
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.shutdown();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
- apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+ apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
apfFilter.setLinkProperties(lp);
- program = ipManagerCallback.getApfProgram();
+ program = ipClientCallback.getApfProgram();
assertDrop(program, mcastv4packet.array());
assertDrop(program, mcastv6packet.array());
assertDrop(program, bcastv4packet1.array());
@@ -956,8 +956,8 @@
@Test
public void testApfFilterMulticastPingWhileDozing() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
- ApfFilter apfFilter = setupApfFilter(ipManagerCallback, getDefaultConfig());
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
+ ApfFilter apfFilter = setupApfFilter(ipClientCallback, getDefaultConfig());
// Construct a multicast ICMPv6 ECHO request.
final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb};
@@ -968,35 +968,35 @@
put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr);
// Normally, we let multicast pings alone...
- assertPass(ipManagerCallback.getApfProgram(), packet.array());
+ assertPass(ipClientCallback.getApfProgram(), packet.array());
// ...and even while dozing...
apfFilter.setDozeMode(true);
- assertPass(ipManagerCallback.getApfProgram(), packet.array());
+ assertPass(ipClientCallback.getApfProgram(), packet.array());
// ...but when the multicast filter is also enabled, drop the multicast pings to save power.
apfFilter.setMulticastFilter(true);
- assertDrop(ipManagerCallback.getApfProgram(), packet.array());
+ assertDrop(ipClientCallback.getApfProgram(), packet.array());
// However, we should still let through all other ICMPv6 types.
ByteBuffer raPacket = ByteBuffer.wrap(packet.array().clone());
raPacket.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ROUTER_ADVERTISEMENT);
- assertPass(ipManagerCallback.getApfProgram(), raPacket.array());
+ assertPass(ipClientCallback.getApfProgram(), raPacket.array());
// Now wake up from doze mode to ensure that we no longer drop the packets.
// (The multicast filter is still enabled at this point).
apfFilter.setDozeMode(false);
- assertPass(ipManagerCallback.getApfProgram(), packet.array());
+ assertPass(ipClientCallback.getApfProgram(), packet.array());
apfFilter.shutdown();
}
@Test
public void testApfFilter802_3() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
- ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config);
- byte[] program = ipManagerCallback.getApfProgram();
+ ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
+ byte[] program = ipClientCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
// Note that eth-type = 0 makes it an IEEE802.3 frame
@@ -1012,11 +1012,11 @@
assertPass(program, packet.array());
// Now turn on the filter
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.shutdown();
config.ieee802_3Filter = DROP_802_3_FRAMES;
- apfFilter = setupApfFilter(ipManagerCallback, config);
- program = ipManagerCallback.getApfProgram();
+ apfFilter = setupApfFilter(ipClientCallback, config);
+ program = ipClientCallback.getApfProgram();
// Verify that IEEE802.3 frame is dropped
// In this case ethtype is used for payload length
@@ -1040,10 +1040,10 @@
final int[] ipv4BlackList = {ETH_P_IP};
final int[] ipv4Ipv6BlackList = {ETH_P_IP, ETH_P_IPV6};
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
- ApfFilter apfFilter = setupApfFilter(ipManagerCallback, config);
- byte[] program = ipManagerCallback.getApfProgram();
+ ApfFilter apfFilter = setupApfFilter(ipClientCallback, config);
+ byte[] program = ipClientCallback.getApfProgram();
// Verify empty packet of 100 zero bytes is passed
// Note that eth-type = 0 makes it an IEEE802.3 frame
@@ -1059,11 +1059,11 @@
assertPass(program, packet.array());
// Now add IPv4 to the black list
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.shutdown();
config.ethTypeBlackList = ipv4BlackList;
- apfFilter = setupApfFilter(ipManagerCallback, config);
- program = ipManagerCallback.getApfProgram();
+ apfFilter = setupApfFilter(ipClientCallback, config);
+ program = ipClientCallback.getApfProgram();
// Verify that IPv4 frame will be dropped
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
@@ -1074,11 +1074,11 @@
assertPass(program, packet.array());
// Now let us have both IPv4 and IPv6 in the black list
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.shutdown();
config.ethTypeBlackList = ipv4Ipv6BlackList;
- apfFilter = setupApfFilter(ipManagerCallback, config);
- program = ipManagerCallback.getApfProgram();
+ apfFilter = setupApfFilter(ipClientCallback, config);
+ program = ipClientCallback.getApfProgram();
// Verify that IPv4 frame will be dropped
packet.putShort(ETH_ETHERTYPE_OFFSET, (short)ETH_P_IP);
@@ -1091,7 +1091,7 @@
apfFilter.shutdown();
}
- private byte[] getProgram(MockIpManagerCallback cb, ApfFilter filter, LinkProperties lp) {
+ private byte[] getProgram(MockIpClientCallback cb, ApfFilter filter, LinkProperties lp) {
cb.resetApfProgramWait();
filter.setLinkProperties(lp);
return cb.getApfProgram();
@@ -1114,23 +1114,23 @@
@Test
public void testApfFilterArp() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
// Verify initially ARP request filter is off, and GARP filter is on.
- verifyArpFilter(ipManagerCallback.getApfProgram(), PASS);
+ verifyArpFilter(ipClientCallback.getApfProgram(), PASS);
// Inform ApfFilter of our address and verify ARP filtering is on
LinkAddress linkAddress = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 24);
LinkProperties lp = new LinkProperties();
assertTrue(lp.addLinkAddress(linkAddress));
- verifyArpFilter(getProgram(ipManagerCallback, apfFilter, lp), DROP);
+ verifyArpFilter(getProgram(ipClientCallback, apfFilter, lp), DROP);
// Inform ApfFilter of loss of IP and verify ARP filtering is off
- verifyArpFilter(getProgram(ipManagerCallback, apfFilter, new LinkProperties()), PASS);
+ verifyArpFilter(getProgram(ipClientCallback, apfFilter, new LinkProperties()), PASS);
apfFilter.shutdown();
}
@@ -1161,7 +1161,7 @@
return packet.array();
}
- // Verify that the last program pushed to the IpManager.Callback properly filters the
+ // Verify that the last program pushed to the IpClient.Callback properly filters the
// given packet for the given lifetime.
private void verifyRaLifetime(byte[] program, ByteBuffer packet, int lifetime) {
final int FRACTION_OF_LIFETIME = 6;
@@ -1191,12 +1191,12 @@
// Test that when ApfFilter is shown the given packet, it generates a program to filter it
// for the given lifetime.
- private void verifyRaLifetime(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
+ private void verifyRaLifetime(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
ByteBuffer packet, int lifetime) throws IOException, ErrnoException {
// Verify new program generated if ApfFilter witnesses RA
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.pretendPacketReceived(packet.array());
- byte[] program = ipManagerCallback.getApfProgram();
+ byte[] program = ipClientCallback.getApfProgram();
verifyRaLifetime(program, packet, lifetime);
}
@@ -1229,21 +1229,21 @@
&& (ev1.dnsslLifetime == ev2.dnsslLifetime);
}
- private void assertInvalidRa(TestApfFilter apfFilter, MockIpManagerCallback ipManagerCallback,
+ private void assertInvalidRa(TestApfFilter apfFilter, MockIpClientCallback ipClientCallback,
ByteBuffer packet) throws IOException, ErrnoException {
- ipManagerCallback.resetApfProgramWait();
+ ipClientCallback.resetApfProgramWait();
apfFilter.pretendPacketReceived(packet.array());
- ipManagerCallback.assertNoProgramUpdate();
+ ipClientCallback.assertNoProgramUpdate();
}
@Test
public void testApfFilterRa() throws Exception {
- MockIpManagerCallback ipManagerCallback = new MockIpManagerCallback();
+ MockIpClientCallback ipClientCallback = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
- TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipManagerCallback, mLog);
- byte[] program = ipManagerCallback.getApfProgram();
+ TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback, mLog);
+ byte[] program = ipClientCallback.getApfProgram();
final int ROUTER_LIFETIME = 1000;
final int PREFIX_VALID_LIFETIME = 200;
@@ -1268,7 +1268,7 @@
basePacket.put(IPV6_ALL_NODES_ADDRESS);
assertPass(program, basePacket.array());
- verifyRaLifetime(apfFilter, ipManagerCallback, basePacket, ROUTER_LIFETIME);
+ verifyRaLifetime(apfFilter, ipClientCallback, basePacket, ROUTER_LIFETIME);
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, -1));
ByteBuffer newFlowLabelPacket = ByteBuffer.wrap(new byte[ICMP6_RA_OPTION_OFFSET]);
@@ -1286,7 +1286,7 @@
zeroLengthOptionPacket.put(basePacket);
zeroLengthOptionPacket.put((byte)ICMP6_PREFIX_OPTION_TYPE);
zeroLengthOptionPacket.put((byte)0);
- assertInvalidRa(apfFilter, ipManagerCallback, zeroLengthOptionPacket);
+ assertInvalidRa(apfFilter, ipClientCallback, zeroLengthOptionPacket);
// Generate several RAs with different options and lifetimes, and verify when
// ApfFilter is shown these packets, it generates programs to filter them for the
@@ -1304,7 +1304,7 @@
ICMP6_RA_OPTION_OFFSET + ICMP6_PREFIX_OPTION_VALID_LIFETIME_OFFSET,
PREFIX_VALID_LIFETIME);
verifyRaLifetime(
- apfFilter, ipManagerCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
+ apfFilter, ipClientCallback, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
verifyRaEvent(new RaEvent(
ROUTER_LIFETIME, PREFIX_VALID_LIFETIME, PREFIX_PREFERRED_LIFETIME, -1, -1, -1));
@@ -1316,7 +1316,7 @@
rdnssOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
rdnssOptionPacket.putInt(
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, RDNSS_LIFETIME);
- verifyRaLifetime(apfFilter, ipManagerCallback, rdnssOptionPacket, RDNSS_LIFETIME);
+ verifyRaLifetime(apfFilter, ipClientCallback, rdnssOptionPacket, RDNSS_LIFETIME);
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, RDNSS_LIFETIME, -1));
ByteBuffer routeInfoOptionPacket = ByteBuffer.wrap(
@@ -1327,7 +1327,7 @@
routeInfoOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
routeInfoOptionPacket.putInt(
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, ROUTE_LIFETIME);
- verifyRaLifetime(apfFilter, ipManagerCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
+ verifyRaLifetime(apfFilter, ipClientCallback, routeInfoOptionPacket, ROUTE_LIFETIME);
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, ROUTE_LIFETIME, -1, -1));
ByteBuffer dnsslOptionPacket = ByteBuffer.wrap(
@@ -1338,11 +1338,11 @@
dnsslOptionPacket.put((byte)(ICMP6_4_BYTE_OPTION_LEN / 8));
dnsslOptionPacket.putInt(
ICMP6_RA_OPTION_OFFSET + ICMP6_4_BYTE_LIFETIME_OFFSET, DNSSL_LIFETIME);
- verifyRaLifetime(apfFilter, ipManagerCallback, dnsslOptionPacket, ROUTER_LIFETIME);
+ verifyRaLifetime(apfFilter, ipClientCallback, dnsslOptionPacket, ROUTER_LIFETIME);
verifyRaEvent(new RaEvent(ROUTER_LIFETIME, -1, -1, -1, -1, DNSSL_LIFETIME));
// Verify that current program filters all five RAs:
- program = ipManagerCallback.getApfProgram();
+ program = ipClientCallback.getApfProgram();
verifyRaLifetime(program, basePacket, ROUTER_LIFETIME);
verifyRaLifetime(program, newFlowLabelPacket, ROUTER_LIFETIME);
verifyRaLifetime(program, prefixOptionPacket, PREFIX_PREFERRED_LIFETIME);
@@ -1384,7 +1384,7 @@
public void testRaParsing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
- MockIpManagerCallback cb = new MockIpManagerCallback();
+ MockIpClientCallback cb = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
@@ -1405,7 +1405,7 @@
public void testRaProcessing() throws Exception {
final int maxRandomPacketSize = 512;
final Random r = new Random();
- MockIpManagerCallback cb = new MockIpManagerCallback();
+ MockIpClientCallback cb = new MockIpClientCallback();
ApfConfiguration config = getDefaultConfig();
config.multicastFilter = DROP_MULTICAST;
config.ieee802_3Filter = DROP_802_3_FRAMES;
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index d04160e..e4df974 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -161,6 +161,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
@@ -879,6 +880,10 @@
return mMetricsService;
}
+ @Override
+ protected void registerNetdEventCallback() {
+ }
+
public WrappedNetworkMonitor getLastCreatedWrappedNetworkMonitor() {
return mLastCreatedNetworkMonitor;
}
@@ -3773,6 +3778,11 @@
// The default on Android is opportunistic mode ("Automatic").
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.requestNetwork(cellRequest, cellNetworkCallback);
+
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
waitForIdle();
// CS tells netd about the empty DNS config for this network.
@@ -3808,6 +3818,14 @@
assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
reset(mNetworkManagementService);
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
+ mCellNetworkAgent);
+ CallbackInfo cbi = cellNetworkCallback.expectCallback(
+ CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
setPrivateDnsSettings(PRIVATE_DNS_MODE_OFF, "ignored.example.com");
verify(mNetworkManagementService, times(1)).setDnsConfigurationForNetwork(
@@ -3817,6 +3835,7 @@
assertTrue(ArrayUtils.containsAll(mStringArrayCaptor.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
reset(mNetworkManagementService);
+ cellNetworkCallback.assertNoCallback();
setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
verify(mNetworkManagementService, atLeastOnce()).setDnsConfigurationForNetwork(
@@ -3829,8 +3848,112 @@
assertTrue(ArrayUtils.containsAll(tlsServers.getValue(),
new String[]{"2001:db8::1", "192.0.2.1"}));
reset(mNetworkManagementService);
+ cellNetworkCallback.assertNoCallback();
- // Can't test strict mode without properly mocking out the DNS lookups.
+ setPrivateDnsSettings(PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, "strict.example.com");
+ // Can't test dns configuration for strict mode without properly mocking
+ // out the DNS lookups, but can test that LinkProperties is updated.
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertEquals("strict.example.com", ((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ }
+
+ @Test
+ public void testLinkPropertiesWithPrivateDnsValidationEvents() throws Exception {
+ // The default on Android is opportunistic mode ("Automatic").
+ setPrivateDnsSettings(PRIVATE_DNS_MODE_OPPORTUNISTIC, "ignored.example.com");
+
+ final TestNetworkCallback cellNetworkCallback = new TestNetworkCallback();
+ final NetworkRequest cellRequest = new NetworkRequest.Builder()
+ .addTransportType(TRANSPORT_CELLULAR).build();
+ mCm.requestNetwork(cellRequest, cellNetworkCallback);
+
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ waitForIdle();
+ LinkProperties lp = new LinkProperties();
+ mCellNetworkAgent.sendLinkProperties(lp);
+ mCellNetworkAgent.connect(false);
+ waitForIdle();
+ cellNetworkCallback.expectCallback(CallbackState.AVAILABLE, mCellNetworkAgent);
+ cellNetworkCallback.expectCallback(CallbackState.NETWORK_CAPABILITIES,
+ mCellNetworkAgent);
+ CallbackInfo cbi = cellNetworkCallback.expectCallback(
+ CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ Set<InetAddress> dnsServers = new HashSet<>();
+ checkDnsServers(cbi.arg, dnsServers);
+
+ // Send a validation event for a server that is not part of the current
+ // resolver config. The validation event should be ignored.
+ mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+ mCellNetworkAgent.getNetwork().netId, "", "145.100.185.18", true);
+ cellNetworkCallback.assertNoCallback();
+
+ // Add a dns server to the LinkProperties.
+ LinkProperties lp2 = new LinkProperties(lp);
+ lp2.addDnsServer(InetAddress.getByName("145.100.185.16"));
+ mCellNetworkAgent.sendLinkProperties(lp2);
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ dnsServers.add(InetAddress.getByName("145.100.185.16"));
+ checkDnsServers(cbi.arg, dnsServers);
+
+ // Send a validation event containing a hostname that is not part of
+ // the current resolver config. The validation event should be ignored.
+ mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+ mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "hostname", true);
+ cellNetworkCallback.assertNoCallback();
+
+ // Send a validation event where validation failed.
+ mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+ mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", false);
+ cellNetworkCallback.assertNoCallback();
+
+ // Send a validation event where validation succeeded for a server in
+ // the current resolver config. A LinkProperties callback with updated
+ // private dns fields should be sent.
+ mService.mNetdEventCallback.onPrivateDnsValidationEvent(
+ mCellNetworkAgent.getNetwork().netId, "145.100.185.16", "", true);
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ checkDnsServers(cbi.arg, dnsServers);
+
+ // The private dns fields in LinkProperties should be preserved when
+ // the network agent sends unrelated changes.
+ LinkProperties lp3 = new LinkProperties(lp2);
+ lp3.setMtu(1300);
+ mCellNetworkAgent.sendLinkProperties(lp3);
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertTrue(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ checkDnsServers(cbi.arg, dnsServers);
+ assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());
+
+ // Removing the only validated server should affect the private dns
+ // fields in LinkProperties.
+ LinkProperties lp4 = new LinkProperties(lp3);
+ lp4.removeDnsServer(InetAddress.getByName("145.100.185.16"));
+ mCellNetworkAgent.sendLinkProperties(lp4);
+ cbi = cellNetworkCallback.expectCallback(CallbackState.LINK_PROPERTIES,
+ mCellNetworkAgent);
+ cellNetworkCallback.assertNoCallback();
+ assertFalse(((LinkProperties)cbi.arg).isPrivateDnsActive());
+ assertNull(((LinkProperties)cbi.arg).getPrivateDnsServerName());
+ dnsServers.remove(InetAddress.getByName("145.100.185.16"));
+ checkDnsServers(cbi.arg, dnsServers);
+ assertEquals(1300, ((LinkProperties)cbi.arg).getMtu());
}
private void checkDirectlyConnectedRoutes(Object callbackObj,
@@ -3850,6 +3973,13 @@
assertTrue(observedRoutes.containsAll(expectedRoutes));
}
+ private static void checkDnsServers(Object callbackObj, Set<InetAddress> dnsServers) {
+ assertTrue(callbackObj instanceof LinkProperties);
+ LinkProperties lp = (LinkProperties) callbackObj;
+ assertEquals(dnsServers.size(), lp.getDnsServers().size());
+ assertTrue(lp.getDnsServers().containsAll(dnsServers));
+ }
+
private static <T> void assertEmpty(T[] ts) {
int length = ts.length;
assertEquals("expected empty array, but length was " + length, 0, length);
diff --git a/tests/net/java/com/android/server/connectivity/DnsManagerTest.java b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
new file mode 100644
index 0000000..bcd8bf3
--- /dev/null
+++ b/tests/net/java/com/android/server/connectivity/DnsManagerTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2018, 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.connectivity;
+
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OFF;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_OPPORTUNISTIC;
+import static android.net.ConnectivityManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.net.LinkProperties;
+import android.net.Network;
+import android.os.INetworkManagementService;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.test.mock.MockContentResolver;
+
+import com.android.internal.util.test.FakeSettingsProvider;
+import com.android.server.connectivity.MockableSystemProperties;
+
+import java.net.InetAddress;
+
+import org.junit.runner.RunWith;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Tests for {@link DnsManager}.
+ *
+ * Build, install and run with:
+ * runtest frameworks-net -c com.android.server.connectivity.DnsManagerTest
+ */
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class DnsManagerTest {
+ static final int TEST_NETID = 100;
+ static final int TEST_NETID_ALTERNATE = 101;
+ static final int TEST_NETID_UNTRACKED = 102;
+ final boolean IS_DEFAULT = true;
+ final boolean NOT_DEFAULT = false;
+
+ DnsManager mDnsManager;
+ MockContentResolver mContentResolver;
+
+ @Mock Context mCtx;
+ @Mock INetworkManagementService mNMService;
+ @Mock MockableSystemProperties mSystemProperties;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mContentResolver = new MockContentResolver();
+ mContentResolver.addProvider(Settings.AUTHORITY,
+ new FakeSettingsProvider());
+ when(mCtx.getContentResolver()).thenReturn(mContentResolver);
+ mDnsManager = new DnsManager(mCtx, mNMService, mSystemProperties);
+
+ // Clear the private DNS settings
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_MODE, "");
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_SPECIFIER, "");
+ }
+
+ @Test
+ public void testTrackedValidationUpdates() throws Exception {
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+ mDnsManager.getPrivateDnsConfig());
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID_ALTERNATE),
+ mDnsManager.getPrivateDnsConfig());
+ LinkProperties lp = new LinkProperties();
+ lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
+ lp.addDnsServer(InetAddress.getByName("4.4.4.4"));
+
+ // Send a validation event that is tracked on the alternate netId
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID_ALTERNATE, lp, NOT_DEFAULT);
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_ALTERNATE,
+ InetAddress.parseNumericAddress("4.4.4.4"), "", true));
+ LinkProperties fixedLp = new LinkProperties(lp);
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
+ assertFalse(fixedLp.isPrivateDnsActive());
+ assertNull(fixedLp.getPrivateDnsServerName());
+ fixedLp = new LinkProperties(lp);
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID_ALTERNATE, fixedLp);
+ assertTrue(fixedLp.isPrivateDnsActive());
+ assertNull(fixedLp.getPrivateDnsServerName());
+
+ // Switch to strict mode
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_MODE,
+ PRIVATE_DNS_MODE_PROVIDER_HOSTNAME);
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_SPECIFIER, "strictmode.com");
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+ mDnsManager.getPrivateDnsConfig());
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ fixedLp = new LinkProperties(lp);
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, fixedLp);
+ assertTrue(fixedLp.isPrivateDnsActive());
+ assertEquals("strictmode.com", fixedLp.getPrivateDnsServerName());
+ fixedLp = new LinkProperties(lp);
+ }
+
+ @Test
+ public void testIgnoreUntrackedValidationUpdates() throws Exception {
+ // The PrivateDnsConfig map is empty, so no validation events will
+ // be tracked.
+ LinkProperties lp = new LinkProperties();
+ lp.addDnsServer(InetAddress.getByName("3.3.3.3"));
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Validation event has untracked netId
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+ mDnsManager.getPrivateDnsConfig());
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID_UNTRACKED,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Validation event has untracked ipAddress
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("4.4.4.4"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Validation event has untracked hostname
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "hostname",
+ true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Validation event failed
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", false));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Network removed
+ mDnsManager.removeNetwork(new Network(TEST_NETID));
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+
+ // Turn private DNS mode off
+ Settings.Global.putString(mContentResolver,
+ Settings.Global.PRIVATE_DNS_MODE, PRIVATE_DNS_MODE_OFF);
+ mDnsManager.updatePrivateDns(new Network(TEST_NETID),
+ mDnsManager.getPrivateDnsConfig());
+ mDnsManager.setDnsConfigurationForNetwork(TEST_NETID, lp, IS_DEFAULT);
+ mDnsManager.updatePrivateDnsValidation(
+ new DnsManager.PrivateDnsValidationUpdate(TEST_NETID,
+ InetAddress.parseNumericAddress("3.3.3.3"), "", true));
+ mDnsManager.updatePrivateDnsStatus(TEST_NETID, lp);
+ assertFalse(lp.isPrivateDnsActive());
+ assertNull(lp.getPrivateDnsServerName());
+ }
+}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 8fb87f1..d241b32 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -355,6 +355,7 @@
@Test
public void canRequireProvisioning() {
setupForRequiredProvisioning();
+ sendConfigurationChanged();
assertTrue(mTethering.isTetherProvisioningRequired());
}
@@ -363,6 +364,7 @@
setupForRequiredProvisioning();
when(mContext.getSystemService(Context.CARRIER_CONFIG_SERVICE))
.thenReturn(null);
+ sendConfigurationChanged();
// Couldn't get the CarrierConfigManager, but still had a declared provisioning app.
// We therefore still require provisioning.
assertTrue(mTethering.isTetherProvisioningRequired());
@@ -372,6 +374,7 @@
public void toleratesCarrierConfigMissing() {
setupForRequiredProvisioning();
when(mCarrierConfigManager.getConfig()).thenReturn(null);
+ sendConfigurationChanged();
// We still have a provisioning app configured, so still require provisioning.
assertTrue(mTethering.isTetherProvisioningRequired());
}
@@ -411,6 +414,11 @@
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private void sendConfigurationChanged() {
+ final Intent intent = new Intent(Intent.ACTION_CONFIGURATION_CHANGED);
+ mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
private void verifyInterfaceServingModeStarted() throws Exception {
verify(mNMService, times(1)).getInterfaceConfig(TEST_WLAN_IFNAME);
verify(mNMService, times(1))