Merge "Configure the package name of the updater app"
diff --git a/api/current.txt b/api/current.txt
index 4275e9d..1deb3ad 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -36341,6 +36341,7 @@
field public static final int FD_CLOEXEC;
field public static final int FIONREAD;
field public static final int F_DUPFD;
+ field public static final int F_DUPFD_CLOEXEC;
field public static final int F_GETFD;
field public static final int F_GETFL;
field public static final int F_GETLK;
@@ -36435,7 +36436,9 @@
field public static final int NI_NUMERICSERV;
field public static final int O_ACCMODE;
field public static final int O_APPEND;
+ field public static final int O_CLOEXEC;
field public static final int O_CREAT;
+ field public static final int O_DSYNC;
field public static final int O_EXCL;
field public static final int O_NOCTTY;
field public static final int O_NOFOLLOW;
@@ -49722,6 +49725,7 @@
}
public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
+ ctor public InMemoryDexClassLoader(java.nio.ByteBuffer[], java.lang.ClassLoader);
ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
}
@@ -69072,6 +69076,8 @@
public class JSONException extends java.lang.Exception {
ctor public JSONException(java.lang.String);
+ ctor public JSONException(java.lang.String, java.lang.Throwable);
+ ctor public JSONException(java.lang.Throwable);
}
public class JSONObject {
diff --git a/api/system-current.txt b/api/system-current.txt
index f33d506..48d2aba 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -39292,6 +39292,7 @@
field public static final int FD_CLOEXEC;
field public static final int FIONREAD;
field public static final int F_DUPFD;
+ field public static final int F_DUPFD_CLOEXEC;
field public static final int F_GETFD;
field public static final int F_GETFL;
field public static final int F_GETLK;
@@ -39386,7 +39387,9 @@
field public static final int NI_NUMERICSERV;
field public static final int O_ACCMODE;
field public static final int O_APPEND;
+ field public static final int O_CLOEXEC;
field public static final int O_CREAT;
+ field public static final int O_DSYNC;
field public static final int O_EXCL;
field public static final int O_NOCTTY;
field public static final int O_NOFOLLOW;
@@ -53345,6 +53348,7 @@
}
public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
+ ctor public InMemoryDexClassLoader(java.nio.ByteBuffer[], java.lang.ClassLoader);
ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
}
@@ -72695,6 +72699,8 @@
public class JSONException extends java.lang.Exception {
ctor public JSONException(java.lang.String);
+ ctor public JSONException(java.lang.String, java.lang.Throwable);
+ ctor public JSONException(java.lang.Throwable);
}
public class JSONObject {
diff --git a/api/test-current.txt b/api/test-current.txt
index bd96efb..5dab3d11 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -36424,6 +36424,7 @@
field public static final int FD_CLOEXEC;
field public static final int FIONREAD;
field public static final int F_DUPFD;
+ field public static final int F_DUPFD_CLOEXEC;
field public static final int F_GETFD;
field public static final int F_GETFL;
field public static final int F_GETLK;
@@ -36518,7 +36519,9 @@
field public static final int NI_NUMERICSERV;
field public static final int O_ACCMODE;
field public static final int O_APPEND;
+ field public static final int O_CLOEXEC;
field public static final int O_CREAT;
+ field public static final int O_DSYNC;
field public static final int O_EXCL;
field public static final int O_NOCTTY;
field public static final int O_NOFOLLOW;
@@ -49831,6 +49834,7 @@
}
public final class InMemoryDexClassLoader extends dalvik.system.BaseDexClassLoader {
+ ctor public InMemoryDexClassLoader(java.nio.ByteBuffer[], java.lang.ClassLoader);
ctor public InMemoryDexClassLoader(java.nio.ByteBuffer, java.lang.ClassLoader);
}
@@ -69181,6 +69185,8 @@
public class JSONException extends java.lang.Exception {
ctor public JSONException(java.lang.String);
+ ctor public JSONException(java.lang.String, java.lang.Throwable);
+ ctor public JSONException(java.lang.Throwable);
}
public class JSONObject {
diff --git a/core/java/android/app/timezone/DistroRulesVersion.java b/core/java/android/app/timezone/DistroRulesVersion.java
index 5503ce1..1eb9f45 100644
--- a/core/java/android/app/timezone/DistroRulesVersion.java
+++ b/core/java/android/app/timezone/DistroRulesVersion.java
@@ -125,4 +125,8 @@
+ ", mRevision='" + mRevision + '\''
+ '}';
}
+
+ public String toDumpString() {
+ return mRulesVersion + "," + mRevision;
+ }
}
diff --git a/core/java/android/database/ContentObserver.java b/core/java/android/database/ContentObserver.java
index 5f01e30..4795e97 100644
--- a/core/java/android/database/ContentObserver.java
+++ b/core/java/android/database/ContentObserver.java
@@ -193,6 +193,11 @@
*/
private void dispatchChange(boolean selfChange, Uri uri, int userId) {
if (mHandler == null) {
+ synchronized (mLock) {
+ if (mTransport == null) {
+ return;
+ }
+ }
onChange(selfChange, uri, userId);
} else {
mHandler.post(new NotificationRunnable(selfChange, uri, userId));
@@ -213,6 +218,11 @@
@Override
public void run() {
+ synchronized (mLock) {
+ if (mTransport == null) {
+ return;
+ }
+ }
ContentObserver.this.onChange(mSelfChange, mUri, mUserId);
}
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 02f0f18..55127a8 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -579,6 +579,8 @@
/** {@hide} */
public static final int MAX_NETWORK_TYPE = TYPE_VPN;
+ private static final int MIN_NETWORK_TYPE = TYPE_MOBILE;
+
/**
* If you want to set the default network preference,you can directly
* change the networkAttributes array in framework's config.xml.
@@ -636,7 +638,7 @@
* validate a network type.
*/
public static boolean isNetworkTypeValid(int networkType) {
- return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
+ return MIN_NETWORK_TYPE <= networkType && networkType <= MAX_NETWORK_TYPE;
}
/**
@@ -649,6 +651,8 @@
*/
public static String getNetworkTypeName(int type) {
switch (type) {
+ case TYPE_NONE:
+ return "NONE";
case TYPE_MOBILE:
return "MOBILE";
case TYPE_WIFI:
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 2dd7f75..76646b8 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -21,6 +21,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.BitUtils;
+import com.android.internal.util.Preconditions;
import java.util.Objects;
@@ -429,6 +430,11 @@
/** @hide */
public static final int MAX_TRANSPORT = TRANSPORT_LOWPAN;
+ /** @hide */
+ public static boolean isValidTransport(int transportType) {
+ return (MIN_TRANSPORT <= transportType) && (transportType <= MAX_TRANSPORT);
+ }
+
private static final String[] TRANSPORT_NAMES = {
"CELLULAR",
"WIFI",
@@ -453,9 +459,7 @@
* @hide
*/
public NetworkCapabilities addTransportType(int transportType) {
- if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
- throw new IllegalArgumentException("TransportType out of range");
- }
+ checkValidTransportType(transportType);
mTransportTypes |= 1 << transportType;
setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
return this;
@@ -469,9 +473,7 @@
* @hide
*/
public NetworkCapabilities removeTransportType(int transportType) {
- if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
- throw new IllegalArgumentException("TransportType out of range");
- }
+ checkValidTransportType(transportType);
mTransportTypes &= ~(1 << transportType);
setNetworkSpecifier(mNetworkSpecifier); // used for exception checking
return this;
@@ -495,10 +497,7 @@
* @return {@code true} if set on this instance.
*/
public boolean hasTransport(int transportType) {
- if (transportType < MIN_TRANSPORT || transportType > MAX_TRANSPORT) {
- return false;
- }
- return ((mTransportTypes & (1 << transportType)) != 0);
+ return isValidTransport(transportType) && ((mTransportTypes & (1 << transportType)) != 0);
}
private void combineTransportTypes(NetworkCapabilities nc) {
@@ -906,9 +905,14 @@
* @hide
*/
public static String transportNameOf(int transport) {
- if (transport < 0 || TRANSPORT_NAMES.length <= transport) {
+ if (!isValidTransport(transport)) {
return "UNKNOWN";
}
return TRANSPORT_NAMES[transport];
}
+
+ private static void checkValidTransportType(int transport) {
+ Preconditions.checkArgument(
+ isValidTransport(transport), "Invalid TransportType " + transport);
+ }
}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 42f5feb..84c32be 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -127,7 +127,8 @@
* @hide
*/
public NetworkInfo(int type, int subtype, String typeName, String subtypeName) {
- if (!ConnectivityManager.isNetworkTypeValid(type)) {
+ if (!ConnectivityManager.isNetworkTypeValid(type)
+ && type != ConnectivityManager.TYPE_NONE) {
throw new IllegalArgumentException("Invalid network type: " + type);
}
mNetworkType = type;
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 7678678..218e4f2 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -253,6 +253,20 @@
Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
}
a.recycle();
+ } else if (eventType == XmlPullParser.START_TAG &&
+ tagName.equals("aid-suffix-filter") && currentGroup != null) {
+ final TypedArray a = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AidFilter);
+ String aid = a.getString(com.android.internal.R.styleable.AidFilter_name).
+ toUpperCase();
+ // Add wildcard char to indicate suffix
+ aid = aid.concat("#");
+ if (CardEmulation.isValidAid(aid) && !currentGroup.aids.contains(aid)) {
+ currentGroup.aids.add(aid);
+ } else {
+ Log.e(TAG, "Ignoring invalid or duplicate aid: " + aid);
+ }
+ a.recycle();
}
}
} catch (NameNotFoundException e) {
@@ -297,6 +311,17 @@
return prefixAids;
}
+ public List<String> getSubsetAids() {
+ final ArrayList<String> subsetAids = new ArrayList<String>();
+ for (AidGroup group : getAidGroups()) {
+ for (String aid : group.aids) {
+ if (aid.endsWith("#")) {
+ subsetAids.add(aid);
+ }
+ }
+ }
+ return subsetAids;
+ }
/**
* Returns the registered AID group for this category.
*/
diff --git a/core/java/android/nfc/cardemulation/CardEmulation.java b/core/java/android/nfc/cardemulation/CardEmulation.java
index b49288e..6dd7993 100644
--- a/core/java/android/nfc/cardemulation/CardEmulation.java
+++ b/core/java/android/nfc/cardemulation/CardEmulation.java
@@ -606,6 +606,8 @@
* <li>Consist of only hex characters
* <li>Additionally, we allow an asterisk at the end, to indicate
* a prefix
+ * <li>Additinally we allow an (#) at symbol at the end, to indicate
+ * a subset
* </ul>
*
* @hide
@@ -614,20 +616,20 @@
if (aid == null)
return false;
- // If a prefix AID, the total length must be odd (even # of AID chars + '*')
- if (aid.endsWith("*") && ((aid.length() % 2) == 0)) {
+ // If a prefix/subset AID, the total length must be odd (even # of AID chars + '*')
+ if ((aid.endsWith("*") || aid.endsWith("#")) && ((aid.length() % 2) == 0)) {
Log.e(TAG, "AID " + aid + " is not a valid AID.");
return false;
}
- // If not a prefix AID, the total length must be even (even # of AID chars)
- if (!aid.endsWith("*") && ((aid.length() % 2) != 0)) {
+ // If not a prefix/subset AID, the total length must be even (even # of AID chars)
+ if ((!(aid.endsWith("*") || aid.endsWith("#"))) && ((aid.length() % 2) != 0)) {
Log.e(TAG, "AID " + aid + " is not a valid AID.");
return false;
}
// Verify hex characters
- if (!aid.matches("[0-9A-Fa-f]{10,32}\\*?")) {
+ if (!aid.matches("[0-9A-Fa-f]{10,32}\\*?\\#?")) {
Log.e(TAG, "AID " + aid + " is not a valid AID.");
return false;
}
diff --git a/core/java/android/nfc/cardemulation/HostNfcFService.java b/core/java/android/nfc/cardemulation/HostNfcFService.java
index 27c4976..fd0d8ad 100644
--- a/core/java/android/nfc/cardemulation/HostNfcFService.java
+++ b/core/java/android/nfc/cardemulation/HostNfcFService.java
@@ -64,6 +64,7 @@
* android:description="@string/servicedesc">
* <system-code-filter android:name="4000"/>
* <nfcid2-filter android:name="02FE000000000000"/>
+ <t3tPmm-filter android:name="FFFFFFFFFFFFFFFF"/>
* </host-nfcf-service>
* </pre>
*
@@ -76,6 +77,7 @@
* <ul>
* <li>Exactly one {@link android.R.styleable#SystemCodeFilter <system-code-filter>} tag.</li>
* <li>Exactly one {@link android.R.styleable#Nfcid2Filter <nfcid2-filter>} tag.</li>
+ * <li>Zero or one {@link android.R.styleable#T3tPmmFilter <t3tPmm-filter>} tag.</li>
* </ul>
* </p>
*
diff --git a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
index b93eec1..4201935 100644
--- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
@@ -18,9 +18,9 @@
import android.content.ComponentName;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
@@ -80,11 +80,16 @@
final int mUid;
/**
+ * LF_T3T_PMM of the service
+ */
+ final String mT3tPmm;
+
+ /**
* @hide
*/
public NfcFServiceInfo(ResolveInfo info, String description,
String systemCode, String dynamicSystemCode, String nfcid2, String dynamicNfcid2,
- int uid) {
+ int uid, String t3tPmm) {
this.mService = info;
this.mDescription = description;
this.mSystemCode = systemCode;
@@ -92,6 +97,7 @@
this.mNfcid2 = nfcid2;
this.mDynamicNfcid2 = dynamicNfcid2;
this.mUid = uid;
+ this.mT3tPmm = t3tPmm;
}
public NfcFServiceInfo(PackageManager pm, ResolveInfo info)
@@ -130,6 +136,7 @@
String systemCode = null;
String nfcid2 = null;
+ String t3tPmm = null;
final int depth = parser.getDepth();
while (((eventType = parser.next()) != XmlPullParser.END_TAG ||
@@ -160,10 +167,22 @@
nfcid2 = null;
}
a.recycle();
+ } else if (eventType == XmlPullParser.START_TAG && tagName.equals("t3tPmm-filter")
+ && t3tPmm == null) {
+ final TypedArray a = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.T3tPmmFilter);
+ t3tPmm = a.getString(
+ com.android.internal.R.styleable.T3tPmmFilter_name).toUpperCase();
+ if (t3tPmm == null) {
+ String defaultT3tPmm = "FFFFFFFFFFFFFFFF";
+ t3tPmm = defaultT3tPmm;
+ }
+ a.recycle();
}
}
mSystemCode = (systemCode == null ? "NULL" : systemCode);
mNfcid2 = (nfcid2 == null ? "NULL" : nfcid2);
+ mT3tPmm = (t3tPmm == null ? "NULL" : t3tPmm);
} catch (NameNotFoundException e) {
throw new XmlPullParserException("Unable to create context for: " + si.packageName);
} finally {
@@ -202,6 +221,10 @@
return mUid;
}
+ public String getT3tPmm() {
+ return mT3tPmm;
+ }
+
public CharSequence loadLabel(PackageManager pm) {
return mService.loadLabel(pm);
}
@@ -223,6 +246,7 @@
if (mDynamicNfcid2 != null) {
out.append(", dynamic NFCID2: " + mDynamicNfcid2);
}
+ out.append(", T3T PMM:" + mT3tPmm);
return out.toString();
}
@@ -235,7 +259,7 @@
if (!thatService.getComponent().equals(this.getComponent())) return false;
if (!thatService.mSystemCode.equalsIgnoreCase(this.mSystemCode)) return false;
if (!thatService.mNfcid2.equalsIgnoreCase(this.mNfcid2)) return false;
-
+ if (!thatService.mT3tPmm.equalsIgnoreCase(this.mT3tPmm)) return false;
return true;
}
@@ -264,6 +288,7 @@
dest.writeString(mDynamicNfcid2);
}
dest.writeInt(mUid);
+ dest.writeString(mT3tPmm);
};
public static final Parcelable.Creator<NfcFServiceInfo> CREATOR =
@@ -283,8 +308,9 @@
dynamicNfcid2 = source.readString();
}
int uid = source.readInt();
+ String t3tPmm = source.readString();
NfcFServiceInfo service = new NfcFServiceInfo(info, description,
- systemCode, dynamicSystemCode, nfcid2, dynamicNfcid2, uid);
+ systemCode, dynamicSystemCode, nfcid2, dynamicNfcid2, uid, t3tPmm);
return service;
}
@@ -299,6 +325,7 @@
" (Description: " + getDescription() + ")");
pw.println(" System Code: " + getSystemCode());
pw.println(" NFCID2: " + getNfcid2());
+ pw.println(" T3tPmm: " + getT3tPmm());
}
}
diff --git a/core/java/com/android/internal/app/procstats/PssTable.java b/core/java/com/android/internal/app/procstats/PssTable.java
index b6df983..de5f673 100644
--- a/core/java/com/android/internal/app/procstats/PssTable.java
+++ b/core/java/com/android/internal/app/procstats/PssTable.java
@@ -96,7 +96,7 @@
}
val = getValue(key, PSS_USS_AVERAGE);
- setValue(key, PSS_AVERAGE,
+ setValue(key, PSS_USS_AVERAGE,
(long)(((val*(double)count)+(avgUss*(double)inCount)) / (count+inCount)));
val = getValue(key, PSS_USS_MAXIMUM);
diff --git a/core/jni/Android.bp b/core/jni/Android.bp
index 2006be0..ca11404 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -233,6 +233,7 @@
"libGLESv1_CM",
"libGLESv2",
"libvulkan",
+ "libziparchive",
"libETC1",
"libhardware",
"libhardware_legacy",
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 81504c0..facc0f9 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -799,19 +799,27 @@
"--compiler-filter=", "-Ximage-compiler-option");
}
- // Make sure there is a preloaded-classes file.
- if (!hasFile("/system/etc/preloaded-classes")) {
- ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n",
- strerror(errno));
- return -1;
- }
- addOption("-Ximage-compiler-option");
- addOption("--image-classes=/system/etc/preloaded-classes");
-
- // If there is a compiled-classes file, push it.
- if (hasFile("/system/etc/compiled-classes")) {
+ // If there is a boot profile, it takes precedence over the image and preloaded classes.
+ if (hasFile("/system/etc/boot-image.prof")) {
addOption("-Ximage-compiler-option");
- addOption("--compiled-classes=/system/etc/compiled-classes");
+ addOption("--profile-file=/system/etc/boot-image.prof");
+ addOption("-Ximage-compiler-option");
+ addOption("--compiler-filter=speed-profile");
+ } else {
+ // Make sure there is a preloaded-classes file.
+ if (!hasFile("/system/etc/preloaded-classes")) {
+ ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ addOption("-Ximage-compiler-option");
+ addOption("--image-classes=/system/etc/preloaded-classes");
+
+ // If there is a compiled-classes file, push it.
+ if (hasFile("/system/etc/compiled-classes")) {
+ addOption("-Ximage-compiler-option");
+ addOption("--compiled-classes=/system/etc/compiled-classes");
+ }
}
property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index a271aad..4fc8e11 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -368,7 +368,7 @@
if (transport != IServiceManager::Transport::HWBINDER && !vintfLegacy) {
LOG(ERROR) << "service " << ifaceName << " declares transport method "
<< toString(transport) << " but framework expects hwbinder.";
- signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
+ signalExceptionForError(env, NAME_NOT_FOUND, true /* canThrowRemoteException */);
return NULL;
}
diff --git a/core/jni/android_os_VintfObject.cpp b/core/jni/android_os_VintfObject.cpp
index fa9379e..8b4cc91 100644
--- a/core/jni/android_os_VintfObject.cpp
+++ b/core/jni/android_os_VintfObject.cpp
@@ -102,7 +102,10 @@
cPackageInfo[i] = cString;
env->ReleaseStringUTFChars(element, cString);
}
- int32_t status = VintfObject::CheckCompatibility(cPackageInfo);
+ std::string error;
+ int32_t status = VintfObject::CheckCompatibility(cPackageInfo, &error);
+ if (status)
+ LOG(WARNING) << "VintfObject.verify() returns " << status << ": " << error;
return status;
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index d5ac4ae..d215416 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -273,6 +273,7 @@
<protected-broadcast android:name="com.android.nfc.cardemulation.action.CLOSE_TAP_DIALOG" />
<protected-broadcast android:name="com.android.nfc.handover.action.ALLOW_CONNECT" />
<protected-broadcast android:name="com.android.nfc.handover.action.DENY_CONNECT" />
+ <protected-broadcast android:name="com.android.nfc.handover.action.TIMEOUT_CONNECT" />
<protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_ON_DETECTED" />
<protected-broadcast android:name="com.android.nfc_extras.action.RF_FIELD_OFF_DETECTED" />
<protected-broadcast android:name="com.android.nfc_extras.action.AID_SELECTED" />
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 066a538..d51a304 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -3462,6 +3462,13 @@
<attr name="name" />
</declare-styleable>
+ <!-- Specify one or more <code>t3tPmm-filter</code> elements inside a
+ <code>host-nfcf-service</code> element to specify a LF_T3T_PMM -->
+ <declare-styleable name="T3tPmmFilter">
+ <attr name="name" />
+
+ </declare-styleable>
+
<declare-styleable name="ActionMenuItemView">
<attr name="minWidth" />
</declare-styleable>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 42383b7..c783caa 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -281,6 +281,13 @@
Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
<integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
+ <!-- If the hardware supports specially marking packets that caused a wakeup of the
+ main CPU, set this value to the mark used. -->
+ <integer name="config_networkWakeupPacketMark">0</integer>
+
+ <!-- Mask to use when checking skb mark defined in config_networkWakeupPacketMark above. -->
+ <integer name="config_networkWakeupPacketMask">0</integer>
+
<!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual
device behaviour is controlled by Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE.
This is the default value of that setting. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7363a9b..758ee4a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1788,6 +1788,8 @@
<java-symbol type="integer" name="config_networkNotifySwitchType" />
<java-symbol type="array" name="config_networkNotifySwitches" />
<java-symbol type="integer" name="config_networkAvoidBadWifi" />
+ <java-symbol type="integer" name="config_networkWakeupPacketMark" />
+ <java-symbol type="integer" name="config_networkWakeupPacketMask" />
<java-symbol type="integer" name="config_networkMeteredMultipathPreference" />
<java-symbol type="integer" name="config_notificationsBatteryFullARGB" />
<java-symbol type="integer" name="config_notificationsBatteryLedOff" />
diff --git a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
index ec130e0..11d066b 100644
--- a/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
+++ b/core/tests/coretests/src/android/net/SSLSessionCacheTest.java
@@ -44,12 +44,9 @@
try {
SSLSessionCache.install(new SSLSessionCache(mock), ctx);
- clientCtx.getSession("www.foogle.com", 443);
- Mockito.verify(mock).getSessionData(Mockito.anyString(), Mockito.anyInt());
} finally {
// Restore cacheless behaviour.
SSLSessionCache.install(null, ctx);
- clientCtx.getSession("www.foogle.com", 443);
Mockito.verifyNoMoreInteractions(mock);
}
}
diff --git a/libs/androidfw/Android.bp b/libs/androidfw/Android.bp
index 5d12d95..1198962 100644
--- a/libs/androidfw/Android.bp
+++ b/libs/androidfw/Android.bp
@@ -64,7 +64,16 @@
shared: {
enabled: false,
},
- shared_libs: ["libz-host"],
+ static_libs: [
+ "libziparchive",
+ "libbase",
+ "liblog",
+ "libcutils",
+ "libutils",
+ ],
+ shared_libs: [
+ "libz-host",
+ ],
},
windows: {
enabled: true,
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 7480ad1..fc01a40 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -31,6 +31,7 @@
import android.net.Proxy;
import android.net.Uri;
import android.net.http.SslError;
+import android.os.Build;
import android.os.Bundle;
import android.provider.Settings;
import android.util.ArrayMap;
@@ -57,6 +58,7 @@
import java.lang.InterruptedException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
+import java.util.Objects;
import java.util.Random;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -286,6 +288,18 @@
return null;
}
+ private static String host(URL url) {
+ if (url == null) {
+ return null;
+ }
+ return url.getHost();
+ }
+
+ private static String sanitizeURL(URL url) {
+ // In non-Debug build, only show host to avoid leaking private info.
+ return Build.IS_DEBUGGABLE ? Objects.toString(url) : host(url);
+ }
+
private void testForCaptivePortal() {
// TODO: reuse NetworkMonitor facilities for consistent captive portal detection.
new Thread(new Runnable() {
@@ -339,6 +353,8 @@
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
getResources().getDisplayMetrics());
private int mPagesLoaded;
+ // the host of the page that this webview is currently loading. Can be null when undefined.
+ private String mHostname;
// If we haven't finished cleaning up the history, don't allow going back.
public boolean allowBack() {
@@ -346,8 +362,8 @@
}
@Override
- public void onPageStarted(WebView view, String url, Bitmap favicon) {
- if (url.contains(mBrowserBailOutToken)) {
+ public void onPageStarted(WebView view, String urlString, Bitmap favicon) {
+ if (urlString.contains(mBrowserBailOutToken)) {
mLaunchBrowser = true;
done(Result.WANTED_AS_IS);
return;
@@ -355,11 +371,17 @@
// The first page load is used only to cause the WebView to
// fetch the proxy settings. Don't update the URL bar, and
// don't check if the captive portal is still there.
- if (mPagesLoaded == 0) return;
+ if (mPagesLoaded == 0) {
+ return;
+ }
+ final URL url = makeURL(urlString);
+ Log.d(TAG, "onPageSarted: " + sanitizeURL(url));
+ mHostname = host(url);
// For internally generated pages, leave URL bar listing prior URL as this is the URL
// the page refers to.
- if (!url.startsWith(INTERNAL_ASSETS)) {
- getActionBar().setSubtitle(getHeaderSubtitle(url));
+ if (!urlString.startsWith(INTERNAL_ASSETS)) {
+ String subtitle = (url != null) ? getHeaderSubtitle(url) : urlString;
+ getActionBar().setSubtitle(subtitle);
}
getProgressBar().setVisibility(View.VISIBLE);
testForCaptivePortal();
@@ -398,15 +420,18 @@
@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
- logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR);
- Log.w(TAG, "SSL error (error: " + error.getPrimaryError() + " host: " +
- // Only show host to avoid leaking private info.
- Uri.parse(error.getUrl()).getHost() + " certificate: " +
- error.getCertificate() + "); displaying SSL warning.");
- final String sslErrorPage = makeSslErrorPage();
- if (VDBG) {
- Log.d(TAG, sslErrorPage);
+ final URL url = makeURL(error.getUrl());
+ final String host = host(url);
+ Log.d(TAG, String.format("SSL error: %s, url: %s, certificate: %s",
+ error.getPrimaryError(), sanitizeURL(url), error.getCertificate()));
+ if (url == null || !Objects.equals(host, mHostname)) {
+ // Ignore ssl errors for resources coming from a different hostname than the page
+ // that we are currently loading, and only cancel the request.
+ handler.cancel();
+ return;
}
+ logMetricsEvent(MetricsEvent.CAPTIVE_PORTAL_LOGIN_ACTIVITY_SSL_ERROR);
+ final String sslErrorPage = makeSslErrorPage();
view.loadDataWithBaseURL(INTERNAL_ASSETS, sslErrorPage, "text/HTML", "UTF-8", null);
}
@@ -499,16 +524,13 @@
return getString(R.string.action_bar_title, info.getExtraInfo().replaceAll("^\"|\"$", ""));
}
- private String getHeaderSubtitle(String urlString) {
- URL url = makeURL(urlString);
- if (url == null) {
- return urlString;
- }
+ private String getHeaderSubtitle(URL url) {
+ String host = host(url);
final String https = "https";
if (https.equals(url.getProtocol())) {
- return https + "://" + url.getHost();
+ return https + "://" + host;
}
- return url.getHost();
+ return host;
}
private void logMetricsEvent(int event) {
diff --git a/services/core/Android.mk b/services/core/Android.mk
index bc97330..4e48afc 100644
--- a/services/core/Android.mk
+++ b/services/core/Android.mk
@@ -41,4 +41,16 @@
-D jack.transformations.boost-locked-region-priority.request=com.android.server.am.ActivityManagerService\#boostPriorityForLockedSection \
-D jack.transformations.boost-locked-region-priority.reset=com.android.server.am.ActivityManagerService\#resetPriorityAfterLockedSection
+LOCAL_JAR_PROCESSOR := lockedregioncodeinjection
+# Use = instead of := to delay evaluation of ${in} and ${out}
+LOCAL_JAR_PROCESSOR_ARGS = \
+ --targets \
+ "Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowHashMap;" \
+ --pre \
+ "com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection" \
+ --post \
+ "com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection" \
+ -o ${out} \
+ -i ${in}
+
include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 2435c27..eafa88f 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -215,13 +215,6 @@
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
private final int mReleasePendingIntentDelayMs;
- // Driver specific constants used to select packets received via
- // WiFi that caused the phone to exit sleep state. Currently there
- // is only one kernel implementation so we can get away with
- // constants.
- private static final int mWakeupPacketMark = 0x80000000;
- private static final int mWakeupPacketMask = 0x80000000;
-
private MockableSystemProperties mSystemProperties;
private Tethering mTethering;
@@ -2410,6 +2403,10 @@
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
mKeepaliveTracker.handleStopAllKeepalives(nai,
ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
+ for (String iface : nai.linkProperties.getAllInterfaceNames()) {
+ // Disable wakeup packet monitoring for each interface.
+ wakeupModifyInterface(iface, nai.networkCapabilities, false);
+ }
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
updateClat(null, nai.linkProperties, nai);
@@ -3911,6 +3908,9 @@
public void setProvisioningNotificationVisible(boolean visible, int networkType,
String action) {
enforceConnectivityInternalPermission();
+ if (!ConnectivityManager.isNetworkTypeValid(networkType)) {
+ return;
+ }
final long ident = Binder.clearCallingIdentity();
try {
// Concatenate the range of types onto the range of NetIDs.
@@ -4529,22 +4529,35 @@
}
}
- private void wakeupAddInterface(String iface, NetworkCapabilities caps) throws RemoteException {
+ private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
// Marks are only available on WiFi interaces. Checking for
// marks on unsupported interfaces is harmless.
if (!caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return;
}
- mNetd.getNetdService().wakeupAddInterface(
- iface, "iface:" + iface, mWakeupPacketMark, mWakeupPacketMask);
- }
- private void wakeupDelInterface(String iface, NetworkCapabilities caps) throws RemoteException {
- if (!caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ int mark = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_networkWakeupPacketMark);
+ int mask = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_networkWakeupPacketMask);
+
+ // Mask/mark of zero will not detect anything interesting.
+ // Don't install rules unless both values are nonzero.
+ if (mark == 0 || mask == 0) {
return;
}
- mNetd.getNetdService().wakeupDelInterface(
- iface, "iface:" + iface, mWakeupPacketMark, mWakeupPacketMask);
+
+ final String prefix = "iface:" + iface;
+ try {
+ if (add) {
+ mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask);
+ } else {
+ mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask);
+ }
+ } catch (Exception e) {
+ loge("Exception modifying wakeup packet monitoring: " + e);
+ }
+
}
private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId,
@@ -4559,7 +4572,7 @@
try {
if (DBG) log("Adding iface " + iface + " to network " + netId);
mNetd.addInterfaceToNetwork(iface, netId);
- wakeupAddInterface(iface, caps);
+ wakeupModifyInterface(iface, caps, true);
} catch (Exception e) {
loge("Exception adding interface: " + e);
}
@@ -4567,8 +4580,8 @@
for (String iface : interfaceDiff.removed) {
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
+ wakeupModifyInterface(iface, caps, false);
mNetd.removeInterfaceFromNetwork(iface, netId);
- wakeupDelInterface(iface, caps);
} catch (Exception e) {
loge("Exception removing interface: " + e);
}
@@ -5065,7 +5078,7 @@
if (newNetwork.getCurrentScore() != score) {
Slog.wtf(TAG, String.format(
"BUG: %s changed score during rematch: %d -> %d",
- score, newNetwork.getCurrentScore()));
+ newNetwork.name(), score, newNetwork.getCurrentScore()));
}
// Second pass: process all listens.
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index 66347e6..56859e6 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -29,6 +29,7 @@
import android.net.CaptivePortal;
import android.net.ConnectivityManager;
import android.net.ICaptivePortal;
+import android.net.Network;
import android.net.NetworkRequest;
import android.net.ProxyInfo;
import android.net.TrafficStats;
@@ -71,6 +72,9 @@
import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
@@ -95,7 +99,7 @@
"http://play.googleapis.com/generate_204";
private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) "
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
- + "Chrome/52.0.2743.82 Safari/537.36";
+ + "Chrome/60.0.3112.32 Safari/537.36";
private static final int SOCKET_TIMEOUT_MS = 10000;
private static final int PROBE_TIMEOUT_MS = 3000;
@@ -228,6 +232,7 @@
private final Context mContext;
private final Handler mConnectivityServiceHandler;
private final NetworkAgentInfo mNetworkAgentInfo;
+ private final Network mNetwork;
private final int mNetId;
private final TelephonyManager mTelephonyManager;
private final WifiManager mWifiManager;
@@ -286,7 +291,8 @@
mMetricsLog = logger;
mConnectivityServiceHandler = handler;
mNetworkAgentInfo = networkAgentInfo;
- mNetId = mNetworkAgentInfo.network.netId;
+ mNetwork = new OneAddressPerFamilyNetwork(networkAgentInfo.network);
+ mNetId = mNetwork.netId;
mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
@@ -415,7 +421,7 @@
maybeLogEvaluationResult(
networkEventType(validationStage(), EvaluationResult.VALIDATED));
mConnectivityServiceHandler.sendMessage(obtainMessage(EVENT_NETWORK_TESTED,
- NETWORK_TEST_RESULT_VALID, mNetworkAgentInfo.network.netId, null));
+ NETWORK_TEST_RESULT_VALID, mNetId, null));
mValidations++;
}
@@ -440,7 +446,8 @@
case CMD_LAUNCH_CAPTIVE_PORTAL_APP:
final Intent intent = new Intent(
ConnectivityManager.ACTION_CAPTIVE_PORTAL_SIGN_IN);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK, mNetworkAgentInfo.network);
+ // OneAddressPerFamilyNetwork is not parcelable across processes.
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK, new Network(mNetwork));
intent.putExtra(ConnectivityManager.EXTRA_CAPTIVE_PORTAL,
new CaptivePortal(new ICaptivePortal.Stub() {
@Override
@@ -468,8 +475,7 @@
@Override
public void exit() {
- Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0,
- mNetworkAgentInfo.network.netId, null);
+ Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 0, mNetId, null);
mConnectivityServiceHandler.sendMessage(message);
}
}
@@ -623,7 +629,7 @@
CustomIntentReceiver(String action, int token, int what) {
mToken = token;
mWhat = what;
- mAction = action + "_" + mNetworkAgentInfo.network.netId + "_" + token;
+ mAction = action + "_" + mNetId + "_" + token;
mContext.registerReceiver(this, new IntentFilter(mAction));
}
public PendingIntent getPendingIntent() {
@@ -659,8 +665,7 @@
CMD_LAUNCH_CAPTIVE_PORTAL_APP);
}
// Display the sign in notification.
- Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1,
- mNetworkAgentInfo.network.netId,
+ Message message = obtainMessage(EVENT_PROVISIONING_NOTIFICATION, 1, mNetId,
mLaunchCaptivePortalAppBroadcastReceiver.getPendingIntent());
mConnectivityServiceHandler.sendMessage(message);
// Retest for captive portal occasionally.
@@ -675,6 +680,31 @@
}
}
+ // Limits the list of IP addresses returned by getAllByName or tried by openConnection to at
+ // most one per address family. This ensures we only wait up to 20 seconds for TCP connections
+ // to complete, regardless of how many IP addresses a host has.
+ private static class OneAddressPerFamilyNetwork extends Network {
+ public OneAddressPerFamilyNetwork(Network network) {
+ super(network);
+ }
+
+ @Override
+ public InetAddress[] getAllByName(String host) throws UnknownHostException {
+ List<InetAddress> addrs = Arrays.asList(super.getAllByName(host));
+
+ // Ensure the address family of the first address is tried first.
+ LinkedHashMap<Class, InetAddress> addressByFamily = new LinkedHashMap<>();
+ addressByFamily.put(addrs.get(0).getClass(), addrs.get(0));
+ Collections.shuffle(addrs);
+
+ for (InetAddress addr : addrs) {
+ addressByFamily.put(addr.getClass(), addr);
+ }
+
+ return addressByFamily.values().toArray(new InetAddress[addressByFamily.size()]);
+ }
+ }
+
private static String getCaptivePortalServerHttpsUrl(Context context) {
return getSetting(context, Settings.Global.CAPTIVE_PORTAL_HTTPS_URL, DEFAULT_HTTPS_URL);
}
@@ -805,7 +835,7 @@
int result;
String connectInfo;
try {
- InetAddress[] addresses = mNetworkAgentInfo.network.getAllByName(host);
+ InetAddress[] addresses = mNetwork.getAllByName(host);
StringBuffer buffer = new StringBuffer();
for (InetAddress address : addresses) {
buffer.append(',').append(address.getHostAddress());
@@ -833,7 +863,7 @@
String redirectUrl = null;
final Stopwatch probeTimer = new Stopwatch().start();
try {
- urlConnection = (HttpURLConnection) mNetworkAgentInfo.network.openConnection(url);
+ urlConnection = (HttpURLConnection) mNetwork.openConnection(url);
urlConnection.setInstanceFollowRedirects(probeType == ValidationProbeEvent.PROBE_PAC);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index 3327bec..ae385c1 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -47,6 +48,7 @@
import android.net.ConnectivityManager;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
+import android.net.IpPrefix;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
@@ -104,6 +106,7 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
@@ -210,7 +213,7 @@
mContext.getContentResolver(),
mLog);
mUpstreamNetworkMonitor = new UpstreamNetworkMonitor(
- mContext, mTetherMasterSM, TetherMasterSM.EVENT_UPSTREAM_CALLBACK, mLog);
+ mContext, mTetherMasterSM, mLog, TetherMasterSM.EVENT_UPSTREAM_CALLBACK );
mForwardedDownstreams = new HashSet<>();
mSimChange = new SimChangeListener(
mContext, mTetherMasterSM.getHandler(), () -> reevaluateSimCardProvisioning());
@@ -698,7 +701,7 @@
private void showTetheredNotification(int icon) {
NotificationManager notificationManager =
- (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
return;
}
@@ -745,7 +748,7 @@
private void clearTetheredNotification() {
NotificationManager notificationManager =
- (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null && mLastNotificationId != 0) {
notificationManager.cancelAsUser(null, mLastNotificationId,
UserHandle.ALL);
@@ -784,11 +787,37 @@
private void handleUsbAction(Intent intent) {
final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false);
+ final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false);
final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
+
+ mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s",
+ usbConnected, usbConfigured, rndisEnabled));
+
+ // There are three types of ACTION_USB_STATE:
+ //
+ // - DISCONNECTED (USB_CONNECTED and USB_CONFIGURED are 0)
+ // Meaning: USB connection has ended either because of
+ // software reset or hard unplug.
+ //
+ // - CONNECTED (USB_CONNECTED is 1, USB_CONFIGURED is 0)
+ // Meaning: the first stage of USB protocol handshake has
+ // occurred but it is not complete.
+ //
+ // - CONFIGURED (USB_CONNECTED and USB_CONFIGURED are 1)
+ // Meaning: the USB handshake is completely done and all the
+ // functions are ready to use.
+ //
+ // For more explanation, see b/62552150 .
+ if (usbConnected && !usbConfigured) {
+ // Nothing for us to do here.
+ // TODO: consider ignoring DISCONNECTED broadcasts as well.
+ return;
+ }
+
synchronized (Tethering.this.mPublicSync) {
mRndisEnabled = rndisEnabled;
// start tethering if we have a request pending
- if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
+ if (usbConfigured && mRndisEnabled && mUsbTetherRequested) {
tetherMatchingInterfaces(
IControlsTethering.STATE_TETHERED,
ConnectivityManager.TETHERING_USB);
@@ -971,7 +1000,7 @@
public int setUsbTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
- UsbManager usbManager = mContext.getSystemService(UsbManager.class);
+ UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
synchronized (mPublicSync) {
if (enable) {
@@ -1048,11 +1077,8 @@
return list.toArray(new String[list.size()]);
}
- private void maybeLogMessage(State state, int what) {
- if (DBG) {
- Log.d(TAG, state.getName() + " got " +
- sMagicDecoderRing.get(what, Integer.toString(what)));
- }
+ private void logMessage(State state, int what) {
+ mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what)));
}
private boolean upstreamWanted() {
@@ -1066,7 +1092,7 @@
// Needed because the canonical source of upstream truth is just the
// upstream interface name, |mCurrentUpstreamIface|. This is ripe for
// future simplification, once the upstream Network is canonical.
- boolean pertainsToCurrentUpstream(NetworkState ns) {
+ private boolean pertainsToCurrentUpstream(NetworkState ns) {
if (ns != null && ns.linkProperties != null && mCurrentUpstreamIface != null) {
for (String ifname : ns.linkProperties.getAllInterfaceNames()) {
if (mCurrentUpstreamIface.equals(ifname)) {
@@ -1100,6 +1126,12 @@
}
}
+ private void startOffloadController() {
+ mOffloadController.start();
+ mOffloadController.updateExemptPrefixes(
+ mUpstreamNetworkMonitor.getOffloadExemptPrefixes());
+ }
+
class TetherMasterSM extends StateMachine {
private static final int BASE_MASTER = Protocol.BASE_TETHERING;
// an interface SM has requested Tethering/Local Hotspot
@@ -1170,7 +1202,7 @@
class InitialState extends State {
@Override
public boolean processMessage(Message message) {
- maybeLogMessage(this, message.what);
+ logMessage(this, message.what);
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE:
TetherInterfaceStateMachine who = (TetherInterfaceStateMachine)message.obj;
@@ -1193,146 +1225,138 @@
}
}
- class TetherMasterUtilState extends State {
- @Override
- public boolean processMessage(Message m) {
+ protected boolean turnOnMasterTetherSettings() {
+ final TetheringConfiguration cfg = mConfig;
+ try {
+ mNMService.setIpForwardingEnabled(true);
+ } catch (Exception e) {
+ mLog.e(e);
+ transitionTo(mSetIpForwardingEnabledErrorState);
return false;
}
-
- protected boolean turnOnMasterTetherSettings() {
- final TetheringConfiguration cfg = mConfig;
- try {
- mNMService.setIpForwardingEnabled(true);
- } catch (Exception e) {
- mLog.e(e);
- transitionTo(mSetIpForwardingEnabledErrorState);
- return false;
- }
- // TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
- try {
- // TODO: Find a more accurate method name (startDHCPv4()?).
- mNMService.startTethering(cfg.dhcpRanges);
- } catch (Exception e) {
- try {
- mNMService.stopTethering();
- mNMService.startTethering(cfg.dhcpRanges);
- } catch (Exception ee) {
- mLog.e(ee);
- transitionTo(mStartTetheringErrorState);
- return false;
- }
- }
- mLog.log("SET master tether settings: ON");
- return true;
- }
-
- protected boolean turnOffMasterTetherSettings() {
+ // TODO: Randomize DHCPv4 ranges, especially in hotspot mode.
+ try {
+ // TODO: Find a more accurate method name (startDHCPv4()?).
+ mNMService.startTethering(cfg.dhcpRanges);
+ } catch (Exception e) {
try {
mNMService.stopTethering();
- } catch (Exception e) {
- mLog.e(e);
- transitionTo(mStopTetheringErrorState);
+ mNMService.startTethering(cfg.dhcpRanges);
+ } catch (Exception ee) {
+ mLog.e(ee);
+ transitionTo(mStartTetheringErrorState);
return false;
}
- try {
- mNMService.setIpForwardingEnabled(false);
- } catch (Exception e) {
- mLog.e(e);
- transitionTo(mSetIpForwardingDisabledErrorState);
- return false;
- }
- transitionTo(mInitialState);
- mLog.log("SET master tether settings: OFF");
- return true;
}
+ mLog.log("SET master tether settings: ON");
+ return true;
+ }
- protected void chooseUpstreamType(boolean tryCell) {
- updateConfiguration(); // TODO - remove?
-
- final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
- mConfig.preferredUpstreamIfaceTypes);
- if (ns == null) {
- if (tryCell) {
- mUpstreamNetworkMonitor.registerMobileNetworkRequest();
- // We think mobile should be coming up; don't set a retry.
- } else {
- sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
- }
- }
- setUpstreamNetwork(ns);
+ protected boolean turnOffMasterTetherSettings() {
+ try {
+ mNMService.stopTethering();
+ } catch (Exception e) {
+ mLog.e(e);
+ transitionTo(mStopTetheringErrorState);
+ return false;
}
+ try {
+ mNMService.setIpForwardingEnabled(false);
+ } catch (Exception e) {
+ mLog.e(e);
+ transitionTo(mSetIpForwardingDisabledErrorState);
+ return false;
+ }
+ transitionTo(mInitialState);
+ mLog.log("SET master tether settings: OFF");
+ return true;
+ }
- protected void setUpstreamNetwork(NetworkState ns) {
- String iface = null;
- if (ns != null && ns.linkProperties != null) {
- // Find the interface with the default IPv4 route. It may be the
- // interface described by linkProperties, or one of the interfaces
- // stacked on top of it.
- Log.i(TAG, "Finding IPv4 upstream interface on: " + ns.linkProperties);
- RouteInfo ipv4Default = RouteInfo.selectBestRoute(
- ns.linkProperties.getAllRoutes(), Inet4Address.ANY);
- if (ipv4Default != null) {
- iface = ipv4Default.getInterface();
- Log.i(TAG, "Found interface " + ipv4Default.getInterface());
- } else {
- Log.i(TAG, "No IPv4 upstream interface, giving up.");
- }
- }
+ protected void chooseUpstreamType(boolean tryCell) {
+ updateConfiguration(); // TODO - remove?
- if (iface != null) {
- setDnsForwarders(ns.network, ns.linkProperties);
+ final NetworkState ns = mUpstreamNetworkMonitor.selectPreferredUpstreamType(
+ mConfig.preferredUpstreamIfaceTypes);
+ if (ns == null) {
+ if (tryCell) {
+ mUpstreamNetworkMonitor.registerMobileNetworkRequest();
+ // We think mobile should be coming up; don't set a retry.
+ } else {
+ sendMessageDelayed(CMD_RETRY_UPSTREAM, UPSTREAM_SETTLE_TIME_MS);
}
- notifyTetheredOfNewUpstreamIface(iface);
- if (ns != null && pertainsToCurrentUpstream(ns)) {
- // If we already have NetworkState for this network examine
- // it immediately, because there likely will be no second
- // EVENT_ON_AVAILABLE (it was already received).
- handleNewUpstreamNetworkState(ns);
- } else if (mCurrentUpstreamIface == null) {
- // There are no available upstream networks, or none that
- // have an IPv4 default route (current metric for success).
- handleNewUpstreamNetworkState(null);
+ }
+ setUpstreamNetwork(ns);
+ }
+
+ protected void setUpstreamNetwork(NetworkState ns) {
+ String iface = null;
+ if (ns != null && ns.linkProperties != null) {
+ // Find the interface with the default IPv4 route. It may be the
+ // interface described by linkProperties, or one of the interfaces
+ // stacked on top of it.
+ mLog.i("Finding IPv4 upstream interface on: " + ns.linkProperties);
+ RouteInfo ipv4Default = RouteInfo.selectBestRoute(
+ ns.linkProperties.getAllRoutes(), Inet4Address.ANY);
+ if (ipv4Default != null) {
+ iface = ipv4Default.getInterface();
+ mLog.i("Found interface " + ipv4Default.getInterface());
+ } else {
+ mLog.i("No IPv4 upstream interface, giving up.");
}
}
- protected void setDnsForwarders(final Network network, final LinkProperties lp) {
- // TODO: Set v4 and/or v6 DNS per available connectivity.
- String[] dnsServers = mConfig.defaultIPv4DNS;
- final Collection<InetAddress> dnses = lp.getDnsServers();
- // TODO: Properly support the absence of DNS servers.
- if (dnses != null && !dnses.isEmpty()) {
- // TODO: remove this invocation of NetworkUtils.makeStrings().
- dnsServers = NetworkUtils.makeStrings(dnses);
- }
- try {
- mNMService.setDnsForwarders(network, dnsServers);
- mLog.log(String.format(
- "SET DNS forwarders: network=%s dnsServers=%s",
- network, Arrays.toString(dnsServers)));
- } catch (Exception e) {
- // TODO: Investigate how this can fail and what exactly
- // happens if/when such failures occur.
- mLog.e("setting DNS forwarders failed, " + e);
- transitionTo(mSetDnsForwardersErrorState);
- }
+ if (iface != null) {
+ setDnsForwarders(ns.network, ns.linkProperties);
}
+ notifyDownstreamsOfNewUpstreamIface(iface);
+ if (ns != null && pertainsToCurrentUpstream(ns)) {
+ // If we already have NetworkState for this network examine
+ // it immediately, because there likely will be no second
+ // EVENT_ON_AVAILABLE (it was already received).
+ handleNewUpstreamNetworkState(ns);
+ } else if (mCurrentUpstreamIface == null) {
+ // There are no available upstream networks, or none that
+ // have an IPv4 default route (current metric for success).
+ handleNewUpstreamNetworkState(null);
+ }
+ }
- protected void notifyTetheredOfNewUpstreamIface(String ifaceName) {
- if (DBG) Log.d(TAG, "Notifying tethered with upstream=" + ifaceName);
- mCurrentUpstreamIface = ifaceName;
- for (TetherInterfaceStateMachine sm : mNotifyList) {
- sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
- ifaceName);
- }
+ protected void setDnsForwarders(final Network network, final LinkProperties lp) {
+ // TODO: Set v4 and/or v6 DNS per available connectivity.
+ String[] dnsServers = mConfig.defaultIPv4DNS;
+ final Collection<InetAddress> dnses = lp.getDnsServers();
+ // TODO: Properly support the absence of DNS servers.
+ if (dnses != null && !dnses.isEmpty()) {
+ // TODO: remove this invocation of NetworkUtils.makeStrings().
+ dnsServers = NetworkUtils.makeStrings(dnses);
}
+ try {
+ mNMService.setDnsForwarders(network, dnsServers);
+ mLog.log(String.format(
+ "SET DNS forwarders: network=%s dnsServers=%s",
+ network, Arrays.toString(dnsServers)));
+ } catch (Exception e) {
+ // TODO: Investigate how this can fail and what exactly
+ // happens if/when such failures occur.
+ mLog.e("setting DNS forwarders failed, " + e);
+ transitionTo(mSetDnsForwardersErrorState);
+ }
+ }
- protected void handleNewUpstreamNetworkState(NetworkState ns) {
- mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
- mOffloadController.setUpstreamLinkProperties(
- (ns != null) ? ns.linkProperties : null);
+ protected void notifyDownstreamsOfNewUpstreamIface(String ifaceName) {
+ mLog.log("Notifying downstreams of upstream=" + ifaceName);
+ mCurrentUpstreamIface = ifaceName;
+ for (TetherInterfaceStateMachine sm : mNotifyList) {
+ sm.sendMessage(TetherInterfaceStateMachine.CMD_TETHER_CONNECTION_CHANGED,
+ ifaceName);
}
}
+ protected void handleNewUpstreamNetworkState(NetworkState ns) {
+ mIPv6TetheringCoordinator.updateUpstreamNetworkState(ns);
+ mOffloadController.setUpstreamLinkProperties((ns != null) ? ns.linkProperties : null);
+ }
+
private void handleInterfaceServingStateActive(int mode, TetherInterfaceStateMachine who) {
if (mNotifyList.indexOf(who) < 0) {
mNotifyList.add(who);
@@ -1379,7 +1403,61 @@
}
}
- class TetherModeAliveState extends TetherMasterUtilState {
+ private void handleUpstreamNetworkMonitorCallback(int arg1, Object o) {
+ if (arg1 == UpstreamNetworkMonitor.NOTIFY_EXEMPT_PREFIXES) {
+ mOffloadController.updateExemptPrefixes((Set<IpPrefix>) o);
+ return;
+ }
+
+ final NetworkState ns = (NetworkState) o;
+
+ if (ns == null || !pertainsToCurrentUpstream(ns)) {
+ // TODO: In future, this is where upstream evaluation and selection
+ // could be handled for notifications which include sufficient data.
+ // For example, after CONNECTIVITY_ACTION listening is removed, here
+ // is where we could observe a Wi-Fi network becoming available and
+ // passing validation.
+ if (mCurrentUpstreamIface == null) {
+ // If we have no upstream interface, try to run through upstream
+ // selection again. If, for example, IPv4 connectivity has shown up
+ // after IPv6 (e.g., 464xlat became available) we want the chance to
+ // notice and act accordingly.
+ chooseUpstreamType(false);
+ }
+ return;
+ }
+
+ switch (arg1) {
+ case UpstreamNetworkMonitor.EVENT_ON_AVAILABLE:
+ // The default network changed, or DUN connected
+ // before this callback was processed. Updates
+ // for the current NetworkCapabilities and
+ // LinkProperties have been requested (default
+ // request) or are being sent shortly (DUN). Do
+ // nothing until they arrive; if no updates
+ // arrive there's nothing to do.
+ break;
+ case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
+ handleNewUpstreamNetworkState(ns);
+ break;
+ case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
+ setDnsForwarders(ns.network, ns.linkProperties);
+ handleNewUpstreamNetworkState(ns);
+ break;
+ case UpstreamNetworkMonitor.EVENT_ON_LOST:
+ // TODO: Re-evaluate possible upstreams. Currently upstream
+ // reevaluation is triggered via received CONNECTIVITY_ACTION
+ // broadcasts that result in being passed a
+ // TetherMasterSM.CMD_UPSTREAM_CHANGED.
+ handleNewUpstreamNetworkState(null);
+ break;
+ default:
+ mLog.e("Unknown arg1 value: " + arg1);
+ break;
+ }
+ }
+
+ class TetherModeAliveState extends State {
boolean mUpstreamWanted = false;
boolean mTryCell = true;
@@ -1397,7 +1475,7 @@
// TODO: De-duplicate with updateUpstreamWanted() below.
if (upstreamWanted()) {
mUpstreamWanted = true;
- mOffloadController.start();
+ startOffloadController();
chooseUpstreamType(true);
mTryCell = false;
}
@@ -1408,7 +1486,7 @@
mOffloadController.stop();
mUpstreamNetworkMonitor.stop();
mSimChange.stopListening();
- notifyTetheredOfNewUpstreamIface(null);
+ notifyDownstreamsOfNewUpstreamIface(null);
handleNewUpstreamNetworkState(null);
}
@@ -1417,7 +1495,7 @@
mUpstreamWanted = upstreamWanted();
if (mUpstreamWanted != previousUpstreamWanted) {
if (mUpstreamWanted) {
- mOffloadController.start();
+ startOffloadController();
} else {
mOffloadController.stop();
}
@@ -1427,7 +1505,7 @@
@Override
public boolean processMessage(Message message) {
- maybeLogMessage(this, message.what);
+ logMessage(this, message.what);
boolean retValue = true;
switch (message.what) {
case EVENT_IFACE_SERVING_STATE_ACTIVE: {
@@ -1497,52 +1575,8 @@
break;
case EVENT_UPSTREAM_CALLBACK: {
updateUpstreamWanted();
- if (!mUpstreamWanted) break;
-
- final NetworkState ns = (NetworkState) message.obj;
-
- if (ns == null || !pertainsToCurrentUpstream(ns)) {
- // TODO: In future, this is where upstream evaluation and selection
- // could be handled for notifications which include sufficient data.
- // For example, after CONNECTIVITY_ACTION listening is removed, here
- // is where we could observe a Wi-Fi network becoming available and
- // passing validation.
- if (mCurrentUpstreamIface == null) {
- // If we have no upstream interface, try to run through upstream
- // selection again. If, for example, IPv4 connectivity has shown up
- // after IPv6 (e.g., 464xlat became available) we want the chance to
- // notice and act accordingly.
- chooseUpstreamType(false);
- }
- break;
- }
-
- switch (message.arg1) {
- case UpstreamNetworkMonitor.EVENT_ON_AVAILABLE:
- // The default network changed, or DUN connected
- // before this callback was processed. Updates
- // for the current NetworkCapabilities and
- // LinkProperties have been requested (default
- // request) or are being sent shortly (DUN). Do
- // nothing until they arrive; if no updates
- // arrive there's nothing to do.
- break;
- case UpstreamNetworkMonitor.EVENT_ON_CAPABILITIES:
- handleNewUpstreamNetworkState(ns);
- break;
- case UpstreamNetworkMonitor.EVENT_ON_LINKPROPERTIES:
- setDnsForwarders(ns.network, ns.linkProperties);
- handleNewUpstreamNetworkState(ns);
- break;
- case UpstreamNetworkMonitor.EVENT_ON_LOST:
- // TODO: Re-evaluate possible upstreams. Currently upstream
- // reevaluation is triggered via received CONNECTIVITY_ACTION
- // broadcasts that result in being passed a
- // TetherMasterSM.CMD_UPSTREAM_CHANGED.
- handleNewUpstreamNetworkState(null);
- break;
- default:
- break;
+ if (mUpstreamWanted) {
+ handleUpstreamNetworkMonitorCallback(message.arg1, message.obj);
}
break;
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 08deef8..20ec206 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -19,6 +19,7 @@
import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED;
import android.content.ContentResolver;
+import android.net.IpPrefix;
import android.net.LinkProperties;
import android.net.RouteInfo;
import android.net.util.SharedLog;
@@ -28,6 +29,7 @@
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.ArrayList;
+import java.util.Set;
/**
* A class to encapsulate the business logic of programming the tethering
@@ -45,6 +47,7 @@
private boolean mConfigInitialized;
private boolean mControlInitialized;
private LinkProperties mUpstreamLinkProperties;
+ private Set<IpPrefix> mExemptPrefixes;
public OffloadController(Handler h, OffloadHardwareInterface hwi,
ContentResolver contentResolver, SharedLog log) {
@@ -55,7 +58,12 @@
}
public void start() {
- if (isOffloadDisabled() || started()) return;
+ if (started()) return;
+
+ if (isOffloadDisabled()) {
+ mLog.i("tethering offload disabled");
+ return;
+ }
if (!mConfigInitialized) {
mConfigInitialized = mHwInterface.initOffloadConfig();
@@ -108,6 +116,17 @@
pushUpstreamParameters();
}
+ public void updateExemptPrefixes(Set<IpPrefix> exemptPrefixes) {
+ if (!started()) return;
+
+ mExemptPrefixes = exemptPrefixes;
+ // TODO:
+ // - add IP addresses from all downstream link properties
+ // - add routes from all non-tethering downstream link properties
+ // - remove any 64share prefixes
+ // - push this to the HAL
+ }
+
public void notifyDownstreamLinkProperties(LinkProperties lp) {
if (!started()) return;
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 1fc1684..09fd96b 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -76,6 +76,10 @@
}
}
+ final String logmsg = String.format("initOffloadControl(%s)",
+ (controlCb == null) ? "null"
+ : "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
+
mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback);
final CbResults results = new CbResults();
try {
@@ -86,11 +90,11 @@
results.errMsg = errMsg;
});
} catch (RemoteException e) {
- mLog.e("failed to initOffload: " + e);
+ record(logmsg, e);
return false;
}
- if (!results.success) mLog.e("initOffload failed: " + results.errMsg);
+ record(logmsg, results);
return results.success;
}
@@ -108,14 +112,18 @@
mOffloadControl = null;
mTetheringOffloadCallback = null;
mControlCallback = null;
+ mLog.log("stopOffloadControl()");
}
public boolean setUpstreamParameters(
String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
- iface = iface != null ? iface : NO_INTERFACE_NAME;
- v4addr = v4addr != null ? v4addr : NO_IPV4_ADDRESS;
- v4gateway = v4gateway != null ? v4gateway : NO_IPV4_GATEWAY;
- v6gws = v6gws != null ? v6gws : new ArrayList<>();
+ iface = (iface != null) ? iface : NO_INTERFACE_NAME;
+ v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS;
+ v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY;
+ v6gws = (v6gws != null) ? v6gws : new ArrayList<>();
+
+ final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
+ iface, v4addr, v4gateway, String.join(",", v6gws));
final CbResults results = new CbResults();
try {
@@ -126,14 +134,27 @@
results.errMsg = errMsg;
});
} catch (RemoteException e) {
- mLog.e("failed to setUpstreamParameters: " + e);
+ record(logmsg, e);
return false;
}
- if (!results.success) mLog.e("setUpstreamParameters failed: " + results.errMsg);
+ record(logmsg, results);
return results.success;
}
+ private void record(String msg, Throwable t) {
+ mLog.e(msg + " -> exception: " + t);
+ }
+
+ private void record(String msg, CbResults results) {
+ final String logmsg = msg + " -> " + results;
+ if (!results.success) {
+ mLog.e(logmsg);
+ } else {
+ mLog.log(logmsg);
+ }
+ }
+
private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
public final Handler handler;
public final ControlCallback controlCb;
@@ -162,5 +183,13 @@
private static class CbResults {
boolean success;
String errMsg;
+
+ public String toString() {
+ if (success) {
+ return "ok";
+ } else {
+ return "fail: " + errMsg;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
index 86b2551..4bac69c 100644
--- a/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
+++ b/services/core/java/com/android/server/connectivity/tethering/TetherInterfaceStateMachine.java
@@ -441,12 +441,8 @@
mLastRaParams = newParams;
}
- private void maybeLogMessage(State state, int what) {
- if (DBG) {
- Log.d(TAG, state.getName() + " got " +
- sMagicDecoderRing.get(what, Integer.toString(what)) + ", Iface = " +
- mIfaceName);
- }
+ private void logMessage(State state, int what) {
+ mLog.log(state.getName() + " got " + sMagicDecoderRing.get(what, Integer.toString(what)));
}
private void sendInterfaceState(int newInterfaceState) {
@@ -473,7 +469,7 @@
@Override
public boolean processMessage(Message message) {
- maybeLogMessage(this, message.what);
+ logMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLastError = ConnectivityManager.TETHER_ERROR_NO_ERROR;
@@ -545,7 +541,7 @@
@Override
public boolean processMessage(Message message) {
- maybeLogMessage(this, message.what);
+ logMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_UNREQUESTED:
transitionTo(mInitialState);
@@ -595,7 +591,7 @@
public boolean processMessage(Message message) {
if (super.processMessage(message)) return true;
- maybeLogMessage(this, message.what);
+ logMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLog.e("CMD_TETHER_REQUESTED while in local-only hotspot mode.");
@@ -667,7 +663,7 @@
public boolean processMessage(Message message) {
if (super.processMessage(message)) return true;
- maybeLogMessage(this, message.what);
+ logMessage(this, message.what);
switch (message.what) {
case CMD_TETHER_REQUESTED:
mLog.e("CMD_TETHER_REQUESTED while already tethering.");
diff --git a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
index 9ebfaf7..eb66767 100644
--- a/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java
@@ -26,18 +26,24 @@
import android.os.Looper;
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.NetworkState;
+import android.net.util.NetworkConstants;
import android.net.util.SharedLog;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.StateMachine;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
/**
@@ -66,10 +72,16 @@
private static final boolean DBG = false;
private static final boolean VDBG = false;
+ private static final IpPrefix[] MINIMUM_LOCAL_PREFIXES_SET = {
+ prefix("127.0.0.0/8"), prefix("169.254.0.0/16"),
+ prefix("::/3"), prefix("fe80::/64"), prefix("fc00::/7"), prefix("ff00::/8"),
+ };
+
public static final int EVENT_ON_AVAILABLE = 1;
public static final int EVENT_ON_CAPABILITIES = 2;
public static final int EVENT_ON_LINKPROPERTIES = 3;
public static final int EVENT_ON_LOST = 4;
+ public static final int NOTIFY_EXEMPT_PREFIXES = 10;
private static final int CALLBACK_LISTEN_ALL = 1;
private static final int CALLBACK_TRACK_DEFAULT = 2;
@@ -81,6 +93,7 @@
private final Handler mHandler;
private final int mWhat;
private final HashMap<Network, NetworkState> mNetworkMap = new HashMap<>();
+ private HashSet<IpPrefix> mOffloadExemptPrefixes;
private ConnectivityManager mCM;
private NetworkCallback mListenAllCallback;
private NetworkCallback mDefaultNetworkCallback;
@@ -88,18 +101,19 @@
private boolean mDunRequired;
private Network mCurrentDefault;
- public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, int what, SharedLog log) {
+ public UpstreamNetworkMonitor(Context ctx, StateMachine tgt, SharedLog log, int what) {
mContext = ctx;
mTarget = tgt;
mHandler = mTarget.getHandler();
- mWhat = what;
mLog = log.forSubComponent(TAG);
+ mWhat = what;
+ mOffloadExemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values());
}
@VisibleForTesting
public UpstreamNetworkMonitor(
- StateMachine tgt, int what, ConnectivityManager cm, SharedLog log) {
- this(null, tgt, what, log);
+ ConnectivityManager cm, StateMachine tgt, SharedLog log, int what) {
+ this((Context) null, tgt, log, what);
mCM = cm;
}
@@ -209,6 +223,10 @@
return typeStatePair.ns;
}
+ public Set<IpPrefix> getOffloadExemptPrefixes() {
+ return (Set<IpPrefix>) mOffloadExemptPrefixes.clone();
+ }
+
private void handleAvailable(int callbackType, Network network) {
if (VDBG) Log.d(TAG, "EVENT_ON_AVAILABLE for " + network);
@@ -342,6 +360,14 @@
notifyTarget(EVENT_ON_LOST, mNetworkMap.remove(network));
}
+ private void recomputeOffloadExemptPrefixes() {
+ final HashSet<IpPrefix> exemptPrefixes = allOffloadExemptPrefixes(mNetworkMap.values());
+ if (!mOffloadExemptPrefixes.equals(exemptPrefixes)) {
+ mOffloadExemptPrefixes = exemptPrefixes;
+ notifyTarget(NOTIFY_EXEMPT_PREFIXES, exemptPrefixes.clone());
+ }
+ }
+
// Fetch (and cache) a ConnectivityManager only if and when we need one.
private ConnectivityManager cm() {
if (mCM == null) {
@@ -376,6 +402,7 @@
@Override
public void onLinkPropertiesChanged(Network network, LinkProperties newLp) {
handleLinkProp(network, newLp);
+ recomputeOffloadExemptPrefixes();
}
// TODO: Handle onNetworkSuspended();
@@ -384,6 +411,7 @@
@Override
public void onLost(Network network) {
handleLost(mCallbackType, network);
+ recomputeOffloadExemptPrefixes();
}
}
@@ -395,16 +423,16 @@
notifyTarget(which, mNetworkMap.get(network));
}
- private void notifyTarget(int which, NetworkState netstate) {
- mTarget.sendMessage(mWhat, which, 0, netstate);
+ private void notifyTarget(int which, Object obj) {
+ mTarget.sendMessage(mWhat, which, 0, obj);
}
- static private class TypeStatePair {
+ private static class TypeStatePair {
public int type = TYPE_NONE;
public NetworkState ns = null;
}
- static private TypeStatePair findFirstAvailableUpstreamByType(
+ private static TypeStatePair findFirstAvailableUpstreamByType(
Iterable<NetworkState> netStates, Iterable<Integer> preferredTypes) {
final TypeStatePair result = new TypeStatePair();
@@ -431,4 +459,36 @@
return result;
}
+
+ private static HashSet<IpPrefix> allOffloadExemptPrefixes(Iterable<NetworkState> netStates) {
+ final HashSet<IpPrefix> prefixSet = new HashSet<>();
+
+ addDefaultLocalPrefixes(prefixSet);
+
+ for (NetworkState ns : netStates) {
+ addOffloadExemptPrefixes(prefixSet, ns.linkProperties);
+ }
+
+ return prefixSet;
+ }
+
+ private static void addDefaultLocalPrefixes(Set<IpPrefix> prefixSet) {
+ Collections.addAll(prefixSet, MINIMUM_LOCAL_PREFIXES_SET);
+ }
+
+ private static void addOffloadExemptPrefixes(Set<IpPrefix> prefixSet, LinkProperties lp) {
+ if (lp == null) return;
+
+ for (LinkAddress linkAddr : lp.getAllLinkAddresses()) {
+ prefixSet.add(new IpPrefix(linkAddr.getAddress(), linkAddr.getPrefixLength()));
+ }
+
+ // TODO: Consider adding other non-default routes associated with this
+ // network. Traffic to these destinations should perhaps not go through
+ // the Internet (upstream).
+ }
+
+ private static IpPrefix prefix(String prefixStr) {
+ return new IpPrefix(prefixStr);
+ }
}
diff --git a/services/core/java/com/android/server/notification/NotificationUsageStats.java b/services/core/java/com/android/server/notification/NotificationUsageStats.java
index 34c5283..4265741 100644
--- a/services/core/java/com/android/server/notification/NotificationUsageStats.java
+++ b/services/core/java/com/android/server/notification/NotificationUsageStats.java
@@ -40,6 +40,7 @@
import org.json.JSONObject;
import java.io.PrintWriter;
+import java.lang.Math;
import java.util.ArrayDeque;
import java.util.Calendar;
import java.util.GregorianCalendar;
@@ -718,8 +719,8 @@
}
void increment(int imp) {
- imp = imp < 0 ? 0 : imp > NUM_IMPORTANCES ? NUM_IMPORTANCES : imp;
- mCount[imp] ++;
+ imp = Math.max(0, Math.min(imp, mCount.length - 1));
+ mCount[imp]++;
}
void maybeCount(ImportanceHistogram prev) {
diff --git a/services/core/java/com/android/server/timezone/PackageStatusStorage.java b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
index 05e97c7..fe82dc4 100644
--- a/services/core/java/com/android/server/timezone/PackageStatusStorage.java
+++ b/services/core/java/com/android/server/timezone/PackageStatusStorage.java
@@ -32,6 +32,7 @@
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
+import java.io.PrintWriter;
import static com.android.server.timezone.PackageStatus.CHECK_COMPLETED_FAILURE;
import static com.android.server.timezone.PackageStatus.CHECK_COMPLETED_SUCCESS;
@@ -375,4 +376,8 @@
}
return value;
}
+
+ public void dump(PrintWriter printWriter) {
+ printWriter.println("Package status: " + getPackageStatus());
+ }
}
diff --git a/services/core/java/com/android/server/timezone/PackageTracker.java b/services/core/java/com/android/server/timezone/PackageTracker.java
index f9af2ea..e8dfd77 100644
--- a/services/core/java/com/android/server/timezone/PackageTracker.java
+++ b/services/core/java/com/android/server/timezone/PackageTracker.java
@@ -26,6 +26,7 @@
import android.util.Slog;
import java.io.File;
+import java.io.PrintWriter;
/**
* Monitors the installed applications associated with time zone updates. If the app packages are
@@ -510,4 +511,23 @@
Slog.wtf(TAG, message, cause);
throw new RuntimeException(message, cause);
}
+
+ public void dump(PrintWriter fout) {
+ fout.println("PackageTrackerState: " + toString());
+ mPackageStatusStorage.dump(fout);
+ }
+
+ @Override
+ public String toString() {
+ return "PackageTracker{" +
+ "mTrackingEnabled=" + mTrackingEnabled +
+ ", mUpdateAppPackageName='" + mUpdateAppPackageName + '\'' +
+ ", mDataAppPackageName='" + mDataAppPackageName + '\'' +
+ ", mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis +
+ ", mFailedCheckRetryCount=" + mFailedCheckRetryCount +
+ ", mLastTriggerTimestamp=" + mLastTriggerTimestamp +
+ ", mCheckTriggered=" + mCheckTriggered +
+ ", mCheckFailureCount=" + mCheckFailureCount +
+ '}';
+ }
}
diff --git a/services/core/java/com/android/server/timezone/PermissionHelper.java b/services/core/java/com/android/server/timezone/PermissionHelper.java
index ba91c7f..2ec31e2 100644
--- a/services/core/java/com/android/server/timezone/PermissionHelper.java
+++ b/services/core/java/com/android/server/timezone/PermissionHelper.java
@@ -16,10 +16,14 @@
package com.android.server.timezone;
+import java.io.PrintWriter;
+
/**
* An easy-to-mock interface around permission checks for use by {@link RulesManagerService}.
*/
public interface PermissionHelper {
void enforceCallerHasPermission(String requiredPermission) throws SecurityException;
+
+ boolean checkDumpPermission(String tag, PrintWriter printWriter);
}
diff --git a/services/core/java/com/android/server/timezone/RulesManagerService.java b/services/core/java/com/android/server/timezone/RulesManagerService.java
index 5724398..d97ba2d 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerService.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerService.java
@@ -37,12 +37,24 @@
import android.util.Slog;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.PrintWriter;
import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import libcore.icu.ICU;
+import libcore.util.ZoneInfoDB;
+
+import static android.app.timezone.RulesState.DISTRO_STATUS_INSTALLED;
+import static android.app.timezone.RulesState.DISTRO_STATUS_NONE;
+import static android.app.timezone.RulesState.DISTRO_STATUS_UNKNOWN;
+import static android.app.timezone.RulesState.STAGED_OPERATION_INSTALL;
+import static android.app.timezone.RulesState.STAGED_OPERATION_NONE;
+import static android.app.timezone.RulesState.STAGED_OPERATION_UNINSTALL;
+import static android.app.timezone.RulesState.STAGED_OPERATION_UNKNOWN;
// TODO(nfuller) Add EventLog calls where useful in the system server.
// TODO(nfuller) Check logging best practices in the system server.
@@ -113,6 +125,11 @@
public RulesState getRulesState() {
mPermissionHelper.enforceCallerHasPermission(REQUIRED_UPDATER_PERMISSION);
+ return getRulesStateInternal();
+ }
+
+ /** Like {@link #getRulesState()} without the permission check. */
+ private RulesState getRulesStateInternal() {
synchronized(this) {
String systemRulesVersion;
try {
@@ -126,18 +143,18 @@
// Determine the staged operation status, if possible.
DistroRulesVersion stagedDistroRulesVersion = null;
- int stagedOperationStatus = RulesState.STAGED_OPERATION_UNKNOWN;
+ int stagedOperationStatus = STAGED_OPERATION_UNKNOWN;
if (!operationInProgress) {
StagedDistroOperation stagedDistroOperation;
try {
stagedDistroOperation = mInstaller.getStagedDistroOperation();
if (stagedDistroOperation == null) {
- stagedOperationStatus = RulesState.STAGED_OPERATION_NONE;
+ stagedOperationStatus = STAGED_OPERATION_NONE;
} else if (stagedDistroOperation.isUninstall) {
- stagedOperationStatus = RulesState.STAGED_OPERATION_UNINSTALL;
+ stagedOperationStatus = STAGED_OPERATION_UNINSTALL;
} else {
// Must be an install.
- stagedOperationStatus = RulesState.STAGED_OPERATION_INSTALL;
+ stagedOperationStatus = STAGED_OPERATION_INSTALL;
DistroVersion stagedDistroVersion = stagedDistroOperation.distroVersion;
stagedDistroRulesVersion = new DistroRulesVersion(
stagedDistroVersion.rulesVersion,
@@ -150,16 +167,16 @@
// Determine the installed distro state, if possible.
DistroVersion installedDistroVersion;
- int distroStatus = RulesState.DISTRO_STATUS_UNKNOWN;
+ int distroStatus = DISTRO_STATUS_UNKNOWN;
DistroRulesVersion installedDistroRulesVersion = null;
if (!operationInProgress) {
try {
installedDistroVersion = mInstaller.getInstalledDistroVersion();
if (installedDistroVersion == null) {
- distroStatus = RulesState.DISTRO_STATUS_NONE;
+ distroStatus = DISTRO_STATUS_NONE;
installedDistroRulesVersion = null;
} else {
- distroStatus = RulesState.DISTRO_STATUS_INSTALLED;
+ distroStatus = DISTRO_STATUS_INSTALLED;
installedDistroRulesVersion = new DistroRulesVersion(
installedDistroVersion.rulesVersion,
installedDistroVersion.revision);
@@ -358,6 +375,86 @@
mPackageTracker.recordCheckResult(checkToken, success);
}
+ @Override
+ protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ if (!mPermissionHelper.checkDumpPermission(TAG, pw)) {
+ return;
+ }
+
+ RulesState rulesState = getRulesStateInternal();
+ if (args != null && args.length == 2) {
+ // Formatting options used for automated tests. The format is less free-form than
+ // the -format options, which are intended to be easier to parse.
+ if ("-format_state".equals(args[0]) && args[1] != null) {
+ for (char c : args[1].toCharArray()) {
+ switch (c) {
+ case 'p': // Report operation in progress
+ pw.println("Operation in progress: "
+ + rulesState.isOperationInProgress());
+ break;
+ case 's': // Report system image rules version
+ pw.println("System rules version: "
+ + rulesState.getSystemRulesVersion());
+ break;
+ case 'c': // Report current installation state
+ pw.println("Current install state: "
+ + distroStatusToString(rulesState.getDistroStatus()));
+ break;
+ case 'i': // Report currently installed version
+ DistroRulesVersion installedRulesVersion =
+ rulesState.getInstalledDistroRulesVersion();
+ pw.print("Installed rules version: ");
+ if (installedRulesVersion == null) {
+ pw.println("<None>");
+ } else {
+ pw.println(installedRulesVersion.toDumpString());
+ }
+ break;
+ case 'o': // Report staged operation type
+ int stagedOperationType = rulesState.getStagedOperationType();
+ pw.println("Staged operation: "
+ + stagedOperationToString(stagedOperationType));
+ break;
+ case 't':
+ // Report staged version (i.e. the one that will be installed next boot
+ // if the staged operation is an install).
+ pw.print("Staged rules version: ");
+ DistroRulesVersion stagedDistroRulesVersion =
+ rulesState.getStagedDistroRulesVersion();
+ if (stagedDistroRulesVersion == null) {
+ pw.println("<None>");
+ } else {
+ pw.println(stagedDistroRulesVersion.toDumpString());
+ }
+ break;
+ case 'a':
+ // Report the active rules version (i.e. the rules in use by the current
+ // process).
+ pw.println("Active rules version (ICU, libcore): "
+ + ICU.getTZDataVersion() + ","
+ + ZoneInfoDB.getInstance().getVersion());
+ break;
+ default:
+ pw.println("Unknown option: " + c);
+ }
+ }
+ return;
+ }
+ }
+
+ pw.println("RulesManagerService state: " + toString());
+ pw.println("Active rules version (ICU, libcore): " + ICU.getTZDataVersion() + ","
+ + ZoneInfoDB.getInstance().getVersion());
+ mPackageTracker.dump(pw);
+ }
+
+ @Override
+ public String toString() {
+ return "RulesManagerService{" +
+ "mOperationInProgress=" + mOperationInProgress +
+ '}';
+ }
+
private static CheckToken createCheckTokenOrThrow(byte[] checkTokenBytes) {
CheckToken checkToken;
try {
@@ -368,4 +465,30 @@
}
return checkToken;
}
+
+ private static String distroStatusToString(int distroStatus) {
+ switch(distroStatus) {
+ case DISTRO_STATUS_NONE:
+ return "None";
+ case DISTRO_STATUS_INSTALLED:
+ return "Installed";
+ case DISTRO_STATUS_UNKNOWN:
+ default:
+ return "Unknown";
+ }
+ }
+
+ private static String stagedOperationToString(int stagedOperationType) {
+ switch(stagedOperationType) {
+ case STAGED_OPERATION_NONE:
+ return "None";
+ case STAGED_OPERATION_UNINSTALL:
+ return "Uninstall";
+ case STAGED_OPERATION_INSTALL:
+ return "Install";
+ case STAGED_OPERATION_UNKNOWN:
+ default:
+ return "Unknown";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
index 482d8e2..767f0e0 100644
--- a/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
+++ b/services/core/java/com/android/server/timezone/RulesManagerServiceHelperImpl.java
@@ -17,10 +17,13 @@
package com.android.server.timezone;
import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
import android.os.ParcelFileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.concurrent.Executor;
import libcore.io.Streams;
@@ -40,6 +43,19 @@
mContext.enforceCallingPermission(requiredPermission, null /* message */);
}
+ @Override
+ public boolean checkDumpPermission(String tag, PrintWriter pw) {
+ // TODO(nfuller): Switch to DumpUtils.checkDumpPermission() when it is available in AOSP.
+ if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
+ != PackageManager.PERMISSION_GRANTED) {
+ pw.println("Permission Denial: can't dump LocationManagerService from from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ return false;
+ }
+ return true;
+ }
+
// TODO Wake lock required?
@Override
public void execute(Runnable runnable) {
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 9e2d696..b2930a4 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -35,6 +35,7 @@
import android.net.metrics.IpConnectivityLog;
import android.net.metrics.IpManagerEvent;
import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.SharedLog;
import android.os.INetworkManagementService;
import android.os.Message;
import android.os.RemoteException;
@@ -187,7 +188,7 @@
}
private void log(String msg) {
- mLocalLog.log(PREFIX + msg);
+ mLog.log(PREFIX + msg);
}
@Override
@@ -414,7 +415,7 @@
private final WakeupMessage mProvisioningTimeoutAlarm;
private final WakeupMessage mDhcpActionTimeoutAlarm;
private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
- private final LocalLog mLocalLog;
+ private final SharedLog mLog;
private final LocalLog mConnectivityPacketLog;
private final MessageHandlingLogger mMsgStateLogger;
private final IpConnectivityLog mMetricsLog = new IpConnectivityLog();
@@ -455,7 +456,7 @@
mCallback = new LoggingCallbackWrapper(callback);
mNwService = nwService;
- mLocalLog = new LocalLog(MAX_LOG_RECORDS);
+ mLog = new SharedLog(MAX_LOG_RECORDS, mTag);
mConnectivityPacketLog = new LocalLog(MAX_PACKET_RECORDS);
mMsgStateLogger = new MessageHandlingLogger();
@@ -500,7 +501,7 @@
private void logMsg(String msg) {
Log.d(mTag, msg);
- getHandler().post(() -> { mLocalLog.log("OBSERVED " + msg); });
+ getHandler().post(() -> { mLog.log("OBSERVED " + msg); });
}
};
@@ -508,7 +509,7 @@
mLinkProperties.setInterfaceName(mInterfaceName);
mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(mContext, getHandler(),
- () -> { mLocalLog.log("OBSERVED AvoidBadWifi changed"); });
+ () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
mProvisioningTimeoutAlarm = new WakeupMessage(mContext, getHandler(),
mTag + ".EVENT_PROVISIONING_TIMEOUT", EVENT_PROVISIONING_TIMEOUT);
@@ -658,7 +659,7 @@
pw.println();
pw.println(mTag + " StateMachine dump:");
pw.increaseIndent();
- mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
+ mLog.dump(fd, pw, args);
pw.decreaseIndent();
pw.println();
@@ -693,7 +694,7 @@
msg.arg1, msg.arg2, Objects.toString(msg.obj), mMsgStateLogger);
final String richerLogLine = getWhatToString(msg.what) + " " + logLine;
- mLocalLog.log(richerLogLine);
+ mLog.log(richerLogLine);
if (VDBG) {
Log.d(mTag, richerLogLine);
}
@@ -717,7 +718,7 @@
private void logError(String fmt, Object... args) {
final String msg = "ERROR " + String.format(fmt, args);
Log.e(mTag, msg);
- mLocalLog.log(msg);
+ mLog.log(msg);
}
private void getNetworkInterface() {
@@ -1063,7 +1064,7 @@
mNwService.setIPv6AddrGenMode(mInterfaceName, mConfiguration.mIPv6AddrGenMode);
} catch (ServiceSpecificException e) {
if (e.errorCode != OsConstants.EOPNOTSUPP) {
- throw e;
+ logError("Unable to set IPv6 addrgen mode: %s", e);
}
}
}
@@ -1088,6 +1089,7 @@
mIpReachabilityMonitor = new IpReachabilityMonitor(
mContext,
mInterfaceName,
+ mLog,
new IpReachabilityMonitor.Callback() {
@Override
public void notifyLost(InetAddress ip, String logMsg) {
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index a004dbb..846af17 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -35,6 +35,7 @@
import android.net.netlink.StructNdMsg;
import android.net.netlink.StructNlMsgHdr;
import android.net.util.MultinetworkPolicyTracker;
+import android.net.util.SharedLog;
import android.os.PowerManager;
import android.os.SystemClock;
import android.system.ErrnoException;
@@ -150,6 +151,7 @@
private final PowerManager.WakeLock mWakeLock;
private final String mInterfaceName;
private final int mInterfaceIndex;
+ private final SharedLog mLog;
private final Callback mCallback;
private final MultinetworkPolicyTracker mMultinetworkPolicyTracker;
private final NetlinkSocketObserver mNetlinkSocketObserver;
@@ -222,11 +224,11 @@
return errno;
}
- public IpReachabilityMonitor(Context context, String ifName, Callback callback) {
- this(context, ifName, callback, null);
+ public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback) {
+ this(context, ifName, log, callback, null);
}
- public IpReachabilityMonitor(Context context, String ifName, Callback callback,
+ public IpReachabilityMonitor(Context context, String ifName, SharedLog log, Callback callback,
MultinetworkPolicyTracker tracker) throws IllegalArgumentException {
mInterfaceName = ifName;
int ifIndex = -1;
@@ -238,6 +240,7 @@
}
mWakeLock = ((PowerManager) context.getSystemService(Context.POWER_SERVICE)).newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, TAG + "." + mInterfaceName);
+ mLog = log.forSubComponent(TAG);
mCallback = callback;
mMultinetworkPolicyTracker = tracker;
mNetlinkSocketObserver = new NetlinkSocketObserver();
@@ -410,6 +413,8 @@
break;
}
final int returnValue = probeNeighbor(mInterfaceIndex, target);
+ mLog.log(String.format("put neighbor %s into NUD_PROBE state (rval=%d)",
+ target.getHostAddress(), returnValue));
logEvent(IpReachabilityEvent.PROBE, returnValue);
}
mLastProbeTimeMs = SystemClock.elapsedRealtime();
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java
index dd56072..b57cac0 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageStatusStorageTest.java
@@ -25,6 +25,8 @@
import android.support.test.filters.SmallTest;
import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
import static junit.framework.Assert.assertTrue;
import static org.junit.Assert.assertEquals;
@@ -228,4 +230,27 @@
assertFalse(writeOk2);
assertEquals(expectedPackageStatus, mPackageStatusStorage.getPackageStatus());
}
+
+ @Test
+ public void dump() {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ // Dump initial state.
+ mPackageStatusStorage.dump(printWriter);
+
+ // No crash and it does something.
+ assertFalse(stringWriter.toString().isEmpty());
+
+ // Reset
+ stringWriter.getBuffer().setLength(0);
+ assertTrue(stringWriter.toString().isEmpty());
+
+ // Store something.
+ mPackageStatusStorage.generateCheckToken(VALID_PACKAGE_VERSIONS);
+
+ mPackageStatusStorage.dump(printWriter);
+
+ assertFalse(stringWriter.toString().isEmpty());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
index 4c7680b..a972e4f 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/PackageTrackerTest.java
@@ -30,6 +30,9 @@
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.SmallTest;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -1174,6 +1177,16 @@
assertFalse(token1.equals(token2));
}
+ @Test
+ public void dump() {
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ mPackageTracker.dump(printWriter);
+
+ assertFalse(stringWriter.toString().isEmpty());
+ }
+
private void simulatePackageInstallation(PackageVersions packageVersions) throws Exception {
configureApplicationsValidManifests(packageVersions);
diff --git a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
index 1407e26..2887e3b 100644
--- a/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/timezone/RulesManagerServiceTest.java
@@ -32,11 +32,15 @@
import android.os.ParcelFileDescriptor;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.PrintWriter;
import java.util.concurrent.Executor;
import javax.annotation.Nullable;
+import libcore.io.IoUtils;
+
import static com.android.server.timezone.RulesManagerService.REQUIRED_UPDATER_PERMISSION;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -52,6 +56,7 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
/**
@@ -724,6 +729,97 @@
verifyPackageTrackerCalled(null /* token */, true /* success */);
}
+ @Test
+ public void dump_noPermission() throws Exception {
+ when(mMockPermissionHelper.checkDumpPermission(any(String.class), any(PrintWriter.class)))
+ .thenReturn(false);
+
+ doDumpCallAndCapture(mRulesManagerService, null);
+ verifyZeroInteractions(mMockPackageTracker, mMockTimeZoneDistroInstaller);
+ }
+
+ @Test
+ public void dump_emptyArgs() throws Exception {
+ doSuccessfulDumpCall(mRulesManagerService, new String[0]);
+
+ // Verify the package tracker was consulted.
+ verify(mMockPackageTracker).dump(any(PrintWriter.class));
+ }
+
+ @Test
+ public void dump_nullArgs() throws Exception {
+ doSuccessfulDumpCall(mRulesManagerService, null);
+ // Verify the package tracker was consulted.
+ verify(mMockPackageTracker).dump(any(PrintWriter.class));
+ }
+
+ @Test
+ public void dump_unknownArgs() throws Exception {
+ String dumpedTextUnknownArgs = doSuccessfulDumpCall(
+ mRulesManagerService, new String[] { "foo", "bar"});
+
+ // Verify the package tracker was consulted.
+ verify(mMockPackageTracker).dump(any(PrintWriter.class));
+
+ String dumpedTextZeroArgs = doSuccessfulDumpCall(mRulesManagerService, null);
+ assertEquals(dumpedTextZeroArgs, dumpedTextUnknownArgs);
+ }
+
+ @Test
+ public void dump_formatState() throws Exception {
+ // Just expect these to not throw exceptions, not return nothing, and not interact with the
+ // package tracker.
+ doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("p"));
+ doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("s"));
+ doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("c"));
+ doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("i"));
+ doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("o"));
+ doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("t"));
+ doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("a"));
+ doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("z" /* Unknown */));
+ doSuccessfulDumpCall(mRulesManagerService, dumpFormatArgs("piscotz"));
+
+ verifyZeroInteractions(mMockPackageTracker);
+ }
+
+ private static String[] dumpFormatArgs(String argsString) {
+ return new String[] { "-format_state", argsString};
+ }
+
+ private String doSuccessfulDumpCall(RulesManagerService rulesManagerService, String[] args)
+ throws Exception {
+ when(mMockPermissionHelper.checkDumpPermission(any(String.class), any(PrintWriter.class)))
+ .thenReturn(true);
+
+ // Set up the mocks to return (arbitrary) information about the current device state.
+ when(mMockTimeZoneDistroInstaller.getSystemRulesVersion()).thenReturn("2017a");
+ when(mMockTimeZoneDistroInstaller.getInstalledDistroVersion()).thenReturn(
+ new DistroVersion(2, 3, "2017b", 4));
+ when(mMockTimeZoneDistroInstaller.getStagedDistroOperation()).thenReturn(
+ StagedDistroOperation.install(new DistroVersion(5, 6, "2017c", 7)));
+
+ // Do the dump call.
+ String dumpedOutput = doDumpCallAndCapture(rulesManagerService, args);
+
+ assertFalse(dumpedOutput.isEmpty());
+
+ return dumpedOutput;
+ }
+
+ private static String doDumpCallAndCapture(
+ RulesManagerService rulesManagerService, String[] args) throws IOException {
+ File file = File.createTempFile("dump", null);
+ try {
+ try (FileOutputStream fos = new FileOutputStream(file)) {
+ FileDescriptor fd = fos.getFD();
+ rulesManagerService.dump(fd, args);
+ }
+ return IoUtils.readFileAsString(file.getAbsolutePath());
+ } finally {
+ file.delete();
+ }
+ }
+
private void verifyNoPackageTrackerCallsMade() {
verifyNoMoreInteractions(mMockPackageTracker);
reset(mMockPackageTracker);
diff --git a/telephony/java/android/telephony/MbmsDownloadManager.java b/telephony/java/android/telephony/MbmsDownloadManager.java
index b3f46c2..231f2c8 100644
--- a/telephony/java/android/telephony/MbmsDownloadManager.java
+++ b/telephony/java/android/telephony/MbmsDownloadManager.java
@@ -93,19 +93,21 @@
/**
* Integer extra indicating the result code of the download. One of
* {@link #RESULT_SUCCESSFUL}, {@link #RESULT_EXPIRED}, or {@link #RESULT_CANCELLED}.
+ * TODO: Not systemapi.
*/
public static final String EXTRA_RESULT = "android.telephony.mbms.extra.RESULT";
/**
* Extra containing the {@link android.telephony.mbms.FileInfo} for which the download result
* is for. Must not be null.
- * TODO: future systemapi (here and and all extras) except the two for the app intent
+ * TODO: Not systemapi.
*/
public static final String EXTRA_FILE_INFO = "android.telephony.mbms.extra.FILE_INFO";
/**
* Extra containing the {@link DownloadRequest} for which the download result or file
* descriptor request is for. Must not be null.
+ * TODO: future systemapi (here and and all extras) except the three for the app intent
*/
public static final String EXTRA_REQUEST = "android.telephony.mbms.extra.REQUEST";
@@ -180,6 +182,7 @@
* {@link android.telephony.mbms.DownloadRequest.Builder#setAppIntent(Intent)}.
* Will always be set to a non-null value if {@link #EXTRA_RESULT} is set to
* {@link #RESULT_SUCCESSFUL}.
+ * TODO: Not systemapi.
*/
public static final String EXTRA_COMPLETED_FILE_URI =
"android.telephony.mbms.extra.COMPLETED_FILE_URI";
@@ -554,7 +557,7 @@
private File getDownloadRequestTokenPath(DownloadRequest request) {
File tempFileLocation = MbmsUtils.getEmbmsTempFileDirForService(mContext,
- request.getFileServiceInfo());
+ request.getFileServiceId());
String downloadTokenFileName = request.getHash()
+ MbmsDownloadReceiver.DOWNLOAD_TOKEN_SUFFIX;
return new File(tempFileLocation, downloadTokenFileName);
diff --git a/telephony/java/android/telephony/MbmsStreamingManager.java b/telephony/java/android/telephony/MbmsStreamingManager.java
index fb3d5025..8cc447e 100644
--- a/telephony/java/android/telephony/MbmsStreamingManager.java
+++ b/telephony/java/android/telephony/MbmsStreamingManager.java
@@ -142,7 +142,8 @@
* Starts streaming a requested service, reporting status to the indicated listener.
* Returns an object used to control that stream. The stream may not be ready for consumption
* immediately upon return from this method -- wait until the streaming state has been
- * reported via {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int)}
+ * reported via
+ * {@link android.telephony.mbms.StreamingServiceCallback#streamStateUpdated(int, int)}
*
* May throw an {@link MbmsException} containing any of the following error codes:
* {@link MbmsException#ERROR_MIDDLEWARE_NOT_BOUND}
diff --git a/telephony/java/android/telephony/mbms/DownloadRequest.java b/telephony/java/android/telephony/mbms/DownloadRequest.java
index 345729d..01e0bbd 100644
--- a/telephony/java/android/telephony/mbms/DownloadRequest.java
+++ b/telephony/java/android/telephony/mbms/DownloadRequest.java
@@ -21,7 +21,14 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Base64;
+import android.util.Log;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@@ -35,24 +42,52 @@
public class DownloadRequest implements Parcelable {
// Version code used to keep token calculation consistent.
private static final int CURRENT_VERSION = 1;
+ private static final String LOG_TAG = "MbmsDownloadRequest";
+
+ /**
+ * Maximum permissible length for the app's download-completion intent, when serialized via
+ * {@link Intent#toUri(int)}.
+ */
+ public static final int MAX_APP_INTENT_SIZE = 50000;
+
+ /**
+ * Maximum permissible length for the app's destination path, when serialized via
+ * {@link Uri#toString()}.
+ */
+ public static final int MAX_DESTINATION_URI_SIZE = 50000;
/** @hide */
+ private static class OpaqueDataContainer implements Serializable {
+ private final String destinationUri;
+ private final String appIntent;
+ private final int version;
+
+ public OpaqueDataContainer(String destinationUri, String appIntent, int version) {
+ this.destinationUri = destinationUri;
+ this.appIntent = appIntent;
+ this.version = version;
+ }
+ }
+
public static class Builder {
- private int id;
- private FileServiceInfo serviceInfo;
+ private String fileServiceId;
private Uri source;
private Uri dest;
private int subscriptionId;
private String appIntent;
private int version = CURRENT_VERSION;
- public Builder setId(int id) {
- this.id = id;
+ public Builder setServiceInfo(FileServiceInfo serviceInfo) {
+ fileServiceId = serviceInfo.getServiceId();
return this;
}
- public Builder setServiceInfo(FileServiceInfo serviceInfo) {
- this.serviceInfo = serviceInfo;
+ /**
+ * @hide
+ * TODO: systemapi
+ */
+ public Builder setServiceId(String serviceId) {
+ fileServiceId = serviceId;
return this;
}
@@ -62,6 +97,10 @@
}
public Builder setDest(Uri dest) {
+ if (dest.toString().length() > MAX_DESTINATION_URI_SIZE) {
+ throw new IllegalArgumentException("Destination uri must not exceed length " +
+ MAX_DESTINATION_URI_SIZE);
+ }
this.dest = dest;
return this;
}
@@ -73,33 +112,53 @@
public Builder setAppIntent(Intent intent) {
this.appIntent = intent.toUri(0);
+ if (this.appIntent.length() > MAX_APP_INTENT_SIZE) {
+ throw new IllegalArgumentException("App intent must not exceed length " +
+ MAX_APP_INTENT_SIZE);
+ }
return this;
}
- public Builder setVersion(int version) {
- this.version = version;
+ /**
+ * For use by middleware only
+ * TODO: systemapi
+ * @hide
+ */
+ public Builder setOpaqueData(byte[] data) {
+ try {
+ ObjectInputStream stream = new ObjectInputStream(new ByteArrayInputStream(data));
+ OpaqueDataContainer dataContainer = (OpaqueDataContainer) stream.readObject();
+ version = dataContainer.version;
+ appIntent = dataContainer.appIntent;
+ dest = Uri.parse(dataContainer.destinationUri);
+ } catch (IOException e) {
+ // Really should never happen
+ Log.e(LOG_TAG, "Got IOException trying to parse opaque data");
+ throw new IllegalArgumentException(e);
+ } catch (ClassNotFoundException e) {
+ Log.e(LOG_TAG, "Got ClassNotFoundException trying to parse opaque data");
+ throw new IllegalArgumentException(e);
+ }
return this;
}
public DownloadRequest build() {
- return new DownloadRequest(id, serviceInfo, source, dest,
+ return new DownloadRequest(fileServiceId, source, dest,
subscriptionId, appIntent, version);
}
}
- private final int downloadId;
- private final FileServiceInfo fileServiceInfo;
+ private final String fileServiceId;
private final Uri sourceUri;
private final Uri destinationUri;
private final int subscriptionId;
private final String serializedResultIntentForApp;
private final int version;
- private DownloadRequest(int id, FileServiceInfo serviceInfo,
+ private DownloadRequest(String fileServiceId,
Uri source, Uri dest,
int sub, String appIntent, int version) {
- downloadId = id;
- fileServiceInfo = serviceInfo;
+ this.fileServiceId = fileServiceId;
sourceUri = source;
destinationUri = dest;
subscriptionId = sub;
@@ -112,8 +171,7 @@
}
private DownloadRequest(DownloadRequest dr) {
- downloadId = dr.downloadId;
- fileServiceInfo = dr.fileServiceInfo;
+ fileServiceId = dr.fileServiceId;
sourceUri = dr.sourceUri;
destinationUri = dr.destinationUri;
subscriptionId = dr.subscriptionId;
@@ -122,8 +180,7 @@
}
private DownloadRequest(Parcel in) {
- downloadId = in.readInt();
- fileServiceInfo = in.readParcelable(getClass().getClassLoader());
+ fileServiceId = in.readString();
sourceUri = in.readParcelable(getClass().getClassLoader());
destinationUri = in.readParcelable(getClass().getClassLoader());
subscriptionId = in.readInt();
@@ -136,8 +193,7 @@
}
public void writeToParcel(Parcel out, int flags) {
- out.writeInt(downloadId);
- out.writeParcelable(fileServiceInfo, flags);
+ out.writeString(fileServiceId);
out.writeParcelable(sourceUri, flags);
out.writeParcelable(destinationUri, flags);
out.writeInt(subscriptionId);
@@ -145,12 +201,8 @@
out.writeInt(version);
}
- public int getDownloadId() {
- return downloadId;
- }
-
- public FileServiceInfo getFileServiceInfo() {
- return fileServiceInfo;
+ public String getFileServiceId() {
+ return fileServiceId;
}
public Uri getSourceUri() {
@@ -173,6 +225,27 @@
}
}
+ /**
+ * @hide
+ * TODO: systemapi
+ */
+ public byte[] getOpaqueData() {
+ try {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
+ ObjectOutputStream stream = new ObjectOutputStream(byteArrayOutputStream);
+ OpaqueDataContainer container = new OpaqueDataContainer(
+ destinationUri.toString(), serializedResultIntentForApp, version);
+ stream.writeObject(container);
+ stream.flush();
+ return byteArrayOutputStream.toByteArray();
+ } catch (IOException e) {
+ // Really should never happen
+ Log.e(LOG_TAG, "Got IOException trying to serialize opaque data");
+ return null;
+ }
+ }
+
+ /** @hide */
public int getVersion() {
return version;
}
@@ -228,10 +301,9 @@
return false;
}
DownloadRequest request = (DownloadRequest) o;
- return downloadId == request.downloadId &&
- subscriptionId == request.subscriptionId &&
+ return subscriptionId == request.subscriptionId &&
version == request.version &&
- Objects.equals(fileServiceInfo, request.fileServiceInfo) &&
+ Objects.equals(fileServiceId, request.fileServiceId) &&
Objects.equals(sourceUri, request.sourceUri) &&
Objects.equals(destinationUri, request.destinationUri) &&
Objects.equals(serializedResultIntentForApp, request.serializedResultIntentForApp);
@@ -239,7 +311,7 @@
@Override
public int hashCode() {
- return Objects.hash(downloadId, fileServiceInfo, sourceUri, destinationUri,
+ return Objects.hash(fileServiceId, sourceUri, destinationUri,
subscriptionId, serializedResultIntentForApp, version);
}
}
diff --git a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
index 94c80f4..0952fbe 100755
--- a/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
+++ b/telephony/java/android/telephony/mbms/IStreamingServiceCallback.aidl
@@ -21,7 +21,7 @@
*/
oneway interface IStreamingServiceCallback {
void error(int errorCode, String message);
- void streamStateUpdated(int state);
+ void streamStateUpdated(int state, int reason);
void mediaDescriptionUpdated();
void broadcastSignalStrengthUpdated(int signalStrength);
void streamMethodUpdated(int methodType);
diff --git a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
index 5bc53a8..3617165 100644
--- a/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
+++ b/telephony/java/android/telephony/mbms/MbmsDownloadReceiver.java
@@ -83,7 +83,7 @@
public static final int RESULT_DOWNLOAD_FINALIZATION_ERROR = 4;
/**
- * Indicates that the manager weas unable to generate one or more of the requested file
+ * Indicates that the manager was unable to generate one or more of the requested file
* descriptors.
* This is a non-fatal result code -- some file descriptors may still be generated, but there
* is no guarantee that they will be the same number as requested.
@@ -149,7 +149,7 @@
DownloadRequest request = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_REQUEST);
String expectedTokenFileName = request.getHash() + DOWNLOAD_TOKEN_SUFFIX;
File expectedTokenFile = new File(
- MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceInfo()),
+ MbmsUtils.getEmbmsTempFileDirForService(context, request.getFileServiceId()),
expectedTokenFileName);
if (!expectedTokenFile.exists()) {
Log.w(LOG_TAG, "Supplied download request does not match a token that we have. " +
@@ -201,22 +201,24 @@
Uri destinationUri = request.getDestinationUri();
Uri finalTempFile = intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FINAL_URI);
- if (!verifyTempFilePath(context, request.getFileServiceInfo(), finalTempFile)) {
+ if (!verifyTempFilePath(context, request.getFileServiceId(), finalTempFile)) {
Log.w(LOG_TAG, "Download result specified an invalid temp file " + finalTempFile);
setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
return;
}
- String relativePath = calculateDestinationFileRelativePath(request,
- (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO));
+ FileInfo completedFileInfo =
+ (FileInfo) intent.getParcelableExtra(MbmsDownloadManager.EXTRA_FILE_INFO);
+ String relativePath = calculateDestinationFileRelativePath(request, completedFileInfo);
Uri finalFileLocation = moveTempFile(finalTempFile, destinationUri, relativePath);
if (finalFileLocation == null) {
Log.w(LOG_TAG, "Failed to move temp file to final destination");
- // TODO: how do we notify the app of this?
setResultCode(RESULT_DOWNLOAD_FINALIZATION_ERROR);
+ return;
}
intentForApp.putExtra(MbmsDownloadManager.EXTRA_COMPLETED_FILE_URI, finalFileLocation);
+ intentForApp.putExtra(MbmsDownloadManager.EXTRA_FILE_INFO, completedFileInfo);
context.sendBroadcast(intentForApp);
setResultCode(RESULT_OK);
@@ -235,7 +237,7 @@
}
for (Uri tempFileUri : tempFiles) {
- if (verifyTempFilePath(context, request.getFileServiceInfo(), tempFileUri)) {
+ if (verifyTempFilePath(context, request.getFileServiceId(), tempFileUri)) {
File tempFile = new File(tempFileUri.getSchemeSpecificPart());
tempFile.delete();
}
@@ -276,7 +278,8 @@
private ArrayList<UriPathPair> generateFreshTempFiles(Context context,
FileServiceInfo serviceInfo,
int freshFdCount) {
- File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceInfo);
+ File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
+ serviceInfo.getServiceId());
if (!tempFileDir.exists()) {
tempFileDir.mkdirs();
}
@@ -327,7 +330,7 @@
ArrayList<UriPathPair> result = new ArrayList<>(pausedFiles.size());
for (Uri fileUri : pausedFiles) {
- if (!verifyTempFilePath(context, serviceInfo, fileUri)) {
+ if (!verifyTempFilePath(context, serviceInfo.getServiceId(), fileUri)) {
Log.w(LOG_TAG, "Supplied file " + fileUri + " is not a valid temp file to resume");
setResultCode(RESULT_TEMP_FILE_GENERATION_ERROR);
continue;
@@ -351,7 +354,8 @@
private void cleanupTempFiles(Context context, Intent intent) {
FileServiceInfo serviceInfo =
intent.getParcelableExtra(MbmsDownloadManager.EXTRA_SERVICE_INFO);
- File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context, serviceInfo);
+ File tempFileDir = MbmsUtils.getEmbmsTempFileDirForService(context,
+ serviceInfo.getServiceId());
List<Uri> filesInUse =
intent.getParcelableArrayListExtra(MbmsDownloadManager.EXTRA_TEMP_FILES_IN_USE);
File[] filesToDelete = tempFileDir.listFiles(new FileFilter() {
@@ -439,7 +443,7 @@
return null;
}
- private static boolean verifyTempFilePath(Context context, FileServiceInfo serviceInfo,
+ private static boolean verifyTempFilePath(Context context, String serviceId,
Uri filePath) {
if (!ContentResolver.SCHEME_FILE.equals(filePath.getScheme())) {
Log.w(LOG_TAG, "Uri " + filePath + " does not have a file scheme");
@@ -454,7 +458,7 @@
}
if (!MbmsUtils.isContainedIn(
- MbmsUtils.getEmbmsTempFileDirForService(context, serviceInfo), tempFile)) {
+ MbmsUtils.getEmbmsTempFileDirForService(context, serviceId), tempFile)) {
return false;
}
diff --git a/telephony/java/android/telephony/mbms/MbmsUtils.java b/telephony/java/android/telephony/mbms/MbmsUtils.java
index b28950e..1e03fb9 100644
--- a/telephony/java/android/telephony/mbms/MbmsUtils.java
+++ b/telephony/java/android/telephony/mbms/MbmsUtils.java
@@ -89,10 +89,9 @@
/**
* Returns a File linked to the directory used to store temp files for this file service
*/
- public static File getEmbmsTempFileDirForService(Context context, FileServiceInfo serviceInfo) {
+ public static File getEmbmsTempFileDirForService(Context context, String serviceId) {
File embmsTempFileDir = MbmsTempFileProvider.getEmbmsTempFileDir(context);
- String tempFileDirName = String.valueOf(serviceInfo.getServiceId());
- return new File(embmsTempFileDir, tempFileDirName);
+ return new File(embmsTempFileDir, serviceId);
}
}
diff --git a/telephony/java/android/telephony/mbms/StreamingService.java b/telephony/java/android/telephony/mbms/StreamingService.java
index 85731a1..475c93a 100644
--- a/telephony/java/android/telephony/mbms/StreamingService.java
+++ b/telephony/java/android/telephony/mbms/StreamingService.java
@@ -16,12 +16,16 @@
package android.telephony.mbms;
+import android.annotation.IntDef;
import android.net.Uri;
import android.os.DeadObjectException;
import android.os.RemoteException;
import android.telephony.mbms.vendor.IMbmsStreamingService;
import android.util.Log;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
/**
* @hide
*/
@@ -30,12 +34,31 @@
/**
* The state of a stream, reported via {@link StreamingServiceCallback#streamStateUpdated}
+ * @hide
*/
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STATE_STOPPED, STATE_STARTED, STATE_STALLED})
+ public @interface StreamingState {}
public final static int STATE_STOPPED = 1;
public final static int STATE_STARTED = 2;
public final static int STATE_STALLED = 3;
/**
+ * The reason for a stream state change, reported via
+ * {@link StreamingServiceCallback#streamStateUpdated}
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({REASON_BY_USER_REQUEST, REASON_END_OF_SESSION, REASON_FREQUENCY_CONFLICT,
+ REASON_OUT_OF_MEMORY, REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE})
+ public @interface StreamingStateChangeReason {}
+ public static final int REASON_BY_USER_REQUEST = 1;
+ public static final int REASON_END_OF_SESSION = 2;
+ public static final int REASON_FREQUENCY_CONFLICT = 3;
+ public static final int REASON_OUT_OF_MEMORY = 4;
+ public static final int REASON_NOT_CONNECTED_TO_HOMECARRIER_LTE = 5;
+
+ /**
* The method of transmission currently used for a stream,
* reported via {@link StreamingServiceCallback#streamMethodUpdated}
*/
diff --git a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
index fdfb188..cab9c23 100644
--- a/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
+++ b/telephony/java/android/telephony/mbms/StreamingServiceCallback.java
@@ -31,6 +31,7 @@
*/
public static final int SIGNAL_STRENGTH_UNAVAILABLE = -1;
+ @Override
public void error(int errorCode, String message) {
// default implementation empty
}
@@ -41,7 +42,9 @@
* See {@link StreamingService#STATE_STOPPED}, {@link StreamingService#STATE_STARTED}
* and {@link StreamingService#STATE_STALLED}.
*/
- public void streamStateUpdated(int state) {
+ @Override
+ public void streamStateUpdated(@StreamingService.StreamingState int state,
+ @StreamingService.StreamingStateChangeReason int reason) {
// default implementation empty
}
@@ -55,6 +58,7 @@
* This may be called when a looping stream hits the end or
* when parameters have changed to account for time drift.
*/
+ @Override
public void mediaDescriptionUpdated() {
// default implementation empty
}
@@ -69,6 +73,7 @@
* {@link #SIGNAL_STRENGTH_UNAVAILABLE} if broadcast is not available
* for this service due to timing, geography or popularity.
*/
+ @Override
public void broadcastSignalStrengthUpdated(int signalStrength) {
// default implementation empty
}
@@ -89,6 +94,7 @@
* See {@link StreamingService#BROADCAST_METHOD} and
* {@link StreamingService#UNICAST_METHOD}
*/
+ @Override
public void streamMethodUpdated(int methodType) {
// default implementation empty
}
diff --git a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
index b6cf628..c97e754 100644
--- a/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
+++ b/telephony/java/android/telephony/mbms/vendor/MbmsStreamingServiceBase.java
@@ -72,7 +72,7 @@
/**
* Starts streaming on a particular service. This method may perform asynchronous work. When
* the middleware is ready to send bits to the frontend, it should inform the app via
- * {@link IStreamingServiceCallback#streamStateUpdated(int)}.
+ * {@link IStreamingServiceCallback#streamStateUpdated(int, int)}.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
@@ -107,7 +107,7 @@
/**
* Stop streaming the stream identified by {@code serviceId}. Notification of the resulting
* stream state change should be reported to the app via
- * {@link IStreamingServiceCallback#streamStateUpdated(int)}.
+ * {@link IStreamingServiceCallback#streamStateUpdated(int, int)}.
*
* May throw an {@link IllegalArgumentException} or an {@link IllegalStateException}
*
diff --git a/tests/net/java/android/net/nsd/NsdManagerTest.java b/tests/net/java/android/net/nsd/NsdManagerTest.java
index adf6998..f77608f 100644
--- a/tests/net/java/android/net/nsd/NsdManagerTest.java
+++ b/tests/net/java/android/net/nsd/NsdManagerTest.java
@@ -349,7 +349,6 @@
chan.connect(mContext, this, msg.replyTo);
chan.sendMessage(AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED);
}
-
}
public static MockServiceHandler create(Context context) {
diff --git a/tests/net/java/android/net/util/SharedLogTest.java b/tests/net/java/android/net/util/SharedLogTest.java
index 3957cb0..d46facf 100644
--- a/tests/net/java/android/net/util/SharedLogTest.java
+++ b/tests/net/java/android/net/util/SharedLogTest.java
@@ -33,8 +33,8 @@
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SharedLogTest {
- private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}\\.\\d{3}";
- private static final String TIMESTAMP = "HH:MM:SS.xxx";
+ private static final String TIMESTAMP_PATTERN = "\\d{2}:\\d{2}:\\d{2}";
+ private static final String TIMESTAMP = "HH:MM:SS";
@Test
public void testBasicOperation() {
@@ -85,7 +85,7 @@
String got = lines[i];
String want = expected[i];
assertTrue(String.format("'%s' did not contain '%s'", got, want), got.endsWith(want));
- assertTrue(String.format("'%s' did not contain a HH:MM:SS.xxx timestamp", got),
+ assertTrue(String.format("'%s' did not contain a %s timestamp", got, TIMESTAMP),
got.replaceFirst(TIMESTAMP_PATTERN, TIMESTAMP).contains(TIMESTAMP));
}
}
diff --git a/tests/net/java/com/android/internal/util/TestUtils.java b/tests/net/java/com/android/internal/util/TestUtils.java
new file mode 100644
index 0000000..c9fa340
--- /dev/null
+++ b/tests/net/java/com/android/internal/util/TestUtils.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.internal.util;
+
+import static org.junit.Assert.fail;
+
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+
+public final class TestUtils {
+ private TestUtils() { }
+
+ /**
+ * Block until the given Handler thread becomes idle, or until timeoutMs has passed.
+ */
+ public static void waitForIdleHandler(HandlerThread handlerThread, long timeoutMs) {
+ // TODO: convert to getThreadHandler once it is available on aosp
+ waitForIdleLooper(handlerThread.getLooper(), timeoutMs);
+ }
+
+ /**
+ * Block until the given Looper becomes idle, or until timeoutMs has passed.
+ */
+ public static void waitForIdleLooper(Looper looper, long timeoutMs) {
+ waitForIdleHandler(new Handler(looper), timeoutMs);
+ }
+
+ /**
+ * Block until the given Handler becomes idle, or until timeoutMs has passed.
+ */
+ public static void waitForIdleHandler(Handler handler, long timeoutMs) {
+ final ConditionVariable cv = new ConditionVariable();
+ handler.post(() -> cv.open());
+ if (!cv.block(timeoutMs)) {
+ fail(handler.toString() + " did not become idle after " + timeoutMs + " ms");
+ }
+ }
+}
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 0263c57..9d4a2b0 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -19,10 +19,13 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_ETHERNET;
import static android.net.ConnectivityManager.TYPE_MOBILE;
+import static android.net.ConnectivityManager.TYPE_NONE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.getNetworkTypeName;
import static android.net.NetworkCapabilities.*;
+import static com.android.internal.util.TestUtils.waitForIdleHandler;
+
import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.eq;
@@ -111,6 +114,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BooleanSupplier;
+import java.util.function.Predicate;
/**
* Tests for {@link ConnectivityService}.
@@ -211,20 +215,8 @@
}
}
- /**
- * Block until the given handler becomes idle, or until timeoutMs has passed.
- */
- private static void waitForIdleHandler(HandlerThread handlerThread, int timeoutMs) {
- final ConditionVariable cv = new ConditionVariable();
- final Handler handler = new Handler(handlerThread.getLooper());
- handler.post(() -> cv.open());
- if (!cv.block(timeoutMs)) {
- fail("HandlerThread " + handlerThread.getName() +
- " did not become idle after " + timeoutMs + " ms");
- }
- }
-
- public void waitForIdle(int timeoutMs) {
+ public void waitForIdle(int timeoutMsAsInt) {
+ long timeoutMs = timeoutMsAsInt;
waitForIdleHandler(mService.mHandlerThread, timeoutMs);
waitForIdle(mCellNetworkAgent, timeoutMs);
waitForIdle(mWiFiNetworkAgent, timeoutMs);
@@ -232,7 +224,7 @@
waitForIdleHandler(mService.mHandlerThread, timeoutMs);
}
- public void waitForIdle(MockNetworkAgent agent, int timeoutMs) {
+ public void waitForIdle(MockNetworkAgent agent, long timeoutMs) {
if (agent == null) {
return;
}
@@ -326,6 +318,9 @@
case TRANSPORT_CELLULAR:
mScore = 50;
break;
+ case TRANSPORT_WIFI_AWARE:
+ mScore = 20;
+ break;
default:
throw new UnsupportedOperationException("unimplemented network type");
}
@@ -407,6 +402,15 @@
* @param validated Indicate if network should pretend to be validated.
*/
public void connect(boolean validated) {
+ connect(validated, true);
+ }
+
+ /**
+ * Transition this NetworkAgent to CONNECTED state.
+ * @param validated Indicate if network should pretend to be validated.
+ * @param hasInternet Indicate if network should pretend to have NET_CAPABILITY_INTERNET.
+ */
+ public void connect(boolean validated, boolean hasInternet) {
assertEquals("MockNetworkAgents can only be connected once",
mNetworkInfo.getDetailedState(), DetailedState.IDLE);
assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
@@ -429,7 +433,9 @@
};
mCm.registerNetworkCallback(request, callback);
}
- addCapability(NET_CAPABILITY_INTERNET);
+ if (hasInternet) {
+ addCapability(NET_CAPABILITY_INTERNET);
+ }
connectWithoutInternet();
@@ -783,7 +789,10 @@
* Fails if TIMEOUT_MS goes by before {@code conditionVariable} opens.
*/
static private void waitFor(ConditionVariable conditionVariable) {
- assertTrue(conditionVariable.block(TIMEOUT_MS));
+ if (conditionVariable.block(TIMEOUT_MS)) {
+ return;
+ }
+ fail("ConditionVariable was blocked for more than " + TIMEOUT_MS + "ms");
}
@Override
@@ -838,7 +847,7 @@
case TRANSPORT_CELLULAR:
return TYPE_MOBILE;
default:
- throw new IllegalStateException("Unknown transport " + transport);
+ return TYPE_NONE;
}
}
@@ -849,6 +858,9 @@
// Test getActiveNetwork()
assertNotNull(mCm.getActiveNetwork());
assertEquals(mCm.getActiveNetwork(), mCm.getActiveNetworkForUid(Process.myUid()));
+ if (!NetworkCapabilities.isValidTransport(transport)) {
+ throw new IllegalStateException("Unknown transport " + transport);
+ }
switch (transport) {
case TRANSPORT_WIFI:
assertEquals(mCm.getActiveNetwork(), mWiFiNetworkAgent.getNetwork());
@@ -857,7 +869,7 @@
assertEquals(mCm.getActiveNetwork(), mCellNetworkAgent.getNetwork());
break;
default:
- throw new IllegalStateException("Unknown transport" + transport);
+ break;
}
// Test getNetworkInfo(Network)
assertNotNull(mCm.getNetworkInfo(mCm.getActiveNetwork()));
@@ -875,7 +887,7 @@
assertNull(mCm.getActiveNetwork());
assertNull(mCm.getActiveNetworkForUid(Process.myUid()));
// Test getAllNetworks()
- assertEquals(0, mCm.getAllNetworks().length);
+ assertEmpty(mCm.getAllNetworks());
}
/**
@@ -916,7 +928,7 @@
mCellNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_CELLULAR);
- assertEquals(2, mCm.getAllNetworks().length);
+ assertLength(2, mCm.getAllNetworks());
assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
@@ -926,7 +938,7 @@
mWiFiNetworkAgent.connect(true);
waitFor(cv);
verifyActiveNetwork(TRANSPORT_WIFI);
- assertEquals(2, mCm.getAllNetworks().length);
+ assertLength(2, mCm.getAllNetworks());
assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) ||
@@ -934,9 +946,9 @@
// Test cellular linger timeout.
waitFor(mCellNetworkAgent.getDisconnectedCV());
waitForIdle();
- assertEquals(1, mCm.getAllNetworks().length);
+ assertLength(1, mCm.getAllNetworks());
verifyActiveNetwork(TRANSPORT_WIFI);
- assertEquals(1, mCm.getAllNetworks().length);
+ assertLength(1, mCm.getAllNetworks());
assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
// Test WiFi disconnect.
cv = waitForConnectivityBroadcasts(1);
@@ -1277,7 +1289,26 @@
return expectCallback(state, agent, TIMEOUT_MS);
}
- void expectAvailableCallbacks(MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
+ CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn) {
+ return expectCallbackLike(fn, TIMEOUT_MS);
+ }
+
+ CallbackInfo expectCallbackLike(Predicate<CallbackInfo> fn, int timeoutMs) {
+ int timeLeft = timeoutMs;
+ while (timeLeft > 0) {
+ long start = SystemClock.elapsedRealtime();
+ CallbackInfo info = nextCallback(timeLeft);
+ if (fn.test(info)) {
+ return info;
+ }
+ timeLeft -= (SystemClock.elapsedRealtime() - start);
+ }
+ fail("Did not receive expected callback after " + timeoutMs + "ms");
+ return null;
+ }
+
+ void expectAvailableCallbacks(
+ MockNetworkAgent agent, boolean expectSuspended, int timeoutMs) {
expectCallback(CallbackState.AVAILABLE, agent, timeoutMs);
if (expectSuspended) {
expectCallback(CallbackState.SUSPENDED, agent, timeoutMs);
@@ -1834,26 +1865,18 @@
@SmallTest
public void testNoMutableNetworkRequests() throws Exception {
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, new Intent("a"), 0);
- NetworkRequest.Builder builder = new NetworkRequest.Builder();
- builder.addCapability(NET_CAPABILITY_VALIDATED);
- try {
- mCm.requestNetwork(builder.build(), new NetworkCallback());
- fail();
- } catch (IllegalArgumentException expected) {}
- try {
- mCm.requestNetwork(builder.build(), pendingIntent);
- fail();
- } catch (IllegalArgumentException expected) {}
- builder = new NetworkRequest.Builder();
- builder.addCapability(NET_CAPABILITY_CAPTIVE_PORTAL);
- try {
- mCm.requestNetwork(builder.build(), new NetworkCallback());
- fail();
- } catch (IllegalArgumentException expected) {}
- try {
- mCm.requestNetwork(builder.build(), pendingIntent);
- fail();
- } catch (IllegalArgumentException expected) {}
+ NetworkRequest request1 = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_VALIDATED)
+ .build();
+ NetworkRequest request2 = new NetworkRequest.Builder()
+ .addCapability(NET_CAPABILITY_CAPTIVE_PORTAL)
+ .build();
+
+ Class<IllegalArgumentException> expected = IllegalArgumentException.class;
+ assertException(() -> { mCm.requestNetwork(request1, new NetworkCallback()); }, expected);
+ assertException(() -> { mCm.requestNetwork(request1, pendingIntent); }, expected);
+ assertException(() -> { mCm.requestNetwork(request2, new NetworkCallback()); }, expected);
+ assertException(() -> { mCm.requestNetwork(request2, pendingIntent); }, expected);
}
@SmallTest
@@ -1865,7 +1888,7 @@
mCellNetworkAgent.connectWithoutInternet();
waitFor(cv);
waitForIdle();
- assertEquals(0, mCm.getAllNetworks().length);
+ assertEmpty(mCm.getAllNetworks());
verifyNoNetwork();
// Test bringing up validated WiFi.
@@ -2539,7 +2562,7 @@
assertTrue(testFactory.getMyStartRequested());
// Bring up cell data and check that the factory stops looking.
- assertEquals(1, mCm.getAllNetworks().length);
+ assertLength(1, mCm.getAllNetworks());
mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
testFactory.expectAddRequests(2); // Because the cell request changes score twice.
mCellNetworkAgent.connect(true);
@@ -2550,7 +2573,7 @@
// Check that cell data stays up.
waitForIdle();
verifyActiveNetwork(TRANSPORT_WIFI);
- assertEquals(2, mCm.getAllNetworks().length);
+ assertLength(2, mCm.getAllNetworks());
// Turn off mobile data always on and expect the request to disappear...
testFactory.expectRemoveRequests(1);
@@ -2559,7 +2582,7 @@
// ... and cell data to be torn down.
cellNetworkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
- assertEquals(1, mCm.getAllNetworks().length);
+ assertLength(1, mCm.getAllNetworks());
testFactory.unregister();
mCm.unregisterNetworkCallback(cellNetworkCallback);
@@ -2780,19 +2803,17 @@
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
- final int requestTimeoutMs = 100;
+ final int requestTimeoutMs = 50;
mCm.requestNetwork(nr, networkCallback, requestTimeoutMs);
mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
mWiFiNetworkAgent.connect(false);
- final int assertTimeoutMs = 150;
+ final int assertTimeoutMs = 100;
networkCallback.expectAvailableCallbacks(mWiFiNetworkAgent, false, assertTimeoutMs);
- sleepFor(20);
mWiFiNetworkAgent.disconnect();
networkCallback.expectCallback(CallbackState.LOST, mWiFiNetworkAgent);
- // pass timeout and validate that UNAVAILABLE is not called
- sleepFor(100);
+ // Validate that UNAVAILABLE is not called
networkCallback.assertNoCallback();
}
@@ -2819,24 +2840,20 @@
}
/**
- * Validate that when a network request is unregistered (cancelled) the time-out for that
- * request doesn't trigger the onUnavailable() callback.
+ * Validate that when a network request is unregistered (cancelled), no posterior event can
+ * trigger the callback.
*/
@SmallTest
- public void testTimedoutAfterUnregisteredNetworkRequest() {
+ public void testNoCallbackAfterUnregisteredNetworkRequest() {
NetworkRequest nr = new NetworkRequest.Builder().addTransportType(
NetworkCapabilities.TRANSPORT_WIFI).build();
final TestNetworkCallback networkCallback = new TestNetworkCallback();
final int timeoutMs = 10;
+
mCm.requestNetwork(nr, networkCallback, timeoutMs);
-
- // remove request
mCm.unregisterNetworkCallback(networkCallback);
-
- // pass timeout and validate that no callbacks
- // Note: doesn't validate that nothing called from CS since even if called the CM already
- // unregisters the callback and won't pass it through!
- sleepFor(15);
+ // Regardless of the timeout, unregistering the callback in ConnectivityManager ensures
+ // that this callback will not be called.
networkCallback.assertNoCallback();
// create a network satisfying request - validate that request not triggered
@@ -3255,12 +3272,87 @@
}
}
- /* test utilities */
- // TODO: eliminate all usages of sleepFor and replace by proper timeouts/waitForIdle.
- static private void sleepFor(int ms) {
+ @SmallTest
+ public void testNetworkInfoOfTypeNone() {
+ ConditionVariable broadcastCV = waitForConnectivityBroadcasts(1);
+
+ verifyNoNetwork();
+ MockNetworkAgent lowpanNetwork = new MockNetworkAgent(TRANSPORT_WIFI_AWARE);
+ assertNull(mCm.getActiveNetworkInfo());
+
+ Network[] allNetworks = mCm.getAllNetworks();
+ assertLength(1, allNetworks);
+ Network network = allNetworks[0];
+ NetworkCapabilities capabilities = mCm.getNetworkCapabilities(network);
+ assertTrue(capabilities.hasTransport(TRANSPORT_WIFI_AWARE));
+
+ final NetworkRequest request =
+ new NetworkRequest.Builder().addTransportType(TRANSPORT_WIFI_AWARE).build();
+ final TestNetworkCallback callback = new TestNetworkCallback();
+ mCm.registerNetworkCallback(request, callback);
+
+ // Bring up lowpan.
+ lowpanNetwork.connect(false, false);
+ callback.expectAvailableCallbacks(lowpanNetwork);
+
+ assertNull(mCm.getActiveNetworkInfo());
+ assertNull(mCm.getActiveNetwork());
+ // TODO: getAllNetworkInfo is dirty and returns a non-empty array rght from the start
+ // of this test. Fix it and uncomment the assert below.
+ //assertEmpty(mCm.getAllNetworkInfo());
+
+ // Disconnect lowpan.
+ lowpanNetwork.disconnect();
+ callback.expectCallback(CallbackState.LOST, lowpanNetwork);
+ mCm.unregisterNetworkCallback(callback);
+
+ verifyNoNetwork();
+ if (broadcastCV.block(10)) {
+ fail("expected no broadcast, but got CONNECTIVITY_ACTION broadcast");
+ }
+ }
+
+ @SmallTest
+ public void testDeprecatedAndUnsupportedOperations() throws Exception {
+ final int TYPE_NONE = ConnectivityManager.TYPE_NONE;
+ assertNull(mCm.getNetworkInfo(TYPE_NONE));
+ assertNull(mCm.getNetworkForType(TYPE_NONE));
+ assertNull(mCm.getLinkProperties(TYPE_NONE));
+ assertFalse(mCm.isNetworkSupported(TYPE_NONE));
+
+ assertException(() -> { mCm.networkCapabilitiesForType(TYPE_NONE); },
+ IllegalArgumentException.class);
+
+ Class<UnsupportedOperationException> unsupported = UnsupportedOperationException.class;
+ assertException(() -> { mCm.startUsingNetworkFeature(TYPE_WIFI, ""); }, unsupported);
+ assertException(() -> { mCm.stopUsingNetworkFeature(TYPE_WIFI, ""); }, unsupported);
+ // TODO: let test context have configuration application target sdk version
+ // and test that pre-M requesting for TYPE_NONE sends back APN_REQUEST_FAILED
+ assertException(() -> { mCm.startUsingNetworkFeature(TYPE_NONE, ""); }, unsupported);
+ assertException(() -> { mCm.stopUsingNetworkFeature(TYPE_NONE, ""); }, unsupported);
+ assertException(() -> { mCm.requestRouteToHostAddress(TYPE_NONE, null); }, unsupported);
+ }
+
+ private static <T> void assertEmpty(T[] ts) {
+ int length = ts.length;
+ assertEquals("expected empty array, but length was " + length, 0, length);
+ }
+
+ private static <T> void assertLength(int expected, T[] got) {
+ int length = got.length;
+ assertEquals(String.format("expected array of length %s, but length was %s for %s",
+ expected, length, Arrays.toString(got)), expected, length);
+ }
+
+ private static <T> void assertException(Runnable block, Class<T> expected) {
try {
- Thread.sleep(ms);
- } catch (InterruptedException e) {
+ block.run();
+ fail("Expected exception of type " + expected);
+ } catch (Exception got) {
+ if (!got.getClass().equals(expected)) {
+ fail("Expected exception of type " + expected + " but got " + got);
+ }
+ return;
}
}
}
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index 5b4e901..460d53e 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -16,6 +16,11 @@
package com.android.server.connectivity;
+import static android.hardware.usb.UsbManager.USB_CONFIGURED;
+import static android.hardware.usb.UsbManager.USB_CONNECTED;
+import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
@@ -133,6 +138,7 @@
public Object getSystemService(String name) {
if (Context.CONNECTIVITY_SERVICE.equals(name)) return mConnectivityManager;
if (Context.WIFI_SERVICE.equals(name)) return mWifiManager;
+ if (Context.USB_SERVICE.equals(name)) return mUsbManager;
return super.getSystemService(name);
}
}
@@ -145,7 +151,7 @@
when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
.thenReturn(new String[0]);
when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
- .thenReturn(new String[]{ "test_wlan\\d" });
+ .thenReturn(new String[]{ "test_wlan\\d", "test_rndis\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
@@ -245,6 +251,14 @@
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) {
+ final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
+ intent.putExtra(USB_CONNECTED, connected);
+ intent.putExtra(USB_CONFIGURED, configured);
+ intent.putExtra(USB_FUNCTION_RNDIS, rndisFunction);
+ mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
private void verifyInterfaceServingModeStarted(boolean ifnameKnown) throws Exception {
if (!ifnameKnown) {
verify(mNMService, times(1)).listInterfaces();
@@ -264,6 +278,32 @@
mIntents.remove(bcast);
}
+ @Test
+ public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+ when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
+
+ // Emulate pressing the USB tethering button in Settings UI.
+ mTethering.startTethering(TETHERING_USB, null, false);
+ mLooper.dispatchAll();
+ verify(mUsbManager, times(1)).setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
+
+ // Pretend we receive a USB connected broadcast. Here we also pretend
+ // that the RNDIS function is somehow enabled, so that we see if we
+ // might trip ourselves up.
+ sendUsbBroadcast(true, false, true);
+ mLooper.dispatchAll();
+ // This should produce no activity of any kind.
+ verifyNoMoreInteractions(mConnectivityManager);
+ verifyNoMoreInteractions(mNMService);
+
+ // Pretend we then receive USB configured broadcast.
+ sendUsbBroadcast(true, true, true);
+ mLooper.dispatchAll();
+ // Now we should see the start of tethering mechanics (in this case:
+ // tetherMatchingInterfaces() which starts by fetching all interfaces).
+ verify(mNMService, times(1)).listInterfaces();
+ }
+
public void workingLocalOnlyHotspot(
boolean withInterfaceStateChanged, boolean enrichedApBroadcast) throws Exception {
when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
@@ -341,7 +381,7 @@
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false);
+ mTethering.startTethering(TETHERING_WIFI, null, false);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startSoftAp(null);
verifyNoMoreInteractions(mWifiManager);
@@ -391,7 +431,7 @@
/////
// Emulate pressing the WiFi tethering button.
- mTethering.stopTethering(ConnectivityManager.TETHERING_WIFI);
+ mTethering.stopTethering(TETHERING_WIFI);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).stopSoftAp();
verifyNoMoreInteractions(mWifiManager);
@@ -436,7 +476,7 @@
doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false);
+ mTethering.startTethering(TETHERING_WIFI, null, false);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startSoftAp(null);
verifyNoMoreInteractions(mWifiManager);
diff --git a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
index fb5c577..69c93b1 100644
--- a/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
+++ b/tests/net/java/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java
@@ -44,6 +44,9 @@
import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.IConnectivityManager;
+import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.net.LinkProperties;
import android.net.Network;
import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
@@ -66,6 +69,7 @@
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
@@ -77,6 +81,9 @@
public class UpstreamNetworkMonitorTest {
private static final int EVENT_UNM_UPDATE = 1;
+ private static final boolean INCLUDES = true;
+ private static final boolean EXCLUDES = false;
+
@Mock private Context mContext;
@Mock private IConnectivityManager mCS;
@Mock private SharedLog mLog;
@@ -94,7 +101,8 @@
mCM = spy(new TestConnectivityManager(mContext, mCS));
mSM = new TestStateMachine();
- mUNM = new UpstreamNetworkMonitor(mSM, EVENT_UNM_UPDATE, (ConnectivityManager) mCM, mLog);
+ mUNM = new UpstreamNetworkMonitor(
+ (ConnectivityManager) mCM, mSM, mLog, EVENT_UNM_UPDATE);
}
@After public void tearDown() throws Exception {
@@ -315,6 +323,101 @@
assertTrue(netReq.networkCapabilities.hasCapability(NET_CAPABILITY_DUN));
}
+ @Test
+ public void testOffloadExemptPrefixes() throws Exception {
+ mUNM.start();
+
+ // [0] Test minimum set of exempt prefixes.
+ Set<IpPrefix> exempt = mUNM.getOffloadExemptPrefixes();
+ final String[] MINSET = {
+ "127.0.0.0/8", "169.254.0.0/16",
+ "::/3", "fe80::/64", "fc00::/7", "ff00::/8",
+ };
+ assertPrefixSet(exempt, INCLUDES, MINSET);
+ final Set<String> alreadySeen = new HashSet<>();
+ Collections.addAll(alreadySeen, MINSET);
+ assertEquals(alreadySeen.size(), exempt.size());
+
+ // [1] Pretend Wi-Fi connects.
+ final TestNetworkAgent wifiAgent = new TestNetworkAgent(mCM, TRANSPORT_WIFI);
+ final LinkProperties wifiLp = new LinkProperties();
+ wifiLp.setInterfaceName("wlan0");
+ final String[] WIFI_ADDRS = {
+ "fe80::827a:bfff:fe6f:374d", "100.112.103.18",
+ "2001:db8:4:fd00:827a:bfff:fe6f:374d",
+ "2001:db8:4:fd00:6dea:325a:fdae:4ef4",
+ "fd6a:a640:60bf:e985::123", // ULA address for good measure.
+ };
+ for (String addrStr : WIFI_ADDRS) {
+ final String cidr = addrStr.contains(":") ? "/64" : "/20";
+ wifiLp.addLinkAddress(new LinkAddress(addrStr + cidr));
+ }
+ wifiAgent.fakeConnect();
+ wifiAgent.sendLinkProperties(wifiLp);
+
+ exempt = mUNM.getOffloadExemptPrefixes();
+ assertPrefixSet(exempt, INCLUDES, alreadySeen);
+ final String[] wifiLinkPrefixes = {
+ // Excludes link-local as that's already tested within MINSET.
+ "100.112.96.0/20", "2001:db8:4:fd00::/64", "fd6a:a640:60bf:e985::/64",
+ };
+ assertPrefixSet(exempt, INCLUDES, wifiLinkPrefixes);
+ Collections.addAll(alreadySeen, wifiLinkPrefixes);
+ assertEquals(alreadySeen.size(), exempt.size());
+
+ // [2] Pretend mobile connects.
+ final TestNetworkAgent cellAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ final LinkProperties cellLp = new LinkProperties();
+ cellLp.setInterfaceName("rmnet_data0");
+ final String[] CELL_ADDRS = {
+ "10.102.211.48", "2001:db8:0:1:b50e:70d9:10c9:433d",
+ };
+ for (String addrStr : CELL_ADDRS) {
+ final String cidr = addrStr.contains(":") ? "/64" : "/27";
+ cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
+ }
+ cellAgent.fakeConnect();
+ cellAgent.sendLinkProperties(cellLp);
+
+ exempt = mUNM.getOffloadExemptPrefixes();
+ assertPrefixSet(exempt, INCLUDES, alreadySeen);
+ final String[] cellLinkPrefixes = { "10.102.211.32/27", "2001:db8:0:1::/64" };
+ assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes);
+ Collections.addAll(alreadySeen, cellLinkPrefixes);
+ assertEquals(alreadySeen.size(), exempt.size());
+
+ // [3] Pretend DUN connects.
+ final TestNetworkAgent dunAgent = new TestNetworkAgent(mCM, TRANSPORT_CELLULAR);
+ dunAgent.networkCapabilities.addCapability(NET_CAPABILITY_DUN);
+ final LinkProperties dunLp = new LinkProperties();
+ dunLp.setInterfaceName("rmnet_data1");
+ final String[] DUN_ADDRS = {
+ "192.0.2.48", "2001:db8:1:2:b50e:70d9:10c9:433d",
+ };
+ for (String addrStr : DUN_ADDRS) {
+ final String cidr = addrStr.contains(":") ? "/64" : "/27";
+ cellLp.addLinkAddress(new LinkAddress(addrStr + cidr));
+ }
+ dunAgent.fakeConnect();
+ dunAgent.sendLinkProperties(dunLp);
+
+ exempt = mUNM.getOffloadExemptPrefixes();
+ assertPrefixSet(exempt, INCLUDES, alreadySeen);
+ final String[] dunLinkPrefixes = { "192.0.2.32/27", "2001:db8:1:2::/64" };
+ assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes);
+ Collections.addAll(alreadySeen, dunLinkPrefixes);
+ assertEquals(alreadySeen.size(), exempt.size());
+
+ // [4] Pretend Wi-Fi disconnected. It's addresses/prefixes should no
+ // longer be included (should be properly removed).
+ wifiAgent.fakeDisconnect();
+ exempt = mUNM.getOffloadExemptPrefixes();
+ assertPrefixSet(exempt, INCLUDES, MINSET);
+ assertPrefixSet(exempt, EXCLUDES, wifiLinkPrefixes);
+ assertPrefixSet(exempt, INCLUDES, cellLinkPrefixes);
+ assertPrefixSet(exempt, INCLUDES, dunLinkPrefixes);
+ }
+
private void assertSatisfiesLegacyType(int legacyType, NetworkState ns) {
if (legacyType == TYPE_NONE) {
assertTrue(ns == null);
@@ -476,6 +579,12 @@
cb.onLost(networkId);
}
}
+
+ public void sendLinkProperties(LinkProperties lp) {
+ for (NetworkCallback cb : cm.listening.keySet()) {
+ cb.onLinkPropertiesChanged(networkId, lp);
+ }
+ }
}
public static class TestStateMachine extends StateMachine {
@@ -504,4 +613,19 @@
static NetworkCapabilities copy(NetworkCapabilities nc) {
return new NetworkCapabilities(nc);
}
+
+ static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, String... expected) {
+ final Set<String> expectedSet = new HashSet<>();
+ Collections.addAll(expectedSet, expected);
+ assertPrefixSet(prefixes, expectation, expectedSet);
+ }
+
+ static void assertPrefixSet(Set<IpPrefix> prefixes, boolean expectation, Set<String> expected) {
+ for (String expectedPrefix : expected) {
+ final String errStr = expectation ? "did not find" : "found";
+ assertEquals(
+ String.format("Failed expectation: %s prefix: %s", errStr, expectedPrefix),
+ expectation, prefixes.contains(new IpPrefix(expectedPrefix)));
+ }
+ }
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
index 92dcdac..2be5dae 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsObserversTest.java
@@ -27,6 +27,8 @@
import static android.net.TrafficStats.MB_IN_BYTES;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.internal.util.TestUtils.waitForIdleHandler;
+
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -56,7 +58,6 @@
import com.android.internal.net.VpnInfo;
import com.android.server.net.NetworkStatsService;
-import com.android.server.net.NetworkStatsServiceTest.IdleableHandlerThread;
import com.android.server.net.NetworkStatsServiceTest.LatchedHandler;
import java.util.ArrayList;
@@ -102,7 +103,7 @@
private long mElapsedRealtime;
- private IdleableHandlerThread mObserverHandlerThread;
+ private HandlerThread mObserverHandlerThread;
private Handler mObserverNoopHandler;
private LatchedHandler mHandler;
@@ -118,7 +119,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
- mObserverHandlerThread = new IdleableHandlerThread("HandlerThread");
+ mObserverHandlerThread = new HandlerThread("HandlerThread");
mObserverHandlerThread.start();
final Looper observerLooper = mObserverHandlerThread.getLooper();
mStatsObservers = new NetworkStatsObservers() {
@@ -319,7 +320,7 @@
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
waitForObserverToIdle();
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
}
@Test
@@ -356,7 +357,7 @@
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
waitForObserverToIdle();
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
}
@Test
@@ -429,7 +430,7 @@
xtSnapshot, uidSnapshot, mActiveIfaces, mActiveUidIfaces,
VPN_INFO, TEST_START);
waitForObserverToIdle();
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, mHandler.lastMessageType);
}
@Test
@@ -470,19 +471,7 @@
}
private void waitForObserverToIdle() {
- waitForIdleLooper(mObserverHandlerThread.getLooper(), WAIT_TIMEOUT_MS);
- waitForIdleLooper(mHandler.getLooper(), WAIT_TIMEOUT_MS);
+ waitForIdleHandler(mObserverHandlerThread, WAIT_TIMEOUT_MS);
+ waitForIdleHandler(mHandler, WAIT_TIMEOUT_MS);
}
-
- // TODO: unify with ConnectivityService.waitForIdleHandler and
- // NetworkServiceStatsTest.IdleableHandlerThread
- private static void waitForIdleLooper(Looper looper, long timeoutMs) {
- final ConditionVariable cv = new ConditionVariable();
- final Handler handler = new Handler(looper);
- handler.post(() -> cv.open());
- if (!cv.block(timeoutMs)) {
- fail("Looper did not become idle after " + timeoutMs + " ms");
- }
- }
-
}
diff --git a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
index 029693f..feb46d3 100644
--- a/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
+++ b/tests/net/java/com/android/server/net/NetworkStatsServiceTest.java
@@ -45,6 +45,7 @@
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
+import static com.android.internal.util.TestUtils.waitForIdleHandler;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -154,7 +155,7 @@
private @Mock IConnectivityManager mConnManager;
private @Mock IBinder mBinder;
private @Mock AlarmManager mAlarmManager;
- private IdleableHandlerThread mHandlerThread;
+ private HandlerThread mHandlerThread;
private Handler mHandler;
private NetworkStatsService mService;
@@ -181,7 +182,7 @@
mServiceContext, mNetManager, mAlarmManager, wakeLock, mTime,
TelephonyManager.getDefault(), mSettings, new NetworkStatsObservers(),
mStatsDir, getBaseDir(mStatsDir));
- mHandlerThread = new IdleableHandlerThread("HandlerThread");
+ mHandlerThread = new HandlerThread("HandlerThread");
mHandlerThread.start();
Handler.Callback callback = new NetworkStatsService.HandlerCallback(mService);
mHandler = new Handler(mHandlerThread.getLooper(), callback);
@@ -886,7 +887,7 @@
// Send dummy message to make sure that any previous message has been handled
mHandler.sendMessage(mHandler.obtainMessage(-1));
- mHandlerThread.waitForIdle(WAIT_TIMEOUT);
+ waitForIdleHandler(mHandler, WAIT_TIMEOUT);
@@ -908,7 +909,7 @@
assertNetworkTotal(sTemplateWifi, 1024L, 1L, 2048L, 2L, 0);
// make sure callback has not being called
- assertEquals(INVALID_TYPE, latchedHandler.mLastMessageType);
+ assertEquals(INVALID_TYPE, latchedHandler.lastMessageType);
// and bump forward again, with counters going higher. this is
// important, since it will trigger the data usage callback
@@ -926,7 +927,7 @@
// Wait for the caller to ack receipt of CALLBACK_LIMIT_REACHED
assertTrue(cv.block(WAIT_TIMEOUT));
- assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, latchedHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_LIMIT_REACHED, latchedHandler.lastMessageType);
cv.close();
// Allow binder to disconnect
@@ -937,7 +938,7 @@
// Wait for the caller to ack receipt of CALLBACK_RELEASED
assertTrue(cv.block(WAIT_TIMEOUT));
- assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.mLastMessageType);
+ assertEquals(NetworkStatsManager.CALLBACK_RELEASED, latchedHandler.lastMessageType);
// Make sure that the caller binder gets disconnected
verify(mBinder).unlinkToDeath(any(IBinder.DeathRecipient.class), anyInt());
@@ -1203,12 +1204,12 @@
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
// Send dummy message to make sure that any previous message has been handled
mHandler.sendMessage(mHandler.obtainMessage(-1));
- mHandlerThread.waitForIdle(WAIT_TIMEOUT);
+ waitForIdleHandler(mHandler, WAIT_TIMEOUT);
}
static class LatchedHandler extends Handler {
private final ConditionVariable mCv;
- int mLastMessageType = INVALID_TYPE;
+ int lastMessageType = INVALID_TYPE;
LatchedHandler(Looper looper, ConditionVariable cv) {
super(looper);
@@ -1217,49 +1218,9 @@
@Override
public void handleMessage(Message msg) {
- mLastMessageType = msg.what;
+ lastMessageType = msg.what;
mCv.open();
super.handleMessage(msg);
}
}
-
- /**
- * A subclass of HandlerThread that allows callers to wait for it to become idle. waitForIdle
- * will return immediately if the handler is already idle.
- */
- static class IdleableHandlerThread extends HandlerThread {
- private IdleHandler mIdleHandler;
-
- public IdleableHandlerThread(String name) {
- super(name);
- }
-
- public void waitForIdle(long timeoutMs) {
- final ConditionVariable cv = new ConditionVariable();
- final MessageQueue queue = getLooper().getQueue();
-
- synchronized (queue) {
- if (queue.isIdle()) {
- return;
- }
-
- assertNull("BUG: only one idle handler allowed", mIdleHandler);
- mIdleHandler = new IdleHandler() {
- public boolean queueIdle() {
- cv.open();
- mIdleHandler = null;
- return false; // Remove the handler.
- }
- };
- queue.addIdleHandler(mIdleHandler);
- }
-
- if (!cv.block(timeoutMs)) {
- fail("HandlerThread " + getName() + " did not become idle after " + timeoutMs
- + " ms");
- queue.removeIdleHandler(mIdleHandler);
- }
- }
- }
-
}