Merge "Final polish of the interrogation feature."
diff --git a/api/current.txt b/api/current.txt
index 82706f4..f641c96 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11180,6 +11180,8 @@
method public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
method public static javax.net.ssl.SSLSocketFactory getInsecure(int, android.net.SSLSessionCache);
method public java.lang.String[] getSupportedCipherSuites();
+ method public void setKeyManagers(javax.net.ssl.KeyManager[]);
+ method public void setTrustManagers(javax.net.ssl.TrustManager[]);
}
public final class SSLSessionCache {
diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java
index 3626bf5..7fd5a7d 100644
--- a/core/java/android/app/WallpaperManager.java
+++ b/core/java/android/app/WallpaperManager.java
@@ -676,7 +676,8 @@
bm.setDensity(DisplayMetrics.DENSITY_DEVICE);
- if (bm.getWidth() == width && bm.getHeight() == height) {
+ if (width <= 0 || height <= 0
+ || (bm.getWidth() == width && bm.getHeight() == height)) {
return bm;
}
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
index 906a564..9bd45d3 100644
--- a/core/java/android/content/res/Configuration.java
+++ b/core/java/android/content/res/Configuration.java
@@ -277,24 +277,24 @@
public int compatSmallestScreenWidthDp;
/**
- * @hide
+ * @hide Do not use. Implementation not finished.
*/
- public static final int LAYOUT_DIRECTION_UNDEFINED = -1;
+ public static final int TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE = -1;
/**
- * @hide
+ * @hide Do not use. Implementation not finished.
*/
- public static final int LAYOUT_DIRECTION_LTR = 0;
+ public static final int TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE = 0;
/**
- * @hide
+ * @hide Do not use. Implementation not finished.
*/
- public static final int LAYOUT_DIRECTION_RTL = 1;
+ public static final int TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE = 1;
/**
- * @hide The layout direction associated to the current Locale
+ * @hide The text layout direction associated to the current Locale
*/
- public int layoutDirection;
+ public int textLayoutDirection;
/**
* @hide Internal book-keeping.
@@ -322,7 +322,7 @@
mnc = o.mnc;
if (o.locale != null) {
locale = (Locale) o.locale.clone();
- layoutDirection = o.layoutDirection;
+ textLayoutDirection = o.textLayoutDirection;
}
userSetLocale = o.userSetLocale;
touchscreen = o.touchscreen;
@@ -358,6 +358,11 @@
} else {
sb.append(" (no locale)");
}
+ switch (textLayoutDirection) {
+ case TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE: sb.append(" ?layoutdir"); break;
+ case TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE: sb.append(" rtl"); break;
+ default: sb.append(" layoutdir="); sb.append(textLayoutDirection); break;
+ }
if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) {
sb.append(" sw"); sb.append(smallestScreenWidthDp); sb.append("dp");
} else {
@@ -450,11 +455,6 @@
case NAVIGATIONHIDDEN_YES: sb.append("/h"); break;
default: sb.append("/"); sb.append(navigationHidden); break;
}
- switch (layoutDirection) {
- case LAYOUT_DIRECTION_UNDEFINED: sb.append(" ?layoutdir"); break;
- case LAYOUT_DIRECTION_LTR: sb.append(" ltr"); break;
- case LAYOUT_DIRECTION_RTL: sb.append(" rtl"); break;
- }
if (seq != 0) {
sb.append(" s.");
sb.append(seq);
@@ -483,8 +483,8 @@
screenWidthDp = compatScreenWidthDp = SCREEN_WIDTH_DP_UNDEFINED;
screenHeightDp = compatScreenHeightDp = SCREEN_HEIGHT_DP_UNDEFINED;
smallestScreenWidthDp = compatSmallestScreenWidthDp = SMALLEST_SCREEN_WIDTH_DP_UNDEFINED;
+ textLayoutDirection = TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
seq = 0;
- layoutDirection = LAYOUT_DIRECTION_LTR;
}
/** {@hide} */
@@ -519,7 +519,7 @@
changed |= ActivityInfo.CONFIG_LOCALE;
locale = delta.locale != null
? (Locale) delta.locale.clone() : null;
- layoutDirection = getLayoutDirectionFromLocale(locale);
+ textLayoutDirection = getLayoutDirectionFromLocale(locale);
}
if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
{
@@ -611,23 +611,25 @@
/**
* Return the layout direction for a given Locale
* @param locale the Locale for which we want the layout direction. Can be null.
- * @return the layout direction. This may be one of {@link #LAYOUT_DIRECTION_UNDEFINED},
- * {@link #LAYOUT_DIRECTION_LTR} or {@link #LAYOUT_DIRECTION_RTL}.
+ * @return the layout direction. This may be one of:
+ * {@link #TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE} or
+ * {@link #TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE} or
+ * {@link #TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE}.
*
* @hide
*/
public static int getLayoutDirectionFromLocale(Locale locale) {
- if (locale == null || locale.equals(Locale.ROOT)) return LAYOUT_DIRECTION_UNDEFINED;
+ if (locale == null || locale.equals(Locale.ROOT)) return TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE;
// Be careful: this code will need to be changed when vertical scripts will be supported
// OR if ICU4C is updated to have the "likelySubtags" file
switch(Character.getDirectionality(locale.getDisplayName(locale).charAt(0))) {
case Character.DIRECTIONALITY_LEFT_TO_RIGHT:
- return LAYOUT_DIRECTION_LTR;
+ return TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE;
case Character.DIRECTIONALITY_RIGHT_TO_LEFT:
case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC:
- return LAYOUT_DIRECTION_RTL;
+ return TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE;
default:
- return LAYOUT_DIRECTION_UNDEFINED;
+ return TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE;
}
}
@@ -810,7 +812,7 @@
dest.writeInt(compatScreenWidthDp);
dest.writeInt(compatScreenHeightDp);
dest.writeInt(compatSmallestScreenWidthDp);
- dest.writeInt(layoutDirection);
+ dest.writeInt(textLayoutDirection);
dest.writeInt(seq);
}
@@ -838,7 +840,7 @@
compatScreenWidthDp = source.readInt();
compatScreenHeightDp = source.readInt();
compatSmallestScreenWidthDp = source.readInt();
- layoutDirection = source.readInt();
+ textLayoutDirection = source.readInt();
seq = source.readInt();
}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index c9238eb..82495e3 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -34,7 +34,7 @@
void registerListener(INetworkPolicyListener listener);
void unregisterListener(INetworkPolicyListener listener);
- void setNetworkPolicy(int networkType, String subscriberId, in NetworkPolicy policy);
- NetworkPolicy getNetworkPolicy(int networkType, String subscriberId);
+ void setNetworkPolicies(in NetworkPolicy[] policies);
+ NetworkPolicy[] getNetworkPolicies();
}
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index b9909c3..1899281 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -26,17 +26,27 @@
* @hide
*/
public class NetworkPolicy implements Parcelable, Comparable<NetworkPolicy> {
- public final int cycleDay;
- public final long warningBytes;
- public final long limitBytes;
+ public final int networkTemplate;
+ public final String subscriberId;
+ public int cycleDay;
+ public long warningBytes;
+ public long limitBytes;
- public NetworkPolicy(int cycleDay, long warningBytes, long limitBytes) {
+ public static final long WARNING_DISABLED = -1;
+ public static final long LIMIT_DISABLED = -1;
+
+ public NetworkPolicy(int networkTemplate, String subscriberId, int cycleDay, long warningBytes,
+ long limitBytes) {
+ this.networkTemplate = networkTemplate;
+ this.subscriberId = subscriberId;
this.cycleDay = cycleDay;
this.warningBytes = warningBytes;
this.limitBytes = limitBytes;
}
public NetworkPolicy(Parcel in) {
+ networkTemplate = in.readInt();
+ subscriberId = in.readString();
cycleDay = in.readInt();
warningBytes = in.readLong();
limitBytes = in.readLong();
@@ -44,6 +54,8 @@
/** {@inheritDoc} */
public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(networkTemplate);
+ dest.writeString(subscriberId);
dest.writeInt(cycleDay);
dest.writeLong(warningBytes);
dest.writeLong(limitBytes);
@@ -56,17 +68,21 @@
/** {@inheritDoc} */
public int compareTo(NetworkPolicy another) {
- if (another == null || limitBytes < another.limitBytes) {
+ if (another == null || another.limitBytes == LIMIT_DISABLED) {
+ // other value is missing or disabled; we win
return -1;
- } else {
+ }
+ if (limitBytes == LIMIT_DISABLED || another.limitBytes < limitBytes) {
+ // we're disabled or other limit is smaller; they win
return 1;
}
+ return 0;
}
@Override
public String toString() {
- return "NetworkPolicy: cycleDay=" + cycleDay + ", warningBytes=" + warningBytes
- + ", limitBytes=" + limitBytes;
+ return "NetworkPolicy: networkTemplate=" + networkTemplate + ", cycleDay=" + cycleDay
+ + ", warningBytes=" + warningBytes + ", limitBytes=" + limitBytes;
}
public static final Creator<NetworkPolicy> CREATOR = new Creator<NetworkPolicy>() {
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 08b1a81..13ece40 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -55,17 +55,17 @@
}
/** {@hide} */
- public void setNetworkPolicy(int networkType, String subscriberId, NetworkPolicy policy) {
+ public void setNetworkPolicies(NetworkPolicy[] policies) {
try {
- mService.setNetworkPolicy(networkType, subscriberId, policy);
+ mService.setNetworkPolicies(policies);
} catch (RemoteException e) {
}
}
/** {@hide} */
- public NetworkPolicy getNetworkPolicy(int networkType, String subscriberId) {
+ public NetworkPolicy[] getNetworkPolicies() {
try {
- return mService.getNetworkPolicy(networkType, subscriberId);
+ return mService.getNetworkPolicies();
} catch (RemoteException e) {
return null;
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 6354e9a..60f740e 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -23,6 +23,7 @@
import java.io.CharArrayWriter;
import java.io.PrintWriter;
+import java.util.Arrays;
import java.util.HashSet;
/**
@@ -48,74 +49,60 @@
* generated.
*/
public final long elapsedRealtime;
- public final String[] iface;
- public final int[] uid;
- public final long[] rx;
- public final long[] tx;
+ public int size;
+ public String[] iface;
+ public int[] uid;
+ public long[] rx;
+ public long[] tx;
// TODO: add fg/bg stats once reported by kernel
- private NetworkStats(long elapsedRealtime, String[] iface, int[] uid, long[] rx, long[] tx) {
+ public NetworkStats(long elapsedRealtime, int initialSize) {
this.elapsedRealtime = elapsedRealtime;
- this.iface = iface;
- this.uid = uid;
- this.rx = rx;
- this.tx = tx;
+ this.size = 0;
+ this.iface = new String[initialSize];
+ this.uid = new int[initialSize];
+ this.rx = new long[initialSize];
+ this.tx = new long[initialSize];
}
public NetworkStats(Parcel parcel) {
elapsedRealtime = parcel.readLong();
+ size = parcel.readInt();
iface = parcel.createStringArray();
uid = parcel.createIntArray();
rx = parcel.createLongArray();
tx = parcel.createLongArray();
}
- public static class Builder {
- private long mElapsedRealtime;
- private final String[] mIface;
- private final int[] mUid;
- private final long[] mRx;
- private final long[] mTx;
-
- private int mIndex = 0;
-
- public Builder(long elapsedRealtime, int size) {
- mElapsedRealtime = elapsedRealtime;
- mIface = new String[size];
- mUid = new int[size];
- mRx = new long[size];
- mTx = new long[size];
+ public NetworkStats addEntry(String iface, int uid, long rx, long tx) {
+ if (size >= this.iface.length) {
+ final int newLength = Math.max(this.iface.length, 10) * 3 / 2;
+ this.iface = Arrays.copyOf(this.iface, newLength);
+ this.uid = Arrays.copyOf(this.uid, newLength);
+ this.rx = Arrays.copyOf(this.rx, newLength);
+ this.tx = Arrays.copyOf(this.tx, newLength);
}
- public Builder addEntry(String iface, int uid, long rx, long tx) {
- mIface[mIndex] = iface;
- mUid[mIndex] = uid;
- mRx[mIndex] = rx;
- mTx[mIndex] = tx;
- mIndex++;
- return this;
- }
+ this.iface[size] = iface;
+ this.uid[size] = uid;
+ this.rx[size] = rx;
+ this.tx[size] = tx;
+ size++;
- public NetworkStats build() {
- if (mIndex != mIface.length) {
- throw new IllegalArgumentException("unexpected number of entries");
- }
- return new NetworkStats(mElapsedRealtime, mIface, mUid, mRx, mTx);
- }
+ return this;
}
+ @Deprecated
public int length() {
- // length is identical for all fields
- return iface.length;
+ return size;
}
/**
* Find first stats index that matches the requested parameters.
*/
public int findIndex(String iface, int uid) {
- final int length = length();
- for (int i = 0; i < length; i++) {
+ for (int i = 0; i < size; i++) {
if (equal(iface, this.iface[i]) && uid == this.uid[i]) {
return i;
}
@@ -195,9 +182,8 @@
}
// result will have our rows, and elapsed time between snapshots
- final int length = length();
- final NetworkStats.Builder result = new NetworkStats.Builder(deltaRealtime, length);
- for (int i = 0; i < length; i++) {
+ final NetworkStats result = new NetworkStats(deltaRealtime, size);
+ for (int i = 0; i < size; i++) {
final String iface = this.iface[i];
final int uid = this.uid[i];
@@ -221,7 +207,7 @@
}
}
- return result.build();
+ return result;
}
private static boolean equal(Object a, Object b) {
@@ -255,6 +241,7 @@
/** {@inheritDoc} */
public void writeToParcel(Parcel dest, int flags) {
dest.writeLong(elapsedRealtime);
+ dest.writeInt(size);
dest.writeStringArray(iface);
dest.writeIntArray(uid);
dest.writeLongArray(rx);
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index a697e96..5fa8e21 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -53,11 +53,15 @@
public long[] tx;
public NetworkStatsHistory(long bucketDuration) {
+ this(bucketDuration, 10);
+ }
+
+ public NetworkStatsHistory(long bucketDuration, int initialSize) {
this.bucketDuration = bucketDuration;
- bucketStart = new long[0];
- rx = new long[0];
- tx = new long[0];
- bucketCount = bucketStart.length;
+ bucketStart = new long[initialSize];
+ rx = new long[initialSize];
+ tx = new long[initialSize];
+ bucketCount = 0;
}
public NetworkStatsHistory(Parcel in) {
@@ -168,8 +172,8 @@
*/
private void insertBucket(int index, long start) {
// create more buckets when needed
- if (bucketCount + 1 > bucketStart.length) {
- final int newLength = bucketStart.length + 10;
+ if (bucketCount >= bucketStart.length) {
+ final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
bucketStart = Arrays.copyOf(bucketStart, newLength);
rx = Arrays.copyOf(rx, newLength);
tx = Arrays.copyOf(tx, newLength);
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 8e50cd5..5c4b258 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -240,7 +240,6 @@
/**
* Sets the {@link TrustManager}s to be used for connections made by this factory.
- * @hide
*/
public void setTrustManagers(TrustManager[] trustManager) {
mTrustManagers = trustManager;
@@ -253,7 +252,6 @@
/**
* Sets the {@link KeyManager}s to be used for connections made by this factory.
- * @hide
*/
public void setKeyManagers(KeyManager[] keyManagers) {
mKeyManagers = keyManagers;
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 8a688d5..3725fa6 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -41,11 +41,9 @@
*/
public final static int UNSUPPORTED = -1;
- // TODO: find better home for these template constants
-
/**
* Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
- * networks together. Only uses statistics for currently active IMSI.
+ * networks together. Only uses statistics for requested IMSI.
*
* @hide
*/
@@ -54,7 +52,7 @@
/**
* Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
* networks together that roughly meet a "3G" definition, or lower. Only
- * uses statistics for currently active IMSI.
+ * uses statistics for requested IMSI.
*
* @hide
*/
@@ -63,7 +61,7 @@
/**
* Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
* networks together that meet a "4G" definition. Only uses statistics for
- * currently active IMSI.
+ * requested IMSI.
*
* @hide
*/
@@ -184,6 +182,17 @@
}
}
+ /** {@hide} */
+ public static boolean isNetworkTemplateMobile(int networkTemplate) {
+ switch (networkTemplate) {
+ case TEMPLATE_MOBILE_3G_LOWER:
+ case TEMPLATE_MOBILE_4G:
+ case TEMPLATE_MOBILE_ALL:
+ return true;
+ }
+ return false;
+ }
+
/**
* Get the total number of packets transmitted through the mobile interface.
*
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index b66035f..aa5937e 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -43,7 +43,6 @@
int formatNdef(int nativeHandle, in byte[] key);
Tag rediscover(int nativehandle);
- void setIsoDepTimeout(int timeout);
- void setFelicaTimeout(int timeout);
+ int setTimeout(int technology, int timeout);
void resetTimeouts();
}
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 38b2bbd..d02086f 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -16,6 +16,7 @@
package android.nfc.tech;
+import android.nfc.ErrorCodes;
import android.nfc.Tag;
import android.os.Bundle;
import android.os.RemoteException;
@@ -90,7 +91,10 @@
*/
public void setTimeout(int timeout) {
try {
- mTag.getTagService().setIsoDepTimeout(timeout);
+ int err = mTag.getTagService().setTimeout(TagTechnology.ISO_DEP, timeout);
+ if (err != ErrorCodes.SUCCESS) {
+ throw new IllegalArgumentException("The supplied timeout is not valid");
+ }
} catch (RemoteException e) {
Log.e(TAG, "NFC service dead", e);
}
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index c4b7718..5cafe5b 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -16,9 +16,11 @@
package android.nfc.tech;
+import android.nfc.ErrorCodes;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.os.RemoteException;
+import android.util.Log;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -69,6 +71,8 @@
* require the {@link android.Manifest.permission#NFC} permission.
*/
public final class MifareClassic extends BasicTagTechnology {
+ private static final String TAG = "NFC";
+
/**
* The default factory key.
*/
@@ -568,6 +572,31 @@
return transceive(data, true);
}
+ /**
+ * Set the timeout of {@link #transceive} in milliseconds.
+ * <p>The timeout only applies to MifareUltralight {@link #transceive},
+ * and is reset to a default value when {@link #close} is called.
+ * <p>Setting a longer timeout may be useful when performing
+ * transactions that require a long processing time on the tag
+ * such as key generation.
+ *
+ * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param timeout timeout value in milliseconds
+ * @hide
+ */
+ // TODO Unhide for ICS
+ public void setTimeout(int timeout) {
+ try {
+ int err = mTag.getTagService().setTimeout(TagTechnology.MIFARE_CLASSIC, timeout);
+ if (err != ErrorCodes.SUCCESS) {
+ throw new IllegalArgumentException("The supplied timeout is not valid");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ }
+ }
+
private static void validateSector(int sector) {
// Do not be too strict on upper bounds checking, since some cards
// have more addressable memory than they report. For example,
diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java
index 6c2754b..3d4cdd1 100644
--- a/core/java/android/nfc/tech/MifareUltralight.java
+++ b/core/java/android/nfc/tech/MifareUltralight.java
@@ -16,10 +16,12 @@
package android.nfc.tech;
+import android.nfc.ErrorCodes;
import android.nfc.Tag;
import android.nfc.TagLostException;
import android.os.Bundle;
import android.os.RemoteException;
+import android.util.Log;
import java.io.IOException;
@@ -57,6 +59,8 @@
* require the {@link android.Manifest.permission#NFC} permission.
*/
public final class MifareUltralight extends BasicTagTechnology {
+ private static final String TAG = "NFC";
+
/** A MIFARE Ultralight compatible tag of unknown type */
public static final int TYPE_UNKNOWN = -1;
/** A MIFARE Ultralight tag */
@@ -208,6 +212,32 @@
return transceive(data, true);
}
+ /**
+ * Set the timeout of {@link #transceive} in milliseconds.
+ * <p>The timeout only applies to MifareUltralight {@link #transceive},
+ * and is reset to a default value when {@link #close} is called.
+ * <p>Setting a longer timeout may be useful when performing
+ * transactions that require a long processing time on the tag
+ * such as key generation.
+ *
+ * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param timeout timeout value in milliseconds
+ * @hide
+ */
+ // TODO Unhide for ICS
+ public void setTimeout(int timeout) {
+ try {
+ int err = mTag.getTagService().setTimeout(
+ TagTechnology.MIFARE_ULTRALIGHT, timeout);
+ if (err != ErrorCodes.SUCCESS) {
+ throw new IllegalArgumentException("The supplied timeout is not valid");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ }
+ }
+
private static void validatePageIndex(int pageIndex) {
// Do not be too strict on upper bounds checking, since some cards
// may have more addressable memory than they report.
diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java
index 1843eae..08095e6 100644
--- a/core/java/android/nfc/tech/NfcA.java
+++ b/core/java/android/nfc/tech/NfcA.java
@@ -16,9 +16,11 @@
package android.nfc.tech;
+import android.nfc.ErrorCodes;
import android.nfc.Tag;
import android.os.Bundle;
import android.os.RemoteException;
+import android.util.Log;
import java.io.IOException;
@@ -33,6 +35,8 @@
* require the {@link android.Manifest.permission#NFC} permission.
*/
public final class NfcA extends BasicTagTechnology {
+ private static final String TAG = "NFC";
+
/** @hide */
public static final String EXTRA_SAK = "sak";
/** @hide */
@@ -112,4 +116,29 @@
public byte[] transceive(byte[] data) throws IOException {
return transceive(data, true);
}
+
+ /**
+ * Set the timeout of {@link #transceive} in milliseconds.
+ * <p>The timeout only applies to NfcA {@link #transceive}, and is
+ * reset to a default value when {@link #close} is called.
+ * <p>Setting a longer timeout may be useful when performing
+ * transactions that require a long processing time on the tag
+ * such as key generation.
+ *
+ * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+ *
+ * @param timeout timeout value in milliseconds
+ * @hide
+ */
+ // TODO Unhide for ICS
+ public void setTimeout(int timeout) {
+ try {
+ int err = mTag.getTagService().setTimeout(TagTechnology.NFC_A, timeout);
+ if (err != ErrorCodes.SUCCESS) {
+ throw new IllegalArgumentException("The supplied timeout is not valid");
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "NFC service dead", e);
+ }
+ }
}
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
index 250c9b3..85abf89 100644
--- a/core/java/android/nfc/tech/NfcF.java
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -16,6 +16,7 @@
package android.nfc.tech;
+import android.nfc.ErrorCodes;
import android.nfc.Tag;
import android.os.Bundle;
import android.os.RemoteException;
@@ -131,7 +132,10 @@
// TODO Unhide for ICS
public void setTimeout(int timeout) {
try {
- mTag.getTagService().setFelicaTimeout(timeout);
+ int err = mTag.getTagService().setTimeout(TagTechnology.NFC_F, timeout);
+ if (err != ErrorCodes.SUCCESS) {
+ throw new IllegalArgumentException("The supplied timeout is not valid");
+ }
} catch (RemoteException e) {
Log.e(TAG, "NFC service dead", e);
}
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index bb6ed9c..3b2e6b6 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -178,9 +178,8 @@
* have specific uses which are expected to be consistent by the app and
* sync adapter.
*
- * @hide
*/
- public interface SyncColumns extends CalendarSyncColumns {
+ protected interface SyncColumns extends CalendarSyncColumns {
/**
* The account that was used to sync the entry to the device. If the
* account_type is not {@link #ACCOUNT_TYPE_LOCAL} then the name and
@@ -223,7 +222,7 @@
/**
* If set to 1 this causes events on this calendar to be duplicated with
- * {@link EventsColumns#LAST_SYNCED} set to 1 whenever the event
+ * {@link Events#LAST_SYNCED} set to 1 whenever the event
* transitions from non-dirty to dirty. The duplicated event will not be
* expanded in the instances table and will only show up in sync adapter
* queries of the events table. It will also be deleted when the
@@ -236,7 +235,7 @@
/**
* Columns specific to the Calendars Uri that other Uris can query.
*/
- private interface CalendarsColumns {
+ protected interface CalendarsColumns {
/**
* The color of the calendar
* <P>Type: INTEGER (color value)</P>
@@ -549,7 +548,7 @@
/**
* Columns from the Attendees table that other tables join into themselves.
*/
- private interface AttendeesColumns {
+ protected interface AttendeesColumns {
/**
* The id of the event. Column name.
@@ -639,7 +638,7 @@
/**
* Columns from the Events table that other tables join into themselves.
*/
- private interface EventsColumns {
+ protected interface EventsColumns {
/**
* The {@link Calendars#_ID} of the calendar the event belongs to.
@@ -1525,7 +1524,7 @@
* time zone for the instaces. These settings are stored using a key/value
* scheme.
*/
- private interface CalendarCacheColumns {
+ protected interface CalendarCacheColumns {
/**
* The key for the setting. Keys are defined in {@link CalendarCache}.
*/
@@ -1597,7 +1596,7 @@
* the Instances table and these are all stored in the first (and only)
* row of the CalendarMetaData table.
*/
- private interface CalendarMetaDataColumns {
+ protected interface CalendarMetaDataColumns {
/**
* The local timezone that was used for precomputing the fields
* in the Instances table.
@@ -1637,7 +1636,7 @@
public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns {
}
- private interface EventDaysColumns {
+ protected interface EventDaysColumns {
/**
* The Julian starting day number. Column name.
* <P>Type: INTEGER (int)</P>
@@ -1655,7 +1654,7 @@
* Fields and helpers for querying for a list of days that contain events.
*/
public static final class EventDays implements EventDaysColumns {
- private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+ "/instances/groupbyday");
/**
@@ -1690,7 +1689,7 @@
}
}
- private interface RemindersColumns {
+ protected interface RemindersColumns {
/**
* The event the reminder belongs to. Column name.
* <P>Type: INTEGER (foreign key to the Events table)</P>
@@ -1755,7 +1754,7 @@
}
}
- private interface CalendarAlertsColumns {
+ protected interface CalendarAlertsColumns {
/**
* The event that the alert belongs to. Column name.
* <P>Type: INTEGER (foreign key to the Events table)</P>
@@ -2069,7 +2068,7 @@
}
}
- private interface ExtendedPropertiesColumns {
+ protected interface ExtendedPropertiesColumns {
/**
* The event the extended property belongs to. Column name.
* <P>Type: INTEGER (foreign key to the Events table)</P>
@@ -2128,7 +2127,7 @@
/**
* Columns from the EventsRawTimes table
*/
- private interface EventsRawTimesColumns {
+ protected interface EventsRawTimesColumns {
/**
* The corresponding event id. Column name.
* <P>Type: INTEGER (long)</P>
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6ab7738..1025b20 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3800,13 +3800,13 @@
/** {@hide} */
public static final String NETSTATS_PERSIST_THRESHOLD = "netstats_persist_threshold";
/** {@hide} */
- public static final String NETSTATS_SUMMARY_BUCKET_DURATION = "netstats_summary_bucket_duration";
+ public static final String NETSTATS_NETWORK_BUCKET_DURATION = "netstats_network_bucket_duration";
/** {@hide} */
- public static final String NETSTATS_SUMMARY_MAX_HISTORY = "netstats_summary_max_history";
+ public static final String NETSTATS_NETWORK_MAX_HISTORY = "netstats_network_max_history";
/** {@hide} */
- public static final String NETSTATS_DETAIL_BUCKET_DURATION = "netstats_detail_bucket_duration";
+ public static final String NETSTATS_UID_BUCKET_DURATION = "netstats_uid_bucket_duration";
/** {@hide} */
- public static final String NETSTATS_DETAIL_MAX_HISTORY = "netstats_detail_max_history";
+ public static final String NETSTATS_UID_MAX_HISTORY = "netstats_uid_max_history";
/**
* @hide
diff --git a/core/java/android/provider/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java
index c397af9..376e0bb 100644
--- a/core/java/android/provider/VoicemailContract.java
+++ b/core/java/android/provider/VoicemailContract.java
@@ -19,6 +19,7 @@
import android.database.ContentObserver;
import android.net.Uri;
import android.provider.CallLog.Calls;
+
/**
* The contract between the voicemail provider and applications. Contains
* definitions for the supported URIs and columns.
@@ -45,13 +46,17 @@
*/
// TODO: unhide when the API is approved by android-api-council
public class VoicemailContract {
+ /** Not instantiable. */
+ private VoicemailContract() {
+ }
+
/** The authority used by the voicemail provider. */
public static final String AUTHORITY = "com.android.voicemail";
/** URI to insert/retrieve all voicemails. */
public static final Uri CONTENT_URI =
Uri.parse("content://" + AUTHORITY + "/voicemail");
- /** URI to insert/retrieve voicemails by a given voicemai source. */
+ /** URI to insert/retrieve voicemails by a given voicemail source. */
public static final Uri CONTENT_URI_SOURCE =
Uri.parse("content://" + AUTHORITY + "/voicemail/source/");
@@ -72,6 +77,10 @@
"vnd.android.cursor.dir/voicemails";
public static final class Voicemails implements BaseColumns {
+ /** Not instantiable. */
+ private Voicemails() {
+ }
+
/**
* Phone number of the voicemail sender.
* <P>Type: TEXT</P>
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 44229a4..4cae9d8 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -253,18 +253,19 @@
mLayout.setVisibility(View.VISIBLE);
WebChromeClient client = webView.getWebChromeClient();
- client.onShowCustomView(mLayout, mCallback);
- // Plugins like Flash will draw over the video so hide
- // them while we're playing.
- if (webView.getViewManager() != null)
- webView.getViewManager().hideAll();
+ if (client != null) {
+ client.onShowCustomView(mLayout, mCallback);
+ // Plugins like Flash will draw over the video so hide
+ // them while we're playing.
+ if (webView.getViewManager() != null)
+ webView.getViewManager().hideAll();
- mProgressView = client.getVideoLoadingProgressView();
- if (mProgressView != null) {
- mLayout.addView(mProgressView, layoutParams);
- mProgressView.setVisibility(View.VISIBLE);
+ mProgressView = client.getVideoLoadingProgressView();
+ if (mProgressView != null) {
+ mLayout.addView(mProgressView, layoutParams);
+ mProgressView.setVisibility(View.VISIBLE);
+ }
}
-
}
/**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 95e4880..10c7390 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -5605,8 +5605,8 @@
* Adjustable parameters. Angle is the radians on a unit circle, limited
* to quadrant 1. Values range from 0f (horizontal) to PI/2 (vertical)
*/
- private static final float HSLOPE_TO_START_SNAP = .1f;
- private static final float HSLOPE_TO_BREAK_SNAP = .2f;
+ private static final float HSLOPE_TO_START_SNAP = .25f;
+ private static final float HSLOPE_TO_BREAK_SNAP = .4f;
private static final float VSLOPE_TO_START_SNAP = 1.25f;
private static final float VSLOPE_TO_BREAK_SNAP = .95f;
/*
@@ -5617,6 +5617,11 @@
*/
private static final float ANGLE_VERT = 2f;
private static final float ANGLE_HORIZ = 0f;
+ /*
+ * The modified moving average weight.
+ * Formula: MAV[t]=MAV[t-1] + (P[t]-MAV[t-1])/n
+ */
+ private static final float MMA_WEIGHT_N = 5;
private boolean hitFocusedPlugin(int contentX, int contentY) {
if (DebugFlags.WEB_VIEW) {
@@ -6003,8 +6008,9 @@
if (deltaX == 0 && deltaY == 0) {
keepScrollBarsVisible = done = true;
} else {
- mAverageAngle = (mAverageAngle +
- calculateDragAngle(deltaX, deltaY)) / 2;
+ mAverageAngle +=
+ (calculateDragAngle(deltaX, deltaY) - mAverageAngle)
+ / MMA_WEIGHT_N;
if (mSnapScrollMode != SNAP_NONE) {
if (mSnapScrollMode == SNAP_Y) {
// radical change means getting out of snap mode
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index bda82a3..092c2f7 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -494,6 +494,14 @@
requestLayout();
}
+ private static int max2(int[] a, int valueIfEmpty) {
+ int result = valueIfEmpty;
+ for (int i = 0, N = a.length; i < N; i++) {
+ result = Math.max(result, a[i]);
+ }
+ return result;
+ }
+
private static int sum(float[] a) {
int result = 0;
for (int i = 0, length = a.length; i < length; i++) {
@@ -1409,7 +1417,7 @@
// External entry points
private int size(int[] locations) {
- return locations[locations.length - 1] - locations[0];
+ return max2(locations, 0) - locations[0];
}
private int getMin() {
@@ -1878,21 +1886,13 @@
return result;
}
- private static int max(int[] a, int valueIfEmpty) {
- int result = valueIfEmpty;
- for (int i = 0, length = a.length; i < length; i++) {
- result = Math.max(result, a[i]);
- }
- return result;
- }
-
/*
Create a compact array of keys or values using the supplied index.
*/
private static <K> K[] compact(K[] a, int[] index) {
int size = a.length;
Class<?> componentType = a.getClass().getComponentType();
- K[] result = (K[]) Array.newInstance(componentType, max(index, -1) + 1);
+ K[] result = (K[]) Array.newInstance(componentType, max2(index, -1) + 1);
// this overwrite duplicates, retaining the last equivalent entry
for (int i = 0; i < size; i++) {
diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java
index 40b0a9c..4c47d37 100644
--- a/core/java/android/widget/RemoteViewsAdapter.java
+++ b/core/java/android/widget/RemoteViewsAdapter.java
@@ -711,6 +711,17 @@
requestBindService();
}
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (mWorkerThread != null) {
+ mWorkerThread.quit();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
private void loadNextIndexInBackground() {
mWorkerQueue.post(new Runnable() {
@Override
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
index c83f910..37bb522 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
@@ -50,8 +50,8 @@
android:layout_width="0dip"
android:gravity="center_horizontal|center_vertical">
- <com.android.internal.widget.WaveView
- android:id="@+id/unlock_widget"
+ <TextView
+ android:id="@+id/screenLocked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
@@ -75,7 +75,7 @@
<com.android.internal.widget.WaveView
- android:id="@+id/wave_view"
+ android:id="@+id/unlock_widget"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical|center_horizontal"
diff --git a/core/tests/coretests/src/android/content/res/ConfigurationTest.java b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
index f1f745e..54a5e4e 100644
--- a/core/tests/coretests/src/android/content/res/ConfigurationTest.java
+++ b/core/tests/coretests/src/android/content/res/ConfigurationTest.java
@@ -30,169 +30,169 @@
args = {Locale.class}
)
public void testGetLayoutDirectionFromLocale() {
- assertEquals(Configuration.LAYOUT_DIRECTION_UNDEFINED,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(null));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.ENGLISH));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.CANADA));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.CANADA_FRENCH));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.FRANCE));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.FRENCH));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.GERMAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.GERMANY));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.ITALIAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.ITALY));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.UK));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.US));
- assertEquals(Configuration.LAYOUT_DIRECTION_UNDEFINED,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_UNDEFINED_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.ROOT));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.CHINA));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.CHINESE));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.JAPAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.JAPANESE));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.KOREA));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.KOREAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.PRC));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.SIMPLIFIED_CHINESE));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.TAIWAN));
- assertEquals(Configuration.LAYOUT_DIRECTION_LTR,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_LTR_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(Locale.TRADITIONAL_CHINESE));
Locale locale = new Locale("ar");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "AE");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "BH");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "DZ");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "EG");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "IQ");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "JO");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "KW");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "LB");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "LY");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "MA");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "OM");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "QA");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "SA");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "SD");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "SY");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "TN");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ar", "YE");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("fa");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("fa", "AF");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("fa", "IR");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("iw");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("iw", "IL");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("he");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("he", "IL");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
// The following test will not pass until we are able to take care about the scrip subtag
// thru having the "likelySubTags" file into ICU4C
// locale = new Locale("pa_Arab");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// locale = new Locale("pa_Arab", "PK");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ps");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
locale = new Locale("ps", "AF");
- assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+ assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
Configuration.getLayoutDirectionFromLocale(locale));
// The following test will not work as the localized display name would be "Urdu" with ICU 4.4
// We will need ICU 4.6 to get the correct localized display name
// locale = new Locale("ur");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// locale = new Locale("ur", "IN");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// locale = new Locale("ur", "PK");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// The following test will not pass until we are able to take care about the scrip subtag
// thru having the "likelySubTags" file into ICU4C
// locale = new Locale("uz_Arab");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
// locale = new Locale("uz_Arab", "AF");
-// assertEquals(Configuration.LAYOUT_DIRECTION_RTL,
+// assertEquals(Configuration.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE,
// Configuration.getLayoutDirectionFromLocale(locale));
}
}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 8a3e871..5250a7c 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -16,7 +16,6 @@
package android.net;
-import android.os.SystemClock;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -25,12 +24,14 @@
public class NetworkStatsTest extends TestCase {
private static final String TEST_IFACE = "test0";
+ private static final int TEST_UID = 1001;
+ private static final long TEST_START = 1194220800000L;
public void testFindIndex() throws Exception {
- final NetworkStats stats = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+ final NetworkStats stats = new NetworkStats(TEST_START, 3)
.addEntry(TEST_IFACE, 100, 1024, 0)
.addEntry(TEST_IFACE, 101, 0, 1024)
- .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+ .addEntry(TEST_IFACE, 102, 1024, 1024);
assertEquals(2, stats.findIndex(TEST_IFACE, 102));
assertEquals(2, stats.findIndex(TEST_IFACE, 102));
@@ -38,14 +39,40 @@
assertEquals(-1, stats.findIndex(TEST_IFACE, 6));
}
- public void testSubtractIdenticalData() throws Exception {
- final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
- .addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024).build();
+ public void testAddEntryGrow() throws Exception {
+ final NetworkStats stats = new NetworkStats(TEST_START, 2);
- final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ assertEquals(0, stats.size);
+ assertEquals(2, stats.iface.length);
+
+ stats.addEntry(TEST_IFACE, TEST_UID, 1L, 2L);
+ stats.addEntry(TEST_IFACE, TEST_UID, 2L, 2L);
+
+ assertEquals(2, stats.size);
+ assertEquals(2, stats.iface.length);
+
+ stats.addEntry(TEST_IFACE, TEST_UID, 3L, 4L);
+ stats.addEntry(TEST_IFACE, TEST_UID, 4L, 4L);
+ stats.addEntry(TEST_IFACE, TEST_UID, 5L, 5L);
+
+ assertEquals(5, stats.size);
+ assertTrue(stats.iface.length >= 5);
+
+ assertEquals(1L, stats.rx[0]);
+ assertEquals(2L, stats.rx[1]);
+ assertEquals(3L, stats.rx[2]);
+ assertEquals(4L, stats.rx[3]);
+ assertEquals(5L, stats.rx[4]);
+ }
+
+ public void testSubtractIdenticalData() throws Exception {
+ final NetworkStats before = new NetworkStats(TEST_START, 2)
.addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024).build();
+ .addEntry(TEST_IFACE, 101, 0, 1024);
+
+ final NetworkStats after = new NetworkStats(TEST_START, 2)
+ .addEntry(TEST_IFACE, 100, 1024, 0)
+ .addEntry(TEST_IFACE, 101, 0, 1024);
final NetworkStats result = after.subtract(before);
@@ -57,13 +84,13 @@
}
public void testSubtractIdenticalRows() throws Exception {
- final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ final NetworkStats before = new NetworkStats(TEST_START, 2)
.addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024).build();
+ .addEntry(TEST_IFACE, 101, 0, 1024);
- final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ final NetworkStats after = new NetworkStats(TEST_START, 2)
.addEntry(TEST_IFACE, 100, 1025, 2)
- .addEntry(TEST_IFACE, 101, 3, 1028).build();
+ .addEntry(TEST_IFACE, 101, 3, 1028);
final NetworkStats result = after.subtract(before);
@@ -75,14 +102,14 @@
}
public void testSubtractNewRows() throws Exception {
- final NetworkStats before = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 2)
+ final NetworkStats before = new NetworkStats(TEST_START, 2)
.addEntry(TEST_IFACE, 100, 1024, 0)
- .addEntry(TEST_IFACE, 101, 0, 1024).build();
+ .addEntry(TEST_IFACE, 101, 0, 1024);
- final NetworkStats after = new NetworkStats.Builder(SystemClock.elapsedRealtime(), 3)
+ final NetworkStats after = new NetworkStats(TEST_START, 3)
.addEntry(TEST_IFACE, 100, 1024, 0)
.addEntry(TEST_IFACE, 101, 0, 1024)
- .addEntry(TEST_IFACE, 102, 1024, 1024).build();
+ .addEntry(TEST_IFACE, 102, 1024, 1024);
final NetworkStats result = after.subtract(before);
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 2b31462..9294df6 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -188,6 +188,11 @@
status_t setBufferCountServerLocked(int bufferCount);
+ // computeCurrentTransformMatrix computes the transform matrix for the
+ // current texture. It uses mCurrentTransform and the current GraphicBuffer
+ // to compute this matrix and stores it in mCurrentTransformMatrix.
+ void computeCurrentTransformMatrix();
+
enum { INVALID_BUFFER_SLOT = -1 };
struct BufferSlot {
@@ -288,9 +293,9 @@
// by calling setBufferCount or setBufferCountServer
int mBufferCount;
- // mRequestedBufferCount is the number of buffer slots requested by the
- // client. The default is zero, which means the client doesn't care how
- // many buffers there is.
+ // mClientBufferCount is the number of buffer slots requested by the client.
+ // The default is zero, which means the client doesn't care how many buffers
+ // there is.
int mClientBufferCount;
// mServerBufferCount buffer count requested by the server-side
@@ -322,6 +327,11 @@
// gets set to mLastQueuedTransform each time updateTexImage is called.
uint32_t mCurrentTransform;
+ // mCurrentTransformMatrix is the transform matrix for the current texture.
+ // It gets computed by computeTransformMatrix each time updateTexImage is
+ // called.
+ float mCurrentTransformMatrix[16];
+
// mCurrentTimestamp is the timestamp for the current texture. It
// gets set to mLastQueuedTimestamp each time updateTexImage is called.
int64_t mCurrentTimestamp;
@@ -362,6 +372,7 @@
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
mutable Mutex mMutex;
+
};
// ----------------------------------------------------------------------------
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h
new file mode 100644
index 0000000..8f76d72
--- /dev/null
+++ b/include/utils/BlobCache.h
@@ -0,0 +1,181 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+#ifndef ANDROID_BLOB_CACHE_H
+#define ANDROID_BLOB_CACHE_H
+
+#include <stddef.h>
+
+#include <utils/RefBase.h>
+#include <utils/SortedVector.h>
+#include <utils/threads.h>
+
+namespace android {
+
+// A BlobCache is an in-memory cache for binary key/value pairs. All the public
+// methods are thread-safe.
+//
+// The cache contents can be serialized to a file and reloaded in a subsequent
+// execution of the program. This serialization is non-portable and should only
+// be loaded by the device that generated it.
+class BlobCache : public RefBase {
+public:
+
+ // Create an empty blob cache. The blob cache will cache key/value pairs
+ // with key and value sizes less than or equal to maxKeySize and
+ // maxValueSize, respectively. The total combined size of ALL cache entries
+ // (key sizes plus value sizes) will not exceed maxTotalSize.
+ BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize);
+
+ // set inserts a new binary value into the cache and associates it with the
+ // given binary key. If the key or value are too large for the cache then
+ // the cache remains unchanged. This includes the case where a different
+ // value was previously associated with the given key - the old value will
+ // remain in the cache. If the given key and value are small enough to be
+ // put in the cache (based on the maxKeySize, maxValueSize, and maxTotalSize
+ // values specified to the BlobCache constructor), then the key/value pair
+ // will be in the cache after set returns. Note, however, that a subsequent
+ // call to set may evict old key/value pairs from the cache.
+ //
+ // Preconditions:
+ // key != NULL
+ // 0 < keySize
+ // value != NULL
+ // 0 < valueSize
+ void set(const void* key, size_t keySize, const void* value,
+ size_t valueSize);
+
+ // The get function retrieves from the cache the binary value associated
+ // with a given binary key. If the key is present in the cache then the
+ // length of the binary value associated with that key is returned. If the
+ // value argument is non-NULL and the size of the cached value is less than
+ // valueSize bytes then the cached value is copied into the buffer pointed
+ // to by the value argument. If the key is not present in the cache then 0
+ // is returned and the buffer pointed to by the value argument is not
+ // modified.
+ //
+ // Note that when calling get multiple times with the same key, the later
+ // calls may fail, returning 0, even if earlier calls succeeded. The return
+ // value must be checked for each call.
+ //
+ // Preconditions:
+ // key != NULL
+ // 0 < keySize
+ // 0 <= valueSize
+ size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
+
+private:
+ // Copying is disallowed.
+ BlobCache(const BlobCache&);
+ void operator=(const BlobCache&);
+
+ // clean evicts a randomly chosen set of entries from the cache such that
+ // the total size of all remaining entries is less than mMaxTotalSize/2.
+ void clean();
+
+ // isCleanable returns true if the cache is full enough for the clean method
+ // to have some effect, and false otherwise.
+ bool isCleanable() const;
+
+ // A Blob is an immutable sized unstructured data blob.
+ class Blob : public RefBase {
+ public:
+ Blob(const void* data, size_t size, bool copyData);
+ ~Blob();
+
+ bool operator<(const Blob& rhs) const;
+
+ const void* getData() const;
+ size_t getSize() const;
+
+ private:
+ // Copying is not allowed.
+ Blob(const Blob&);
+ void operator=(const Blob&);
+
+ // mData points to the buffer containing the blob data.
+ const void* mData;
+
+ // mSize is the size of the blob data in bytes.
+ size_t mSize;
+
+ // mOwnsData indicates whether or not this Blob object should free the
+ // memory pointed to by mData when the Blob gets destructed.
+ bool mOwnsData;
+ };
+
+ // A CacheEntry is a single key/value pair in the cache.
+ class CacheEntry {
+ public:
+ CacheEntry();
+ CacheEntry(const sp<Blob>& key, const sp<Blob>& value);
+ CacheEntry(const CacheEntry& ce);
+
+ bool operator<(const CacheEntry& rhs) const;
+ const CacheEntry& operator=(const CacheEntry&);
+
+ sp<Blob> getKey() const;
+ sp<Blob> getValue() const;
+
+ void setValue(const sp<Blob>& value);
+
+ private:
+
+ // mKey is the key that identifies the cache entry.
+ sp<Blob> mKey;
+
+ // mValue is the cached data associated with the key.
+ sp<Blob> mValue;
+ };
+
+ // mMaxKeySize is the maximum key size that will be cached. Calls to
+ // BlobCache::set with a keySize parameter larger than mMaxKeySize will
+ // simply not add the key/value pair to the cache.
+ const size_t mMaxKeySize;
+
+ // mMaxValueSize is the maximum value size that will be cached. Calls to
+ // BlobCache::set with a valueSize parameter larger than mMaxValueSize will
+ // simply not add the key/value pair to the cache.
+ const size_t mMaxValueSize;
+
+ // mMaxTotalSize is the maximum size that all cache entries can occupy. This
+ // includes space for both keys and values. When a call to BlobCache::set
+ // would otherwise cause this limit to be exceeded, either the key/value
+ // pair passed to BlobCache::set will not be cached or other cache entries
+ // will be evicted from the cache to make room for the new entry.
+ const size_t mMaxTotalSize;
+
+ // mTotalSize is the total combined size of all keys and values currently in
+ // the cache.
+ size_t mTotalSize;
+
+ // mRandState is the pseudo-random number generator state. It is passed to
+ // nrand48 to generate random numbers when needed. It must be protected by
+ // mMutex.
+ unsigned short mRandState[3];
+
+ // mCacheEntries stores all the cache entries that are resident in memory.
+ // Cache entries are added to it by the 'set' method.
+ SortedVector<CacheEntry> mCacheEntries;
+
+ // mMutex is used to synchronize access to all member variables. It must be
+ // locked any time the member variables are written or read.
+ Mutex mMutex;
+};
+
+}
+
+#endif // ANDROID_BLOB_CACHE_H
diff --git a/include/utils/threads.h b/include/utils/threads.h
index 41e5766..0bd69cf 100644
--- a/include/utils/threads.h
+++ b/include/utils/threads.h
@@ -526,6 +526,7 @@
Thread& operator=(const Thread&);
static int _threadLoop(void* user);
const bool mCanCallJava;
+ // always hold mLock when reading or writing
thread_id_t mThread;
mutable Mutex mLock;
Condition mThreadExitedCondition;
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index ee97dcf..2cda4c8 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -96,6 +96,7 @@
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
mNextCrop.makeInvalid();
+ memcpy(mCurrentTransformMatrix, mtxIdentity, sizeof(mCurrentTransformMatrix));
}
SurfaceTexture::~SurfaceTexture() {
@@ -547,6 +548,7 @@
mCurrentCrop = mSlots[buf].mCrop;
mCurrentTransform = mSlots[buf].mTransform;
mCurrentTimestamp = mSlots[buf].mTimestamp;
+ computeCurrentTransformMatrix();
mDequeueCondition.signal();
} else {
// We always bind the texture even if we don't update its contents.
@@ -596,8 +598,12 @@
}
void SurfaceTexture::getTransformMatrix(float mtx[16]) {
- LOGV("SurfaceTexture::getTransformMatrix");
Mutex::Autolock lock(mMutex);
+ memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
+}
+
+void SurfaceTexture::computeCurrentTransformMatrix() {
+ LOGV("SurfaceTexture::computeCurrentTransformMatrix");
float xform[16];
for (int i = 0; i < 16; i++) {
@@ -684,7 +690,7 @@
// coordinate of 0, so SurfaceTexture must behave the same way. We don't
// want to expose this to applications, however, so we must add an
// additional vertical flip to the transform after all the other transforms.
- mtxMul(mtx, mtxFlipV, mtxBeforeFlipV);
+ mtxMul(mCurrentTransformMatrix, mtxFlipV, mtxBeforeFlipV);
}
nsecs_t SurfaceTexture::getTimestamp() {
diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp
index 2f704c8..da04b4a 100644
--- a/libs/gui/tests/SurfaceTextureClient_test.cpp
+++ b/libs/gui/tests/SurfaceTextureClient_test.cpp
@@ -514,4 +514,112 @@
thread->requestExitAndWait();
}
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ float mtx[16] = {};
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, st->updateTexImage());
+ st->getTransformMatrix(mtx);
+
+ EXPECT_EQ(1.f, mtx[0]);
+ EXPECT_EQ(0.f, mtx[1]);
+ EXPECT_EQ(0.f, mtx[2]);
+ EXPECT_EQ(0.f, mtx[3]);
+
+ EXPECT_EQ(0.f, mtx[4]);
+ EXPECT_EQ(-1.f, mtx[5]);
+ EXPECT_EQ(0.f, mtx[6]);
+ EXPECT_EQ(0.f, mtx[7]);
+
+ EXPECT_EQ(0.f, mtx[8]);
+ EXPECT_EQ(0.f, mtx[9]);
+ EXPECT_EQ(1.f, mtx[10]);
+ EXPECT_EQ(0.f, mtx[11]);
+
+ EXPECT_EQ(0.f, mtx[12]);
+ EXPECT_EQ(1.f, mtx[13]);
+ EXPECT_EQ(0.f, mtx[14]);
+ EXPECT_EQ(1.f, mtx[15]);
}
+
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ float mtx[16] = {};
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, st->updateTexImage());
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers
+ st->getTransformMatrix(mtx);
+
+ EXPECT_EQ(1.f, mtx[0]);
+ EXPECT_EQ(0.f, mtx[1]);
+ EXPECT_EQ(0.f, mtx[2]);
+ EXPECT_EQ(0.f, mtx[3]);
+
+ EXPECT_EQ(0.f, mtx[4]);
+ EXPECT_EQ(-1.f, mtx[5]);
+ EXPECT_EQ(0.f, mtx[6]);
+ EXPECT_EQ(0.f, mtx[7]);
+
+ EXPECT_EQ(0.f, mtx[8]);
+ EXPECT_EQ(0.f, mtx[9]);
+ EXPECT_EQ(1.f, mtx[10]);
+ EXPECT_EQ(0.f, mtx[11]);
+
+ EXPECT_EQ(0.f, mtx[12]);
+ EXPECT_EQ(1.f, mtx[13]);
+ EXPECT_EQ(0.f, mtx[14]);
+ EXPECT_EQ(1.f, mtx[15]);
+}
+
+TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) {
+ sp<ANativeWindow> anw(mSTC);
+ sp<SurfaceTexture> st(mST);
+ android_native_buffer_t* buf[3];
+ float mtx[16] = {};
+ android_native_rect_t crop;
+ crop.left = 0;
+ crop.top = 0;
+ crop.right = 5;
+ crop.bottom = 5;
+
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4));
+ ASSERT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 8, 8, 0));
+ ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0]));
+ ASSERT_EQ(OK, native_window_set_crop(anw.get(), &crop));
+ ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0]));
+ ASSERT_EQ(OK, st->updateTexImage());
+ ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers
+ st->getTransformMatrix(mtx);
+
+ // This accounts for the 1 texel shrink for each edge that's included in the
+ // transform matrix to avoid texturing outside the crop region.
+ EXPECT_EQ(.5f, mtx[0]);
+ EXPECT_EQ(0.f, mtx[1]);
+ EXPECT_EQ(0.f, mtx[2]);
+ EXPECT_EQ(0.f, mtx[3]);
+
+ EXPECT_EQ(0.f, mtx[4]);
+ EXPECT_EQ(-.5f, mtx[5]);
+ EXPECT_EQ(0.f, mtx[6]);
+ EXPECT_EQ(0.f, mtx[7]);
+
+ EXPECT_EQ(0.f, mtx[8]);
+ EXPECT_EQ(0.f, mtx[9]);
+ EXPECT_EQ(1.f, mtx[10]);
+ EXPECT_EQ(0.f, mtx[11]);
+
+ EXPECT_EQ(0.f, mtx[12]);
+ EXPECT_EQ(.5f, mtx[13]);
+ EXPECT_EQ(0.f, mtx[14]);
+ EXPECT_EQ(1.f, mtx[15]);
+}
+
+} // namespace android
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 8747ba5..16280d2 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -14,11 +14,14 @@
* limitations under the License.
*/
+//#define LOG_NDEBUG 0
+
#include <gtest/gtest.h>
#include <gui/SurfaceTexture.h>
#include <gui/SurfaceTextureClient.h>
#include <ui/GraphicBuffer.h>
#include <utils/String8.h>
+#include <utils/threads.h>
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/Surface.h>
@@ -618,4 +621,269 @@
}
}
+/*
+ * This test is for testing GL -> GL texture streaming via SurfaceTexture. It
+ * contains functionality to create a producer thread that will perform GL
+ * rendering to an ANativeWindow that feeds frames to a SurfaceTexture.
+ * Additionally it supports interlocking the producer and consumer threads so
+ * that a specific sequence of calls can be deterministically created by the
+ * test.
+ *
+ * The intended usage is as follows:
+ *
+ * TEST_F(...) {
+ * class PT : public ProducerThread {
+ * virtual void render() {
+ * ...
+ * swapBuffers();
+ * }
+ * };
+ *
+ * runProducerThread(new PT());
+ *
+ * // The order of these calls will vary from test to test and may include
+ * // multiple frames and additional operations (e.g. GL rendering from the
+ * // texture).
+ * fc->waitForFrame();
+ * mST->updateTexImage();
+ * fc->finishFrame();
+ * }
+ *
+ */
+class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
+protected:
+
+ // ProducerThread is an abstract base class to simplify the creation of
+ // OpenGL ES frame producer threads.
+ class ProducerThread : public Thread {
+ public:
+ virtual ~ProducerThread() {
+ }
+
+ void setEglObjects(EGLDisplay producerEglDisplay,
+ EGLSurface producerEglSurface,
+ EGLContext producerEglContext) {
+ mProducerEglDisplay = producerEglDisplay;
+ mProducerEglSurface = producerEglSurface;
+ mProducerEglContext = producerEglContext;
+ }
+
+ virtual bool threadLoop() {
+ eglMakeCurrent(mProducerEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext);
+ render();
+ eglMakeCurrent(mProducerEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE,
+ EGL_NO_CONTEXT);
+ return false;
+ }
+
+ protected:
+ virtual void render() = 0;
+
+ void swapBuffers() {
+ eglSwapBuffers(mProducerEglDisplay, mProducerEglSurface);
+ }
+
+ EGLDisplay mProducerEglDisplay;
+ EGLSurface mProducerEglSurface;
+ EGLContext mProducerEglContext;
+ };
+
+ // FrameCondition is a utility class for interlocking between the producer
+ // and consumer threads. The FrameCondition object should be created and
+ // destroyed in the consumer thread only. The consumer thread should set
+ // the FrameCondition as the FrameAvailableListener of the SurfaceTexture,
+ // and should call both waitForFrame and finishFrame once for each expected
+ // frame.
+ //
+ // This interlocking relies on the fact that onFrameAvailable gets called
+ // synchronously from SurfaceTexture::queueBuffer.
+ class FrameCondition : public SurfaceTexture::FrameAvailableListener {
+ public:
+ // waitForFrame waits for the next frame to arrive. This should be
+ // called from the consumer thread once for every frame expected by the
+ // test.
+ void waitForFrame() {
+ LOGV("+waitForFrame");
+ Mutex::Autolock lock(mMutex);
+ status_t result = mFrameAvailableCondition.wait(mMutex);
+ LOGV("-waitForFrame");
+ }
+
+ // Allow the producer to return from its swapBuffers call and continue
+ // on to produce the next frame. This should be called by the consumer
+ // thread once for every frame expected by the test.
+ void finishFrame() {
+ LOGV("+finishFrame");
+ Mutex::Autolock lock(mMutex);
+ mFrameFinishCondition.signal();
+ LOGV("-finishFrame");
+ }
+
+ // This should be called by SurfaceTexture on the producer thread.
+ virtual void onFrameAvailable() {
+ LOGV("+onFrameAvailable");
+ Mutex::Autolock lock(mMutex);
+ mFrameAvailableCondition.signal();
+ mFrameFinishCondition.wait(mMutex);
+ LOGV("-onFrameAvailable");
+ }
+
+ protected:
+ Mutex mMutex;
+ Condition mFrameAvailableCondition;
+ Condition mFrameFinishCondition;
+ };
+
+ SurfaceTextureGLToGLTest():
+ mProducerEglSurface(EGL_NO_SURFACE),
+ mProducerEglContext(EGL_NO_CONTEXT) {
+ }
+
+ virtual void SetUp() {
+ SurfaceTextureGLTest::SetUp();
+
+ EGLConfig myConfig = {0};
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
+ 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
+ mANW.get(), NULL);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
+
+ mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
+ EGL_NO_CONTEXT, getContextAttribs());
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
+
+ mFC = new FrameCondition();
+ mST->setFrameAvailableListener(mFC);
+ }
+
+ virtual void TearDown() {
+ if (mProducerThread != NULL) {
+ mProducerThread->requestExitAndWait();
+ }
+ if (mProducerEglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, mProducerEglContext);
+ }
+ if (mProducerEglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mProducerEglSurface);
+ }
+ mProducerThread.clear();
+ mFC.clear();
+ }
+
+ void runProducerThread(const sp<ProducerThread> producerThread) {
+ ASSERT_TRUE(mProducerThread == NULL);
+ mProducerThread = producerThread;
+ producerThread->setEglObjects(mEglDisplay, mProducerEglSurface,
+ mProducerEglContext);
+ producerThread->run();
+ }
+
+ EGLSurface mProducerEglSurface;
+ EGLContext mProducerEglContext;
+ sp<ProducerThread> mProducerThread;
+ sp<FrameCondition> mFC;
+};
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_UpdateTexImageBeforeFrameFinishedWorks) {
+ class PT : public ProducerThread {
+ virtual void render() {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ swapBuffers();
+ }
+ };
+
+ runProducerThread(new PT());
+
+ mFC->waitForFrame();
+ mST->updateTexImage();
+ mFC->finishFrame();
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}
+
+TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedWorks) {
+ class PT : public ProducerThread {
+ virtual void render() {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ swapBuffers();
+ }
+ };
+
+ runProducerThread(new PT());
+
+ mFC->waitForFrame();
+ mFC->finishFrame();
+ mST->updateTexImage();
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+}
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageBeforeFrameFinishedWorks) {
+ enum { NUM_ITERATIONS = 1024 };
+
+ class PT : public ProducerThread {
+ virtual void render() {
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ LOGV("+swapBuffers");
+ swapBuffers();
+ LOGV("-swapBuffers");
+ }
+ }
+ };
+
+ runProducerThread(new PT());
+
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ mFC->waitForFrame();
+ LOGV("+updateTexImage");
+ mST->updateTexImage();
+ LOGV("-updateTexImage");
+ mFC->finishFrame();
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+ }
+}
+
+// XXX: This test is disabled because it causes hangs on some devices.
+TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedUpdateTexImageAfterFrameFinishedWorks) {
+ enum { NUM_ITERATIONS = 1024 };
+
+ class PT : public ProducerThread {
+ virtual void render() {
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+ LOGV("+swapBuffers");
+ swapBuffers();
+ LOGV("-swapBuffers");
+ }
+ }
+ };
+
+ runProducerThread(new PT());
+
+ for (int i = 0; i < NUM_ITERATIONS; i++) {
+ mFC->waitForFrame();
+ mFC->finishFrame();
+ LOGV("+updateTexImage");
+ mST->updateTexImage();
+ LOGV("-updateTexImage");
+
+ // TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
+ }
+}
+
+} // namespace android
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index e8d40ba..093189c 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -21,6 +21,7 @@
Asset.cpp \
AssetDir.cpp \
AssetManager.cpp \
+ BlobCache.cpp \
BufferedTextOutput.cpp \
CallStack.cpp \
Debug.cpp \
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
new file mode 100644
index 0000000..1298fa7
--- /dev/null
+++ b/libs/utils/BlobCache.cpp
@@ -0,0 +1,232 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+#define LOG_TAG "BlobCache"
+//#define LOG_NDEBUG 0
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <utils/BlobCache.h>
+#include <utils/Log.h>
+
+namespace android {
+
+BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
+ mMaxKeySize(maxKeySize),
+ mMaxValueSize(maxValueSize),
+ mMaxTotalSize(maxTotalSize),
+ mTotalSize(0) {
+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC);
+ mRandState[0] = (now >> 0) & 0xFFFF;
+ mRandState[1] = (now >> 16) & 0xFFFF;
+ mRandState[2] = (now >> 32) & 0xFFFF;
+ LOGV("initializing random seed using %lld", now);
+}
+
+void BlobCache::set(const void* key, size_t keySize, const void* value,
+ size_t valueSize) {
+ if (mMaxKeySize < keySize) {
+ LOGV("set: not caching because the key is too large: %d (limit: %d)",
+ keySize, mMaxKeySize);
+ return;
+ }
+ if (mMaxValueSize < valueSize) {
+ LOGV("set: not caching because the value is too large: %d (limit: %d)",
+ valueSize, mMaxValueSize);
+ return;
+ }
+ if (mMaxTotalSize < keySize + valueSize) {
+ LOGV("set: not caching because the combined key/value size is too "
+ "large: %d (limit: %d)", keySize + valueSize, mMaxTotalSize);
+ return;
+ }
+ if (keySize == 0) {
+ LOGW("set: not caching because keySize is 0");
+ return;
+ }
+ if (valueSize <= 0) {
+ LOGW("set: not caching because valueSize is 0");
+ return;
+ }
+
+ Mutex::Autolock lock(mMutex);
+ sp<Blob> dummyKey(new Blob(key, keySize, false));
+ CacheEntry dummyEntry(dummyKey, NULL);
+
+ while (true) {
+
+ ssize_t index = mCacheEntries.indexOf(dummyEntry);
+ if (index < 0) {
+ // Create a new cache entry.
+ sp<Blob> keyBlob(new Blob(key, keySize, true));
+ sp<Blob> valueBlob(new Blob(value, valueSize, true));
+ size_t newTotalSize = mTotalSize + keySize + valueSize;
+ if (mMaxTotalSize < newTotalSize) {
+ if (isCleanable()) {
+ // Clean the cache and try again.
+ clean();
+ continue;
+ } else {
+ LOGV("set: not caching new key/value pair because the "
+ "total cache size limit would be exceeded: %d "
+ "(limit: %d)",
+ keySize + valueSize, mMaxTotalSize);
+ break;
+ }
+ }
+ mCacheEntries.add(CacheEntry(keyBlob, valueBlob));
+ mTotalSize = newTotalSize;
+ LOGV("set: created new cache entry with %d byte key and %d byte value",
+ keySize, valueSize);
+ } else {
+ // Update the existing cache entry.
+ sp<Blob> valueBlob(new Blob(value, valueSize, true));
+ sp<Blob> oldValueBlob(mCacheEntries[index].getValue());
+ size_t newTotalSize = mTotalSize + valueSize - oldValueBlob->getSize();
+ if (mMaxTotalSize < newTotalSize) {
+ if (isCleanable()) {
+ // Clean the cache and try again.
+ clean();
+ continue;
+ } else {
+ LOGV("set: not caching new value because the total cache "
+ "size limit would be exceeded: %d (limit: %d)",
+ keySize + valueSize, mMaxTotalSize);
+ break;
+ }
+ }
+ mCacheEntries.editItemAt(index).setValue(valueBlob);
+ mTotalSize = newTotalSize;
+ LOGV("set: updated existing cache entry with %d byte key and %d byte "
+ "value", keySize, valueSize);
+ }
+ break;
+ }
+}
+
+size_t BlobCache::get(const void* key, size_t keySize, void* value,
+ size_t valueSize) {
+ if (mMaxKeySize < keySize) {
+ LOGV("get: not searching because the key is too large: %d (limit %d)",
+ keySize, mMaxKeySize);
+ return 0;
+ }
+ Mutex::Autolock lock(mMutex);
+ sp<Blob> dummyKey(new Blob(key, keySize, false));
+ CacheEntry dummyEntry(dummyKey, NULL);
+ ssize_t index = mCacheEntries.indexOf(dummyEntry);
+ if (index < 0) {
+ LOGV("get: no cache entry found for key of size %d", keySize);
+ return 0;
+ }
+
+ // The key was found. Return the value if the caller's buffer is large
+ // enough.
+ sp<Blob> valueBlob(mCacheEntries[index].getValue());
+ size_t valueBlobSize = valueBlob->getSize();
+ if (valueBlobSize <= valueSize) {
+ LOGV("get: copying %d bytes to caller's buffer", valueBlobSize);
+ memcpy(value, valueBlob->getData(), valueBlobSize);
+ } else {
+ LOGV("get: caller's buffer is too small for value: %d (needs %d)",
+ valueSize, valueBlobSize);
+ }
+ return valueBlobSize;
+}
+
+void BlobCache::clean() {
+ // Remove a random cache entry until the total cache size gets below half
+ // the maximum total cache size.
+ while (mTotalSize > mMaxTotalSize / 2) {
+ size_t i = size_t(nrand48(mRandState) % (mCacheEntries.size()));
+ const CacheEntry& entry(mCacheEntries[i]);
+ mTotalSize -= entry.getKey()->getSize() + entry.getValue()->getSize();
+ mCacheEntries.removeAt(i);
+ }
+}
+
+bool BlobCache::isCleanable() const {
+ return mTotalSize > mMaxTotalSize / 2;
+}
+
+BlobCache::Blob::Blob(const void* data, size_t size, bool copyData):
+ mData(copyData ? malloc(size) : data),
+ mSize(size),
+ mOwnsData(copyData) {
+ if (copyData) {
+ memcpy(const_cast<void*>(mData), data, size);
+ }
+}
+
+BlobCache::Blob::~Blob() {
+ if (mOwnsData) {
+ free(const_cast<void*>(mData));
+ }
+}
+
+bool BlobCache::Blob::operator<(const Blob& rhs) const {
+ if (mSize == rhs.mSize) {
+ return memcmp(mData, rhs.mData, mSize) < 0;
+ } else {
+ return mSize < rhs.mSize;
+ }
+}
+
+const void* BlobCache::Blob::getData() const {
+ return mData;
+}
+
+size_t BlobCache::Blob::getSize() const {
+ return mSize;
+}
+
+BlobCache::CacheEntry::CacheEntry() {
+}
+
+BlobCache::CacheEntry::CacheEntry(const sp<Blob>& key, const sp<Blob>& value):
+ mKey(key),
+ mValue(value) {
+}
+
+BlobCache::CacheEntry::CacheEntry(const CacheEntry& ce):
+ mKey(ce.mKey),
+ mValue(ce.mValue) {
+}
+
+bool BlobCache::CacheEntry::operator<(const CacheEntry& rhs) const {
+ return *mKey < *rhs.mKey;
+}
+
+const BlobCache::CacheEntry& BlobCache::CacheEntry::operator=(const CacheEntry& rhs) {
+ mKey = rhs.mKey;
+ mValue = rhs.mValue;
+ return *this;
+}
+
+sp<BlobCache::Blob> BlobCache::CacheEntry::getKey() const {
+ return mKey;
+}
+
+sp<BlobCache::Blob> BlobCache::CacheEntry::getValue() const {
+ return mValue;
+}
+
+void BlobCache::CacheEntry::setValue(const sp<Blob>& value) {
+ mValue = value;
+}
+
+} // namespace android
diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp
index 8b5da0e..c748228 100644
--- a/libs/utils/Threads.cpp
+++ b/libs/utils/Threads.cpp
@@ -168,6 +168,9 @@
return 0;
}
+ // Note that *threadID is directly available to the parent only, as it is
+ // assigned after the child starts. Use memory barrier / lock if the child
+ // or other threads also need access.
if (threadId != NULL) {
*threadId = (android_thread_id_t)thread; // XXX: this is not portable
}
@@ -718,7 +721,6 @@
res = androidCreateRawThreadEtc(_threadLoop,
this, name, priority, stack, &mThread);
}
- // The new thread wakes up at _threadLoop, but immediately blocks on mLock
if (res == false) {
mStatus = UNKNOWN_ERROR; // something happened!
@@ -742,11 +744,6 @@
{
Thread* const self = static_cast<Thread*>(user);
- // force a memory barrier before reading any fields, in particular mHoldSelf
- {
- Mutex::Autolock _l(self->mLock);
- }
-
sp<Thread> strong(self->mHoldSelf);
wp<Thread> weak(strong);
self->mHoldSelf.clear();
@@ -816,6 +813,7 @@
status_t Thread::requestExitAndWait()
{
+ Mutex::Autolock _l(mLock);
if (mThread == getThreadId()) {
LOGW(
"Thread (this=%p): don't call waitForExit() from this "
@@ -825,9 +823,8 @@
return WOULD_BLOCK;
}
- requestExit();
+ mExitPending = true;
- Mutex::Autolock _l(mLock);
while (mRunning == true) {
mThreadExitedCondition.wait(mLock);
}
diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk
index 72d4876..87ad98e 100644
--- a/libs/utils/tests/Android.mk
+++ b/libs/utils/tests/Android.mk
@@ -6,6 +6,7 @@
# Build the unit tests.
test_src_files := \
+ BlobCache_test.cpp \
ObbFile_test.cpp \
Looper_test.cpp \
String8_test.cpp \
diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp
new file mode 100644
index 0000000..653ea5e
--- /dev/null
+++ b/libs/utils/tests/BlobCache_test.cpp
@@ -0,0 +1,257 @@
+/*
+ ** Copyright 2011, 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.
+ */
+
+#include <gtest/gtest.h>
+
+#include <utils/BlobCache.h>
+
+namespace android {
+
+class BlobCacheTest : public ::testing::Test {
+protected:
+ enum {
+ MAX_KEY_SIZE = 6,
+ MAX_VALUE_SIZE = 8,
+ MAX_TOTAL_SIZE = 13,
+ };
+
+ virtual void SetUp() {
+ mBC = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+ }
+
+ virtual void TearDown() {
+ mBC.clear();
+ }
+
+ sp<BlobCache> mBC;
+};
+
+TEST_F(BlobCacheTest, CacheSingleValueSucceeds) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+ ASSERT_EQ('e', buf[0]);
+ ASSERT_EQ('f', buf[1]);
+ ASSERT_EQ('g', buf[2]);
+ ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheTwoValuesSucceeds) {
+ char buf[2] = { 0xee, 0xee };
+ mBC->set("ab", 2, "cd", 2);
+ mBC->set("ef", 2, "gh", 2);
+ ASSERT_EQ(size_t(2), mBC->get("ab", 2, buf, 2));
+ ASSERT_EQ('c', buf[0]);
+ ASSERT_EQ('d', buf[1]);
+ ASSERT_EQ(size_t(2), mBC->get("ef", 2, buf, 2));
+ ASSERT_EQ('g', buf[0]);
+ ASSERT_EQ('h', buf[1]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesInsideBounds) {
+ char buf[6] = { 0xee, 0xee, 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf+1, 4));
+ ASSERT_EQ(0xee, buf[0]);
+ ASSERT_EQ('e', buf[1]);
+ ASSERT_EQ('f', buf[2]);
+ ASSERT_EQ('g', buf[3]);
+ ASSERT_EQ('h', buf[4]);
+ ASSERT_EQ(0xee, buf[5]);
+}
+
+TEST_F(BlobCacheTest, GetOnlyWritesIfBufferIsLargeEnough) {
+ char buf[3] = { 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 3));
+ ASSERT_EQ(0xee, buf[0]);
+ ASSERT_EQ(0xee, buf[1]);
+ ASSERT_EQ(0xee, buf[2]);
+}
+
+TEST_F(BlobCacheTest, GetDoesntAccessNullBuffer) {
+ mBC->set("abcd", 4, "efgh", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, MultipleSetsCacheLatestValue) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ mBC->set("abcd", 4, "ijkl", 4);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+ ASSERT_EQ('i', buf[0]);
+ ASSERT_EQ('j', buf[1]);
+ ASSERT_EQ('k', buf[2]);
+ ASSERT_EQ('l', buf[3]);
+}
+
+TEST_F(BlobCacheTest, SecondSetKeepsFirstValueIfTooLarge) {
+ char buf[MAX_VALUE_SIZE+1] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+ ASSERT_EQ(size_t(4), mBC->get("abcd", 4, buf, 4));
+ ASSERT_EQ('e', buf[0]);
+ ASSERT_EQ('f', buf[1]);
+ ASSERT_EQ('g', buf[2]);
+ ASSERT_EQ('h', buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyIsTooBig) {
+ char key[MAX_KEY_SIZE+1];
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ for (int i = 0; i < MAX_KEY_SIZE+1; i++) {
+ key[i] = 'a';
+ }
+ mBC->set(key, MAX_KEY_SIZE+1, "bbbb", 4);
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE+1, buf, 4));
+ ASSERT_EQ(0xee, buf[0]);
+ ASSERT_EQ(0xee, buf[1]);
+ ASSERT_EQ(0xee, buf[2]);
+ ASSERT_EQ(0xee, buf[3]);
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfValueIsTooBig) {
+ char buf[MAX_VALUE_SIZE+1];
+ for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ buf[i] = 'b';
+ }
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE+1);
+ for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ buf[i] = 0xee;
+ }
+ ASSERT_EQ(size_t(0), mBC->get("abcd", 4, buf, MAX_VALUE_SIZE+1));
+ for (int i = 0; i < MAX_VALUE_SIZE+1; i++) {
+ SCOPED_TRACE(i);
+ ASSERT_EQ(0xee, buf[i]);
+ }
+}
+
+TEST_F(BlobCacheTest, DoesntCacheIfKeyValuePairIsTooBig) {
+ // Check a testing assumptions
+ ASSERT_TRUE(MAX_TOTAL_SIZE < MAX_KEY_SIZE + MAX_VALUE_SIZE);
+ ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+ enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE + 1 };
+
+ char key[MAX_KEY_SIZE];
+ char buf[bufSize];
+ for (int i = 0; i < MAX_KEY_SIZE; i++) {
+ key[i] = 'a';
+ }
+ for (int i = 0; i < bufSize; i++) {
+ buf[i] = 'b';
+ }
+
+ mBC->set(key, MAX_KEY_SIZE, buf, MAX_VALUE_SIZE);
+ ASSERT_EQ(size_t(0), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeySizeSucceeds) {
+ char key[MAX_KEY_SIZE];
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ for (int i = 0; i < MAX_KEY_SIZE; i++) {
+ key[i] = 'a';
+ }
+ mBC->set(key, MAX_KEY_SIZE, "wxyz", 4);
+ ASSERT_EQ(size_t(4), mBC->get(key, MAX_KEY_SIZE, buf, 4));
+ ASSERT_EQ('w', buf[0]);
+ ASSERT_EQ('x', buf[1]);
+ ASSERT_EQ('y', buf[2]);
+ ASSERT_EQ('z', buf[3]);
+}
+
+TEST_F(BlobCacheTest, CacheMaxValueSizeSucceeds) {
+ char buf[MAX_VALUE_SIZE];
+ for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+ buf[i] = 'b';
+ }
+ mBC->set("abcd", 4, buf, MAX_VALUE_SIZE);
+ for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+ buf[i] = 0xee;
+ }
+ ASSERT_EQ(size_t(MAX_VALUE_SIZE), mBC->get("abcd", 4, buf,
+ MAX_VALUE_SIZE));
+ for (int i = 0; i < MAX_VALUE_SIZE; i++) {
+ SCOPED_TRACE(i);
+ ASSERT_EQ('b', buf[i]);
+ }
+}
+
+TEST_F(BlobCacheTest, CacheMaxKeyValuePairSizeSucceeds) {
+ // Check a testing assumption
+ ASSERT_TRUE(MAX_KEY_SIZE < MAX_TOTAL_SIZE);
+
+ enum { bufSize = MAX_TOTAL_SIZE - MAX_KEY_SIZE };
+
+ char key[MAX_KEY_SIZE];
+ char buf[bufSize];
+ for (int i = 0; i < MAX_KEY_SIZE; i++) {
+ key[i] = 'a';
+ }
+ for (int i = 0; i < bufSize; i++) {
+ buf[i] = 'b';
+ }
+
+ mBC->set(key, MAX_KEY_SIZE, buf, bufSize);
+ ASSERT_EQ(size_t(bufSize), mBC->get(key, MAX_KEY_SIZE, NULL, 0));
+}
+
+TEST_F(BlobCacheTest, CacheMinKeyAndValueSizeSucceeds) {
+ char buf[1] = { 0xee };
+ mBC->set("x", 1, "y", 1);
+ ASSERT_EQ(size_t(1), mBC->get("x", 1, buf, 1));
+ ASSERT_EQ('y', buf[0]);
+}
+
+TEST_F(BlobCacheTest, CacheSizeDoesntExceedTotalLimit) {
+ for (int i = 0; i < 256; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, "x", 1);
+ }
+ int numCached = 0;
+ for (int i = 0; i < 256; i++) {
+ uint8_t k = i;
+ if (mBC->get(&k, 1, NULL, 0) == 1) {
+ numCached++;
+ }
+ }
+ ASSERT_GE(MAX_TOTAL_SIZE / 2, numCached);
+}
+
+TEST_F(BlobCacheTest, ExceedingTotalLimitHalvesCacheSize) {
+ // Fill up the entire cache with 1 char key/value pairs.
+ const int maxEntries = MAX_TOTAL_SIZE / 2;
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ mBC->set(&k, 1, "x", 1);
+ }
+ // Insert one more entry, causing a cache overflow.
+ {
+ uint8_t k = maxEntries;
+ mBC->set(&k, 1, "x", 1);
+ }
+ // Count the number of entries in the cache.
+ int numCached = 0;
+ for (int i = 0; i < maxEntries+1; i++) {
+ uint8_t k = i;
+ if (mBC->get(&k, 1, NULL, 0) == 1) {
+ numCached++;
+ }
+ }
+ ASSERT_EQ(maxEntries/2 + 1, numCached);
+}
+
+} // namespace android
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
index ef57228..72519fb 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
@@ -57,15 +57,15 @@
android:layout_width="match_parent"
android:layout_weight="1"
>
- <LinearLayout
+ <com.android.systemui.statusbar.policy.NotificationRowLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|bottom"
- android:orientation="vertical"
android:clickable="true"
android:focusable="true"
android:descendantFocusability="afterDescendants"
+ systemui:rowHeight="@dimen/notification_height"
/>
</ScrollView>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml
index 8e456b2..93085d7 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_row.xml
@@ -1,6 +1,6 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="65dp"
+ android:layout_height="@dimen/notification_height"
>
<ImageButton
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 18e8273..6670eff 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -18,7 +18,9 @@
*/
-->
-<com.android.systemui.statusbar.phone.ExpandedView xmlns:android="http://schemas.android.com/apk/res/android"
+<com.android.systemui.statusbar.phone.ExpandedView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:systemui="http://schemas.android.com/apk/res/com.android.systemui"
android:orientation="vertical"
android:focusable="true"
android:descendantFocusability="afterDescendants"
@@ -97,10 +99,11 @@
android:textAppearance="@style/TextAppearance.StatusBar.Title"
android:text="@string/status_bar_ongoing_events_title"
/>
- <LinearLayout android:id="@+id/ongoingItems"
+ <com.android.systemui.statusbar.policy.NotificationRowLayout
+ android:id="@+id/ongoingItems"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
+ systemui:rowHeight="@dimen/notification_height"
/>
<TextView android:id="@+id/latestTitle"
@@ -111,10 +114,11 @@
android:textAppearance="@style/TextAppearance.StatusBar.Title"
android:text="@string/status_bar_latest_events_title"
/>
- <LinearLayout android:id="@+id/latestItems"
+ <com.android.systemui.statusbar.policy.NotificationRowLayout
+ android:id="@+id/latestItems"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical"
+ systemui:rowHeight="@dimen/notification_height"
/>
</LinearLayout>
</ScrollView>
diff --git a/packages/SystemUI/res/layout/status_bar_notification_row.xml b/packages/SystemUI/res/layout/status_bar_notification_row.xml
index 8e456b2..93085d7 100644
--- a/packages/SystemUI/res/layout/status_bar_notification_row.xml
+++ b/packages/SystemUI/res/layout/status_bar_notification_row.xml
@@ -1,6 +1,6 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="65dp"
+ android:layout_height="@dimen/notification_height"
>
<ImageButton
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index fb2f7d63..5291629 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -24,5 +24,8 @@
<declare-styleable name="NotificationLinearLayout">
<attr name="insetLeft" format="dimension" />
</declare-styleable>
+ <declare-styleable name="NotificationRowLayout">
+ <attr name="rowHeight" format="dimension" />
+ </declare-styleable>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 657dc46..5f0fbef 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -47,5 +47,8 @@
<!-- thickness (height) of the navigation bar on phones that require it -->
<dimen name="navigation_bar_size">42dp</dimen>
+
+ <!-- thickness (height) of each notification row, including any separators or padding -->
+ <dimen name="notification_height">65dp</dimen>
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java
index 92b8976..51fc7c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandedView.java
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.phone;
+import android.animation.LayoutTransition;
import android.content.Context;
import android.util.AttributeSet;
import android.view.Display;
@@ -37,6 +38,8 @@
@Override
protected void onFinishInflate() {
super.onFinishInflate();
+
+ setLayerType(LAYER_TYPE_HARDWARE, null);
}
/** We want to shrink down to 0, and ignore the background. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 00de920..3d15a1d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -130,11 +130,11 @@
// ongoing
NotificationData mOngoing = new NotificationData();
TextView mOngoingTitle;
- LinearLayout mOngoingItems;
+ ViewGroup mOngoingItems;
// latest
NotificationData mLatest = new NotificationData();
TextView mLatestTitle;
- LinearLayout mLatestItems;
+ ViewGroup mLatestItems;
// position
int[] mPositionTmp = new int[2];
boolean mExpanded;
@@ -268,9 +268,9 @@
mExpandedView = expanded;
mExpandedContents = expanded.findViewById(R.id.notificationLinearLayout);
mOngoingTitle = (TextView)expanded.findViewById(R.id.ongoingTitle);
- mOngoingItems = (LinearLayout)expanded.findViewById(R.id.ongoingItems);
+ mOngoingItems = (ViewGroup)expanded.findViewById(R.id.ongoingItems);
mLatestTitle = (TextView)expanded.findViewById(R.id.latestTitle);
- mLatestItems = (LinearLayout)expanded.findViewById(R.id.latestItems);
+ mLatestItems = (ViewGroup)expanded.findViewById(R.id.latestItems);
mNoNotificationsTitle = (TextView)expanded.findViewById(R.id.noNotificationsTitle);
mClearButton = (TextView)expanded.findViewById(R.id.clear_all_button);
mClearButton.setOnClickListener(mClearButtonListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
new file mode 100644
index 0000000..24eee27
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2011 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.systemui.statusbar.policy;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.ValueAnimator;
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.AccelerateInterpolator;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import java.util.HashSet;
+
+import com.android.systemui.R;
+
+public class NotificationRowLayout extends ViewGroup {
+ private static final String TAG = "NotificationRowLayout";
+ private static final boolean DEBUG = false;
+
+ private static final boolean ANIMATE_LAYOUT = true;
+
+ private static final int ANIM_LEN = DEBUG ? 5000 : 250;
+
+ Rect mTmpRect = new Rect();
+ int mNumRows = 0;
+ int mRowHeight = 0;
+ int mHeight = 0;
+
+ HashSet<View> mAppearingViews = new HashSet<View>();
+ HashSet<View> mDisappearingViews = new HashSet<View>();
+
+ public NotificationRowLayout(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public NotificationRowLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NotificationRowLayout,
+ defStyle, 0);
+ mRowHeight = a.getDimensionPixelSize(R.styleable.NotificationRowLayout_rowHeight, 0);
+ a.recycle();
+
+ setLayoutTransition(null);
+
+ if (DEBUG) {
+ setOnHierarchyChangeListener(new ViewGroup.OnHierarchyChangeListener() {
+ @Override
+ public void onChildViewAdded(View parent, View child) {
+ Slog.d(TAG, "view added: " + child + "; new count: " + getChildCount());
+ }
+ @Override
+ public void onChildViewRemoved(View parent, View child) {
+ Slog.d(TAG, "view removed: " + child + "; new count: " + (getChildCount() - 1));
+ }
+ });
+
+ setBackgroundColor(0x80FF8000);
+ }
+
+ }
+
+ //**
+ @Override
+ public void addView(View child, int index, LayoutParams params) {
+ super.addView(child, index, params);
+
+ final View childF = child;
+
+ if (ANIMATE_LAYOUT) {
+ mAppearingViews.add(child);
+
+ child.setPivotY(0);
+ AnimatorSet a = new AnimatorSet();
+ a.playTogether(
+ ObjectAnimator.ofFloat(child, "alpha", 0f, 1f)
+// ,ObjectAnimator.ofFloat(child, "scaleY", 0f, 1f)
+ );
+ a.setDuration(ANIM_LEN);
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ mAppearingViews.remove(childF);
+ }
+ });
+ a.start();
+ requestLayout(); // start the container animation
+ }
+ }
+
+ @Override
+ public void removeView(View child) {
+ final View childF = child;
+ if (ANIMATE_LAYOUT) {
+ if (mAppearingViews.contains(child)) {
+ mAppearingViews.remove(child);
+ }
+ mDisappearingViews.add(child);
+
+ child.setPivotY(0);
+ AnimatorSet a = new AnimatorSet();
+ a.playTogether(
+ ObjectAnimator.ofFloat(child, "alpha", 0f)
+// ,ObjectAnimator.ofFloat(child, "scaleY", 0f)
+ ,ObjectAnimator.ofFloat(child, "translationX", 300f)
+ );
+ a.setDuration(ANIM_LEN);
+ a.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ NotificationRowLayout.super.removeView(childF);
+ childF.setAlpha(1f);
+ mDisappearingViews.remove(childF);
+ }
+ });
+ a.start();
+ requestLayout(); // start the container animation
+ } else {
+ super.removeView(child);
+ }
+ }
+ //**
+
+ @Override
+ public void onFinishInflate() {
+ super.onFinishInflate();
+ setWillNotDraw(false);
+ }
+
+ @Override
+ public void onDraw(android.graphics.Canvas c) {
+ super.onDraw(c);
+ if (DEBUG) {
+ Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: "
+ + getMeasuredHeight() + "px");
+ c.save();
+ c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6,
+ android.graphics.Region.Op.DIFFERENCE);
+ c.drawColor(0xFFFF8000);
+ c.restore();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int count = getChildCount();
+
+ // pass 1: count the number of non-GONE views
+ int numRows = 0;
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+ if (mDisappearingViews.contains(child)) {
+ continue;
+ }
+ numRows++;
+ }
+ if (numRows != mNumRows) {
+ // uh oh, now you made us go and do work
+
+ final int computedHeight = numRows * mRowHeight;
+ if (DEBUG) {
+ Slog.d(TAG, String.format("rows went from %d to %d, resizing to %dpx",
+ mNumRows, numRows, computedHeight));
+ }
+
+ mNumRows = numRows;
+
+ if (ANIMATE_LAYOUT && isShown()) {
+ ObjectAnimator.ofInt(this, "forcedHeight", computedHeight)
+ .setDuration(ANIM_LEN)
+ .start();
+ } else {
+ setForcedHeight(computedHeight);
+ }
+ }
+
+ // pass 2: you know, do the measuring
+ final int childWidthMS = widthMeasureSpec;
+ final int childHeightMS = MeasureSpec.makeMeasureSpec(
+ mRowHeight, MeasureSpec.EXACTLY);
+
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+
+ child.measure(childWidthMS, childHeightMS);
+ }
+
+ setMeasuredDimension(
+ getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
+ resolveSize(getForcedHeight(), heightMeasureSpec));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ final int width = right - left;
+ final int height = bottom - top;
+
+ if (DEBUG) Slog.d(TAG, "onLayout: height=" + height);
+
+ final int count = getChildCount();
+ int y = 0;
+ for (int i = 0; i < count; i++) {
+ final View child = getChildAt(i);
+ if (child.getVisibility() == GONE) {
+ continue;
+ }
+// final int thisRowHeight = (int)(
+// ((mAppearingViews.contains(child) || mDisappearingViews.contains(child))
+// ? child.getScaleY()
+// : 1.0f)
+// * mRowHeight);
+ final int thisRowHeight = (int)(child.getAlpha() * mRowHeight);
+// child.layout(0, y, width, y + thisRowHeight);
+ child.layout(0, y, width, y + mRowHeight);
+ y += thisRowHeight;
+ }
+ }
+
+ public void setForcedHeight(int h) {
+ if (DEBUG) Slog.d(TAG, "forcedHeight: " + h);
+ if (h != mHeight) {
+ mHeight = h;
+ requestLayout();
+ }
+ }
+
+ public int getForcedHeight() {
+ return mHeight;
+ }
+}
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
index 7a18831..a3749cf 100644
--- a/services/camera/libcameraservice/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -552,7 +552,7 @@
#define anw(n) __to_anw(((struct camera_preview_window *)n)->user)
static int __dequeue_buffer(struct preview_stream_ops* w,
- buffer_handle_t** buffer)
+ buffer_handle_t** buffer, int *stride)
{
int rc;
ANativeWindow *a = anw(w);
@@ -560,8 +560,10 @@
rc = a->dequeueBuffer(a, &anb);
if (!rc) {
rc = a->lockBuffer(a, anb);
- if (!rc)
+ if (!rc) {
*buffer = &anb->handle;
+ *stride = anb->stride;
+ }
else
a->cancelBuffer(a, anb);
}
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 4c6098d..eff65c2 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -1464,6 +1464,23 @@
injectionPermission = INJECTION_PERMISSION_GRANTED;
}
+ // Check whether windows listening for outside touches are owned by the same UID. If it is
+ // set the policy flag that we will not reveal coordinate information to this window.
+ if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
+ const InputWindow* foregroundWindow = mTempTouchState.getFirstForegroundWindow();
+ const int32_t foregroundWindowUid = foregroundWindow->ownerUid;
+ for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
+ const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
+ if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+ const InputWindow* inputWindow = touchedWindow.window;
+ if (inputWindow->ownerUid != foregroundWindowUid) {
+ mTempTouchState.addOrUpdateWindow(inputWindow,
+ InputTarget::FLAG_ZERO_COORDS, BitSet32(0));
+ }
+ }
+ }
+ }
+
// Ensure all touched foreground windows are ready for new input.
for (size_t i = 0; i < mTempTouchState.windows.size(); i++) {
const TouchedWindow& touchedWindow = mTempTouchState.windows[i];
@@ -1987,7 +2004,8 @@
// Set the X and Y offset depending on the input source.
float xOffset, yOffset, scaleFactor;
- if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER) {
+ if (motionEntry->source & AINPUT_SOURCE_CLASS_POINTER
+ && !(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
scaleFactor = dispatchEntry->scaleFactor;
xOffset = dispatchEntry->xOffset * scaleFactor;
yOffset = dispatchEntry->yOffset * scaleFactor;
@@ -2002,6 +2020,14 @@
xOffset = 0.0f;
yOffset = 0.0f;
scaleFactor = 1.0f;
+
+ // We don't want the dispatch target to know.
+ if (dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS) {
+ for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+ scaledCoords[i].clear();
+ }
+ usingCoords = scaledCoords;
+ }
}
// Update the connection's input state.
@@ -2030,9 +2056,11 @@
MotionSample* nextMotionSample = firstMotionSample->next;
for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
if (usingCoords == scaledCoords) {
- for (size_t i = 0; i < motionEntry->pointerCount; i++) {
- scaledCoords[i] = nextMotionSample->pointerCoords[i];
- scaledCoords[i].scale(scaleFactor);
+ if (!(dispatchEntry->targetFlags & InputTarget::FLAG_ZERO_COORDS)) {
+ for (size_t i = 0; i < motionEntry->pointerCount; i++) {
+ scaledCoords[i] = nextMotionSample->pointerCoords[i];
+ scaledCoords[i].scale(scaleFactor);
+ }
}
} else {
usingCoords = nextMotionSample->pointerCoords;
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 37cef90..39d4aeb 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -96,6 +96,12 @@
/* This flag indicates that a motion event is being split across multiple windows. */
FLAG_SPLIT = 1 << 2,
+ /* This flag indicates that the pointer coordinates dispatched to the application
+ * will be zeroed out to avoid revealing information to an application. This is
+ * used in conjunction with FLAG_DISPATCH_AS_OUTSIDE to prevent apps not sharing
+ * the same UID from watching all touches. */
+ FLAG_ZERO_COORDS = 1 << 3,
+
/* This flag indicates that the event should be sent as is.
* Should always be set unless the event is to be transmuted. */
FLAG_DISPATCH_AS_IS = 1 << 8,
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index 8f179f5..2190b30 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -882,8 +882,7 @@
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
final String[] ifaces = listInterfaces();
- final NetworkStats.Builder stats = new NetworkStats.Builder(
- SystemClock.elapsedRealtime(), ifaces.length);
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length);
for (String iface : ifaces) {
final long rx = getInterfaceCounter(iface, true);
@@ -891,7 +890,7 @@
stats.addEntry(iface, NetworkStats.UID_ALL, rx, tx);
}
- return stats.build();
+ return stats;
}
@Override
@@ -900,7 +899,7 @@
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
final String[] knownUids = PATH_PROC_UID_STAT.list();
- final NetworkStats.Builder stats = new NetworkStats.Builder(
+ final NetworkStats stats = new NetworkStats(
SystemClock.elapsedRealtime(), knownUids.length);
for (String uid : knownUids) {
@@ -908,7 +907,7 @@
collectNetworkStatsDetail(stats, uidInt);
}
- return stats.build();
+ return stats;
}
@Override
@@ -918,13 +917,12 @@
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
}
- final NetworkStats.Builder stats = new NetworkStats.Builder(
- SystemClock.elapsedRealtime(), 1);
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
collectNetworkStatsDetail(stats, uid);
- return stats.build();
+ return stats;
}
- private void collectNetworkStatsDetail(NetworkStats.Builder stats, int uid) {
+ private void collectNetworkStatsDetail(NetworkStats stats, int uid) {
// TODO: kernel module will provide interface-level stats in future
// TODO: migrate these stats to come across netd in bulk, instead of all
// these individual file reads.
diff --git a/services/java/com/android/server/net/NetworkIdentity.java b/services/java/com/android/server/net/NetworkIdentity.java
index f7a7c49..4a207f7 100644
--- a/services/java/com/android/server/net/NetworkIdentity.java
+++ b/services/java/com/android/server/net/NetworkIdentity.java
@@ -26,6 +26,7 @@
import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_UNKNOWN;
import static android.telephony.TelephonyManager.getNetworkClass;
import android.content.Context;
@@ -148,6 +149,7 @@
if (isNetworkTypeMobile(type)
&& Objects.equal(this.subscriberId, subscriberId)) {
switch (getNetworkClass(subType)) {
+ case NETWORK_CLASS_UNKNOWN:
case NETWORK_CLASS_2_G:
case NETWORK_CLASS_3_G:
return true;
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 2766093..e7d60638 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -20,7 +20,9 @@
import static android.Manifest.permission.DUMP;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
import static android.Manifest.permission.MANAGE_NETWORK_POLICY;
+import static android.Manifest.permission.READ_PHONE_STATE;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
@@ -28,6 +30,8 @@
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
import static android.net.NetworkPolicyManager.dumpPolicy;
import static android.net.NetworkPolicyManager.dumpRules;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
+import static android.net.TrafficStats.isNetworkTemplateMobile;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull;
import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
@@ -53,6 +57,8 @@
import android.os.IPowerManager;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.telephony.TelephonyManager;
+import android.text.format.Time;
import android.util.NtpTrustedTime;
import android.util.Slog;
import android.util.SparseArray;
@@ -96,9 +102,14 @@
public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub {
private static final String TAG = "NetworkPolicy";
private static final boolean LOGD = true;
+ private static final boolean LOGV = false;
private static final int VERSION_CURRENT = 1;
+ private static final long KB_IN_BYTES = 1024;
+ private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
+ private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+
private static final String TAG_POLICY_LIST = "policy-list";
private static final String TAG_NETWORK_POLICY = "network-policy";
private static final String TAG_UID_POLICY = "uid-policy";
@@ -126,14 +137,14 @@
private boolean mScreenOn;
- /** Current network policy for each UID. */
+ /** Current policy for network templates. */
+ private ArrayList<NetworkPolicy> mNetworkPolicy = Lists.newArrayList();
+
+ /** Current policy for each UID. */
private SparseIntArray mUidPolicy = new SparseIntArray();
/** Current derived network rules for each UID. */
private SparseIntArray mUidRules = new SparseIntArray();
- /** Set of policies for strong network templates. */
- private HashMap<StrongTemplate, NetworkPolicy> mTemplatePolicy = Maps.newHashMap();
-
/** Foreground at both UID and PID granularity. */
private SparseBooleanArray mUidForeground = new SparseBooleanArray();
private SparseArray<SparseBooleanArray> mUidPidForeground = new SparseArray<
@@ -150,8 +161,6 @@
// TODO: keep whitelist of system-critical services that should never have
// rules enforced, such as system, phone, and radio UIDs.
- // TODO: dispatch callbacks through handler when locked
-
public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
IPowerManager powerManager, INetworkStatsService networkStats) {
// TODO: move to using cached NtpTrustedTime
@@ -264,8 +273,7 @@
/**
* Receiver that watches for {@link IConnectivityManager} to claim network
- * interfaces. Used to apply {@link NetworkPolicy} when networks match
- * {@link StrongTemplate}.
+ * interfaces. Used to apply {@link NetworkPolicy} to matching networks.
*/
private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
@Override
@@ -273,6 +281,7 @@
// on background handler thread, and verified CONNECTIVITY_INTERNAL
// permission above.
synchronized (mRulesLock) {
+ ensureActiveMobilePolicyLocked();
updateIfacesLocked();
}
}
@@ -284,7 +293,7 @@
* remaining quota based on usage cycle and historical stats.
*/
private void updateIfacesLocked() {
- if (LOGD) Slog.v(TAG, "updateIfacesLocked()");
+ if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
final NetworkState[] states;
try {
@@ -307,14 +316,14 @@
}
// build list of rules and ifaces to enforce them against
- final HashMap<StrongTemplate, String[]> rules = Maps.newHashMap();
+ final HashMap<NetworkPolicy, String[]> rules = Maps.newHashMap();
final ArrayList<String> ifaceList = Lists.newArrayList();
- for (StrongTemplate template : mTemplatePolicy.keySet()) {
+ for (NetworkPolicy policy : mNetworkPolicy) {
// collect all active ifaces that match this template
ifaceList.clear();
for (NetworkIdentity ident : networks.keySet()) {
- if (ident.matchesTemplate(template.networkTemplate, template.subscriberId)) {
+ if (ident.matchesTemplate(policy.networkTemplate, policy.subscriberId)) {
final String iface = networks.get(ident);
ifaceList.add(iface);
}
@@ -322,7 +331,7 @@
if (ifaceList.size() > 0) {
final String[] ifaces = ifaceList.toArray(new String[ifaceList.size()]);
- rules.put(template, ifaces);
+ rules.put(policy, ifaces);
}
}
@@ -336,41 +345,81 @@
// apply each policy that we found ifaces for; compute remaining data
// based on current cycle and historical stats, and push to kernel.
- for (StrongTemplate template : rules.keySet()) {
- final NetworkPolicy policy = mTemplatePolicy.get(template);
+ for (NetworkPolicy policy : rules.keySet()) {
final String[] ifaces = rules.get(policy);
final long start = computeLastCycleBoundary(currentTime, policy);
final long end = currentTime;
final NetworkStats stats;
+ final long total;
try {
stats = mNetworkStats.getSummaryForNetwork(
- start, end, template.networkTemplate, template.subscriberId);
+ start, end, policy.networkTemplate, policy.subscriberId);
+ total = stats.rx[0] + stats.tx[0];
} catch (RemoteException e) {
- Slog.w(TAG, "problem reading summary for template " + template.networkTemplate);
+ Slog.w(TAG, "problem reading summary for template " + policy.networkTemplate);
continue;
}
- // remaining "quota" is based on usage in current cycle
- final long total = stats.rx[0] + stats.tx[0];
- final long quota = Math.max(0, policy.limitBytes - total);
-
if (LOGD) {
Slog.d(TAG, "applying policy " + policy.toString() + " to ifaces "
- + Arrays.toString(ifaces) + " with quota " + quota);
+ + Arrays.toString(ifaces));
}
- // TODO: push rule down through NetworkManagementService.setInterfaceQuota()
+ // TODO: register for warning notification trigger through NMS
+ if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
+ // remaining "quota" is based on usage in current cycle
+ final long quota = Math.max(0, policy.limitBytes - total);
+
+ // TODO: push quota rule down through NMS
+ }
+ }
+ }
+
+ /**
+ * Once any {@link #mNetworkPolicy} are loaded from disk, ensure that we
+ * have at least a default mobile policy defined.
+ */
+ private void ensureActiveMobilePolicyLocked() {
+ if (LOGV) Slog.v(TAG, "ensureActiveMobilePolicyLocked()");
+ final String subscriberId = getActiveSubscriberId();
+ if (subscriberId == null) {
+ if (LOGV) Slog.v(TAG, "no active mobile network, ignoring policy check");
+ return;
+ }
+
+ // examine to see if any policy is defined for active mobile
+ boolean mobileDefined = false;
+ for (NetworkPolicy policy : mNetworkPolicy) {
+ if (isNetworkTemplateMobile(policy.networkTemplate)
+ && Objects.equal(subscriberId, policy.subscriberId)) {
+ mobileDefined = true;
+ }
+ }
+
+ if (!mobileDefined) {
+ Slog.i(TAG, "no policy for active mobile network; generating default policy");
+
+ // default mobile policy has combined 4GB warning, and assume usage
+ // cycle starts today today.
+
+ // TODO: move this policy definition to overlay or secure setting
+ final Time time = new Time(Time.TIMEZONE_UTC);
+ time.setToNow();
+ final int cycleDay = time.monthDay;
+
+ mNetworkPolicy.add(new NetworkPolicy(
+ TEMPLATE_MOBILE_ALL, subscriberId, cycleDay, 4 * GB_IN_BYTES, LIMIT_DISABLED));
}
}
private void readPolicyLocked() {
- if (LOGD) Slog.v(TAG, "readPolicyLocked()");
+ if (LOGV) Slog.v(TAG, "readPolicyLocked()");
// clear any existing policy and read from disk
- mTemplatePolicy.clear();
+ mNetworkPolicy.clear();
mUidPolicy.clear();
FileInputStream fis = null;
@@ -390,13 +439,12 @@
} else if (TAG_NETWORK_POLICY.equals(tag)) {
final int networkTemplate = readIntAttribute(in, ATTR_NETWORK_TEMPLATE);
final String subscriberId = in.getAttributeValue(null, ATTR_SUBSCRIBER_ID);
-
final int cycleDay = readIntAttribute(in, ATTR_CYCLE_DAY);
final long warningBytes = readLongAttribute(in, ATTR_WARNING_BYTES);
final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES);
- mTemplatePolicy.put(new StrongTemplate(networkTemplate, subscriberId),
- new NetworkPolicy(cycleDay, warningBytes, limitBytes));
+ mNetworkPolicy.add(new NetworkPolicy(
+ networkTemplate, subscriberId, cycleDay, warningBytes, limitBytes));
} else if (TAG_UID_POLICY.equals(tag)) {
final int uid = readIntAttribute(in, ATTR_UID);
@@ -419,7 +467,7 @@
}
private void writePolicyLocked() {
- if (LOGD) Slog.v(TAG, "writePolicyLocked()");
+ if (LOGV) Slog.v(TAG, "writePolicyLocked()");
FileOutputStream fos = null;
try {
@@ -433,13 +481,11 @@
writeIntAttribute(out, ATTR_VERSION, VERSION_CURRENT);
// write all known network policies
- for (StrongTemplate template : mTemplatePolicy.keySet()) {
- final NetworkPolicy policy = mTemplatePolicy.get(template);
-
+ for (NetworkPolicy policy : mNetworkPolicy) {
out.startTag(null, TAG_NETWORK_POLICY);
- writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, template.networkTemplate);
- if (template.subscriberId != null) {
- out.attribute(null, ATTR_SUBSCRIBER_ID, template.subscriberId);
+ writeIntAttribute(out, ATTR_NETWORK_TEMPLATE, policy.networkTemplate);
+ if (policy.subscriberId != null) {
+ out.attribute(null, ATTR_SUBSCRIBER_ID, policy.subscriberId);
}
writeIntAttribute(out, ATTR_CYCLE_DAY, policy.cycleDay);
writeLongAttribute(out, ATTR_WARNING_BYTES, policy.warningBytes);
@@ -519,25 +565,27 @@
}
@Override
- public void setNetworkPolicy(int networkType, String subscriberId, NetworkPolicy policy) {
+ public void setNetworkPolicies(NetworkPolicy[] policies) {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
synchronized (mRulesLock) {
- mTemplatePolicy.put(new StrongTemplate(networkType, subscriberId), policy);
+ mNetworkPolicy.clear();
+ for (NetworkPolicy policy : policies) {
+ mNetworkPolicy.add(policy);
+ }
- // network policy changed, recompute template rules based on active
- // interfaces and persist policy.
updateIfacesLocked();
writePolicyLocked();
}
}
@Override
- public NetworkPolicy getNetworkPolicy(int networkType, String subscriberId) {
+ public NetworkPolicy[] getNetworkPolicies() {
mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ mContext.enforceCallingOrSelfPermission(READ_PHONE_STATE, TAG);
synchronized (mRulesLock) {
- return mTemplatePolicy.get(new StrongTemplate(networkType, subscriberId));
+ return mNetworkPolicy.toArray(new NetworkPolicy[mNetworkPolicy.size()]);
}
}
@@ -547,10 +595,8 @@
synchronized (mRulesLock) {
fout.println("Network policies:");
- for (StrongTemplate template : mTemplatePolicy.keySet()) {
- final NetworkPolicy policy = mTemplatePolicy.get(template);
- fout.print(" "); fout.println(template.toString());
- fout.print(" "); fout.println(policy.toString());
+ for (NetworkPolicy policy : mNetworkPolicy) {
+ fout.print(" "); fout.println(policy.toString());
}
fout.println("Policy status for known UIDs:");
@@ -682,6 +728,12 @@
mListeners.finishBroadcast();
}
+ private String getActiveSubscriberId() {
+ final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
+ Context.TELEPHONY_SERVICE);
+ return telephony.getSubscriberId();
+ }
+
private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
final int size = source.size();
for (int i = 0; i < size; i++) {
@@ -734,40 +786,4 @@
out.attribute(null, name, Long.toString(value));
}
- /**
- * Network template with strong subscriber ID, used as key when defining
- * {@link NetworkPolicy}.
- */
- private static class StrongTemplate {
- public final int networkTemplate;
- public final String subscriberId;
-
- public StrongTemplate(int networkTemplate, String subscriberId) {
- this.networkTemplate = networkTemplate;
- this.subscriberId = subscriberId;
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(networkTemplate, subscriberId);
- }
-
- @Override
- public boolean equals(Object obj) {
- if (obj instanceof StrongTemplate) {
- final StrongTemplate template = (StrongTemplate) obj;
- return template.networkTemplate == networkTemplate
- && Objects.equal(template.subscriberId, subscriberId);
- }
- return false;
- }
-
- @Override
- public String toString() {
- return "TemplateIdentity: networkTemplate=" + networkTemplate + ", subscriberId="
- + subscriberId;
- }
-
- }
-
}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 161c393..de69849 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -23,12 +23,12 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.UID_ALL;
-import static android.provider.Settings.Secure.NETSTATS_DETAIL_BUCKET_DURATION;
-import static android.provider.Settings.Secure.NETSTATS_DETAIL_MAX_HISTORY;
+import static android.provider.Settings.Secure.NETSTATS_NETWORK_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_NETWORK_MAX_HISTORY;
import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
-import static android.provider.Settings.Secure.NETSTATS_SUMMARY_BUCKET_DURATION;
-import static android.provider.Settings.Secure.NETSTATS_SUMMARY_MAX_HISTORY;
+import static android.provider.Settings.Secure.NETSTATS_UID_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_UID_MAX_HISTORY;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -38,6 +38,7 @@
import android.app.IAlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -88,6 +89,7 @@
public class NetworkStatsService extends INetworkStatsService.Stub {
private static final String TAG = "NetworkStats";
private static final boolean LOGD = true;
+ private static final boolean LOGV = false;
/** File header magic number: "ANET" */
private static final int FILE_MAGIC = 0x414E4554;
@@ -97,6 +99,7 @@
private final INetworkManagementService mNetworkManager;
private final IAlarmManager mAlarmManager;
private final TrustedTime mTime;
+ private final NetworkStatsSettings mSettings;
private IConnectivityManager mConnManager;
@@ -112,22 +115,18 @@
private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
- private LongSecureSetting mPollInterval = new LongSecureSetting(
- NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
- private LongSecureSetting mPersistThreshold = new LongSecureSetting(
- NETSTATS_PERSIST_THRESHOLD, 16 * KB_IN_BYTES);
-
- // TODO: adjust these timings for production builds
- private LongSecureSetting mSummaryBucketDuration = new LongSecureSetting(
- NETSTATS_SUMMARY_BUCKET_DURATION, 1 * HOUR_IN_MILLIS);
- private LongSecureSetting mSummaryMaxHistory = new LongSecureSetting(
- NETSTATS_SUMMARY_MAX_HISTORY, 90 * DAY_IN_MILLIS);
- private LongSecureSetting mDetailBucketDuration = new LongSecureSetting(
- NETSTATS_DETAIL_BUCKET_DURATION, 2 * HOUR_IN_MILLIS);
- private LongSecureSetting mDetailMaxHistory = new LongSecureSetting(
- NETSTATS_DETAIL_MAX_HISTORY, 90 * DAY_IN_MILLIS);
-
- private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
+ /**
+ * Settings that can be changed externally.
+ */
+ public interface NetworkStatsSettings {
+ public long getPollInterval();
+ public long getPersistThreshold();
+ public long getNetworkBucketDuration();
+ public long getNetworkMaxHistory();
+ public long getUidBucketDuration();
+ public long getUidMaxHistory();
+ public long getTimeCacheMaxAge();
+ }
private final Object mStatsLock = new Object();
@@ -135,19 +134,23 @@
private HashMap<String, InterfaceIdentity> mActiveIface = Maps.newHashMap();
/** Set of historical stats for known ifaces. */
- private HashMap<InterfaceIdentity, NetworkStatsHistory> mSummaryStats = Maps.newHashMap();
+ private HashMap<InterfaceIdentity, NetworkStatsHistory> mNetworkStats = Maps.newHashMap();
/** Set of historical stats for known UIDs. */
- private SparseArray<NetworkStatsHistory> mDetailStats = new SparseArray<NetworkStatsHistory>();
+ private SparseArray<NetworkStatsHistory> mUidStats = new SparseArray<NetworkStatsHistory>();
- private NetworkStats mLastSummaryPoll;
- private NetworkStats mLastSummaryPersist;
+ /** Flag if {@link #mUidStats} have been loaded from disk. */
+ private boolean mUidStatsLoaded = false;
- private NetworkStats mLastDetailPoll;
+ private NetworkStats mLastNetworkPoll;
+ private NetworkStats mLastNetworkPersist;
+
+ private NetworkStats mLastUidPoll;
private final HandlerThread mHandlerThread;
private final Handler mHandler;
- private final AtomicFile mSummaryFile;
+ private final AtomicFile mNetworkFile;
+ private final AtomicFile mUidFile;
// TODO: collect detailed uid stats, storing tag-granularity data until next
// dropbox, and uid summary for a specific bucket count.
@@ -157,7 +160,8 @@
public NetworkStatsService(
Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
// TODO: move to using cached NtpTrustedTime
- this(context, networkManager, alarmManager, new NtpTrustedTime(), getSystemDir());
+ this(context, networkManager, alarmManager, new NtpTrustedTime(), getSystemDir(),
+ new DefaultNetworkStatsSettings(context));
}
private static File getSystemDir() {
@@ -165,17 +169,20 @@
}
public NetworkStatsService(Context context, INetworkManagementService networkManager,
- IAlarmManager alarmManager, TrustedTime time, File systemDir) {
+ IAlarmManager alarmManager, TrustedTime time, File systemDir,
+ NetworkStatsSettings settings) {
mContext = checkNotNull(context, "missing Context");
mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager");
mTime = checkNotNull(time, "missing TrustedTime");
+ mSettings = checkNotNull(settings, "missing NetworkStatsSettings");
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
- mSummaryFile = new AtomicFile(new File(systemDir, "netstats.bin"));
+ mNetworkFile = new AtomicFile(new File(systemDir, "netstats.bin"));
+ mUidFile = new AtomicFile(new File(systemDir, "netstats_uid.bin"));
}
public void bindConnectivityManager(IConnectivityManager connManager) {
@@ -184,8 +191,10 @@
public void systemReady() {
synchronized (mStatsLock) {
- // read historical stats from disk
- readStatsLocked();
+ // read historical network stats from disk, since policy service
+ // might need them right away. we delay loading detailed UID stats
+ // until actually needed.
+ readNetworkStatsLocked();
}
// watch for network interfaces to be claimed
@@ -214,14 +223,16 @@
mContext.unregisterReceiver(mPollReceiver);
mContext.unregisterReceiver(mShutdownReceiver);
- writeStatsLocked();
- mSummaryStats.clear();
- mDetailStats.clear();
+ writeNetworkStatsLocked();
+ writeUidStatsLocked();
+ mNetworkStats.clear();
+ mUidStats.clear();
+ mUidStatsLoaded = false;
}
/**
* Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and
- * reschedule based on current {@link #mPollInterval} value.
+ * reschedule based on current {@link NetworkStatsSettings#getPollInterval()}.
*/
private void registerPollAlarmLocked() throws RemoteException {
if (mPollIntent != null) {
@@ -232,8 +243,8 @@
mContext, 0, new Intent(ACTION_NETWORK_STATS_POLL), 0);
final long currentRealtime = SystemClock.elapsedRealtime();
- mAlarmManager.setInexactRepeating(
- AlarmManager.ELAPSED_REALTIME, currentRealtime, mPollInterval.get(), mPollIntent);
+ mAlarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, currentRealtime,
+ mSettings.getPollInterval(), mPollIntent);
}
@Override
@@ -244,9 +255,9 @@
// combine all interfaces that match template
final String subscriberId = getActiveSubscriberId();
final NetworkStatsHistory combined = new NetworkStatsHistory(
- mSummaryBucketDuration.get());
- for (InterfaceIdentity ident : mSummaryStats.keySet()) {
- final NetworkStatsHistory history = mSummaryStats.get(ident);
+ mSettings.getNetworkBucketDuration(), estimateNetworkBuckets());
+ for (InterfaceIdentity ident : mNetworkStats.keySet()) {
+ final NetworkStatsHistory history = mNetworkStats.get(ident);
if (ident.matchesTemplate(networkTemplate, subscriberId)) {
combined.recordEntireHistory(history);
}
@@ -259,8 +270,11 @@
public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
- // TODO: return history for requested uid
- return null;
+ synchronized (mStatsLock) {
+ // TODO: combine based on template, if we store that granularity
+ ensureUidStatsLoadedLocked();
+ return mUidStats.get(uid);
+ }
}
@Override
@@ -274,8 +288,8 @@
long[] networkTotal = new long[2];
// combine total from all interfaces that match template
- for (InterfaceIdentity ident : mSummaryStats.keySet()) {
- final NetworkStatsHistory history = mSummaryStats.get(ident);
+ for (InterfaceIdentity ident : mNetworkStats.keySet()) {
+ final NetworkStatsHistory history = mNetworkStats.get(ident);
if (ident.matchesTemplate(networkTemplate, subscriberId)) {
networkTotal = history.getTotalData(start, end, networkTotal);
rx += networkTotal[0];
@@ -283,9 +297,9 @@
}
}
- final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, 1);
+ final NetworkStats stats = new NetworkStats(end - start, 1);
stats.addEntry(IFACE_ALL, UID_ALL, tx, tx);
- return stats.build();
+ return stats;
}
}
@@ -296,17 +310,19 @@
// TODO: apply networktemplate once granular uid stats are stored.
synchronized (mStatsLock) {
- final int size = mDetailStats.size();
- final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size);
+ ensureUidStatsLoadedLocked();
+
+ final int size = mUidStats.size();
+ final NetworkStats stats = new NetworkStats(end - start, size);
long[] total = new long[2];
for (int i = 0; i < size; i++) {
- final int uid = mDetailStats.keyAt(i);
- final NetworkStatsHistory history = mDetailStats.valueAt(i);
+ final int uid = mUidStats.keyAt(i);
+ final NetworkStatsHistory history = mUidStats.valueAt(i);
total = history.getTotalData(start, end, total);
stats.addEntry(IFACE_ALL, uid, total[0], total[1]);
}
- return stats.build();
+ return stats;
}
}
@@ -333,7 +349,7 @@
// permission above.
synchronized (mStatsLock) {
// TODO: acquire wakelock while performing poll
- performPollLocked();
+ performPollLocked(true);
}
}
};
@@ -355,13 +371,12 @@
* {@link InterfaceIdentity}.
*/
private void updateIfacesLocked() {
- if (LOGD) Slog.v(TAG, "updateIfacesLocked()");
+ if (LOGV) Slog.v(TAG, "updateIfacesLocked()");
// take one last stats snapshot before updating iface mapping. this
// isn't perfect, since the kernel may already be counting traffic from
// the updated network.
- // TODO: verify that we only poll summary stats, not uid details
- performPollLocked();
+ performPollLocked(false);
final NetworkState[] states;
try {
@@ -384,11 +399,18 @@
}
}
- private void performPollLocked() {
- if (LOGD) Slog.v(TAG, "performPollLocked()");
+ /**
+ * Periodic poll operation, reading current statistics and recording into
+ * {@link NetworkStatsHistory}.
+ *
+ * @param detailedPoll Indicate if detailed UID stats should be collected
+ * during this poll operation.
+ */
+ private void performPollLocked(boolean detailedPoll) {
+ if (LOGV) Slog.v(TAG, "performPollLocked()");
// try refreshing time source when stale
- if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) {
+ if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) {
mTime.forceRefresh();
}
@@ -396,42 +418,45 @@
final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
: System.currentTimeMillis();
- final NetworkStats summary;
- final NetworkStats detail;
+ final NetworkStats networkStats;
+ final NetworkStats uidStats;
try {
- summary = mNetworkManager.getNetworkStatsSummary();
- detail = mNetworkManager.getNetworkStatsDetail();
+ networkStats = mNetworkManager.getNetworkStatsSummary();
+ uidStats = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null;
} catch (RemoteException e) {
Slog.w(TAG, "problem reading network stats");
return;
}
- performSummaryPollLocked(summary, currentTime);
- performDetailPollLocked(detail, currentTime);
+ performNetworkPollLocked(networkStats, currentTime);
+ if (detailedPoll) {
+ performUidPollLocked(uidStats, currentTime);
+ }
// decide if enough has changed to trigger persist
- final NetworkStats persistDelta = computeStatsDelta(mLastSummaryPersist, summary);
- final long persistThreshold = mPersistThreshold.get();
+ final NetworkStats persistDelta = computeStatsDelta(mLastNetworkPersist, networkStats);
+ final long persistThreshold = mSettings.getPersistThreshold();
for (String iface : persistDelta.getUniqueIfaces()) {
final int index = persistDelta.findIndex(iface, UID_ALL);
if (persistDelta.rx[index] > persistThreshold
|| persistDelta.tx[index] > persistThreshold) {
- writeStatsLocked();
- mLastSummaryPersist = summary;
+ writeNetworkStatsLocked();
+ writeUidStatsLocked();
+ mLastNetworkPersist = networkStats;
break;
}
}
}
/**
- * Update {@link #mSummaryStats} historical usage.
+ * Update {@link #mNetworkStats} historical usage.
*/
- private void performSummaryPollLocked(NetworkStats summary, long currentTime) {
+ private void performNetworkPollLocked(NetworkStats networkStats, long currentTime) {
final ArrayList<String> unknownIface = Lists.newArrayList();
- final NetworkStats delta = computeStatsDelta(mLastSummaryPoll, summary);
+ final NetworkStats delta = computeStatsDelta(mLastNetworkPoll, networkStats);
final long timeStart = currentTime - delta.elapsedRealtime;
- final long maxHistory = mSummaryMaxHistory.get();
+ final long maxHistory = mSettings.getNetworkMaxHistory();
for (String iface : delta.getUniqueIfaces()) {
final InterfaceIdentity ident = mActiveIface.get(iface);
if (ident == null) {
@@ -443,11 +468,11 @@
final long rx = delta.rx[index];
final long tx = delta.tx[index];
- final NetworkStatsHistory history = findOrCreateSummaryLocked(ident);
+ final NetworkStatsHistory history = findOrCreateNetworkLocked(ident);
history.recordData(timeStart, currentTime, rx, tx);
history.removeBucketsBefore(currentTime - maxHistory);
}
- mLastSummaryPoll = summary;
+ mLastNetworkPoll = networkStats;
if (LOGD && unknownIface.size() > 0) {
Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
@@ -455,40 +480,71 @@
}
/**
- * Update {@link #mDetailStats} historical usage.
+ * Update {@link #mUidStats} historical usage.
*/
- private void performDetailPollLocked(NetworkStats detail, long currentTime) {
- final NetworkStats delta = computeStatsDelta(mLastDetailPoll, detail);
+ private void performUidPollLocked(NetworkStats uidStats, long currentTime) {
+ ensureUidStatsLoadedLocked();
+
+ final NetworkStats delta = computeStatsDelta(mLastUidPoll, uidStats);
final long timeStart = currentTime - delta.elapsedRealtime;
- final long maxHistory = mDetailMaxHistory.get();
+ final long maxHistory = mSettings.getUidMaxHistory();
for (int uid : delta.getUniqueUids()) {
+ // TODO: traverse all ifaces once surfaced in stats
final int index = delta.findIndex(IFACE_ALL, uid);
- final long rx = delta.rx[index];
- final long tx = delta.tx[index];
+ if (index != -1) {
+ final long rx = delta.rx[index];
+ final long tx = delta.tx[index];
- final NetworkStatsHistory history = findOrCreateDetailLocked(uid);
- history.recordData(timeStart, currentTime, rx, tx);
- history.removeBucketsBefore(currentTime - maxHistory);
+ final NetworkStatsHistory history = findOrCreateUidLocked(uid);
+ history.recordData(timeStart, currentTime, rx, tx);
+ history.removeBucketsBefore(currentTime - maxHistory);
+ }
}
- mLastDetailPoll = detail;
+ mLastUidPoll = uidStats;
}
- private NetworkStatsHistory findOrCreateSummaryLocked(InterfaceIdentity ident) {
- NetworkStatsHistory stats = mSummaryStats.get(ident);
- if (stats == null) {
- stats = new NetworkStatsHistory(mSummaryBucketDuration.get());
- mSummaryStats.put(ident, stats);
+ private NetworkStatsHistory findOrCreateNetworkLocked(InterfaceIdentity ident) {
+ final long bucketDuration = mSettings.getNetworkBucketDuration();
+ final NetworkStatsHistory existing = mNetworkStats.get(ident);
+
+ // update when no existing, or when bucket duration changed
+ NetworkStatsHistory updated = null;
+ if (existing == null) {
+ updated = new NetworkStatsHistory(bucketDuration, 10);
+ } else if (existing.bucketDuration != bucketDuration) {
+ updated = new NetworkStatsHistory(
+ bucketDuration, estimateResizeBuckets(existing, bucketDuration));
+ updated.recordEntireHistory(existing);
}
- return stats;
+
+ if (updated != null) {
+ mNetworkStats.put(ident, updated);
+ return updated;
+ } else {
+ return existing;
+ }
}
- private NetworkStatsHistory findOrCreateDetailLocked(int uid) {
- NetworkStatsHistory stats = mDetailStats.get(uid);
- if (stats == null) {
- stats = new NetworkStatsHistory(mDetailBucketDuration.get());
- mDetailStats.put(uid, stats);
+ private NetworkStatsHistory findOrCreateUidLocked(int uid) {
+ final long bucketDuration = mSettings.getUidBucketDuration();
+ final NetworkStatsHistory existing = mUidStats.get(uid);
+
+ // update when no existing, or when bucket duration changed
+ NetworkStatsHistory updated = null;
+ if (existing == null) {
+ updated = new NetworkStatsHistory(bucketDuration, 10);
+ } else if (existing.bucketDuration != bucketDuration) {
+ updated = new NetworkStatsHistory(
+ bucketDuration, estimateResizeBuckets(existing, bucketDuration));
+ updated.recordEntireHistory(existing);
}
- return stats;
+
+ if (updated != null) {
+ mUidStats.put(uid, updated);
+ return updated;
+ } else {
+ return existing;
+ }
}
private InterfaceIdentity findOrCreateInterfaceLocked(String iface) {
@@ -500,15 +556,15 @@
return ident;
}
- private void readStatsLocked() {
- if (LOGD) Slog.v(TAG, "readStatsLocked()");
+ private void readNetworkStatsLocked() {
+ if (LOGV) Slog.v(TAG, "readNetworkStatsLocked()");
// clear any existing stats and read from disk
- mSummaryStats.clear();
+ mNetworkStats.clear();
FileInputStream fis = null;
try {
- fis = mSummaryFile.openRead();
+ fis = mNetworkFile.openRead();
final DataInputStream in = new DataInputStream(fis);
// verify file magic header intact
@@ -521,13 +577,14 @@
switch (version) {
case VERSION_CURRENT: {
// file format is pairs of interfaces and stats:
- // summary := size *(InterfaceIdentity NetworkStatsHistory)
+ // network := size *(InterfaceIdentity NetworkStatsHistory)
final int size = in.readInt();
for (int i = 0; i < size; i++) {
final InterfaceIdentity ident = new InterfaceIdentity(in);
final NetworkStatsHistory history = new NetworkStatsHistory(in);
- mSummaryStats.put(ident, history);
+
+ mNetworkStats.put(ident, history);
}
break;
}
@@ -544,30 +601,113 @@
}
}
- private void writeStatsLocked() {
- if (LOGD) Slog.v(TAG, "writeStatsLocked()");
+ private void ensureUidStatsLoadedLocked() {
+ if (!mUidStatsLoaded) {
+ readUidStatsLocked();
+ mUidStatsLoaded = true;
+ }
+ }
+
+ private void readUidStatsLocked() {
+ if (LOGV) Slog.v(TAG, "readUidStatsLocked()");
+
+ // clear any existing stats and read from disk
+ mUidStats.clear();
+
+ FileInputStream fis = null;
+ try {
+ fis = mUidFile.openRead();
+ final DataInputStream in = new DataInputStream(fis);
+
+ // verify file magic header intact
+ final int magic = in.readInt();
+ if (magic != FILE_MAGIC) {
+ throw new ProtocolException("unexpected magic: " + magic);
+ }
+
+ final int version = in.readInt();
+ switch (version) {
+ case VERSION_CURRENT: {
+ // file format is pairs of UIDs and stats:
+ // uid := size *(UID NetworkStatsHistory)
+
+ final int size = in.readInt();
+ for (int i = 0; i < size; i++) {
+ final int uid = in.readInt();
+ final NetworkStatsHistory history = new NetworkStatsHistory(in);
+
+ mUidStats.put(uid, history);
+ }
+ break;
+ }
+ default: {
+ throw new ProtocolException("unexpected version: " + version);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // missing stats is okay, probably first boot
+ } catch (IOException e) {
+ Slog.e(TAG, "problem reading uid stats", e);
+ } finally {
+ IoUtils.closeQuietly(fis);
+ }
+ }
+
+ private void writeNetworkStatsLocked() {
+ if (LOGV) Slog.v(TAG, "writeNetworkStatsLocked()");
// TODO: consider duplicating stats and releasing lock while writing
FileOutputStream fos = null;
try {
- fos = mSummaryFile.startWrite();
+ fos = mNetworkFile.startWrite();
final DataOutputStream out = new DataOutputStream(fos);
out.writeInt(FILE_MAGIC);
out.writeInt(VERSION_CURRENT);
- out.writeInt(mSummaryStats.size());
- for (InterfaceIdentity ident : mSummaryStats.keySet()) {
- final NetworkStatsHistory history = mSummaryStats.get(ident);
+ out.writeInt(mNetworkStats.size());
+ for (InterfaceIdentity ident : mNetworkStats.keySet()) {
+ final NetworkStatsHistory history = mNetworkStats.get(ident);
ident.writeToStream(out);
history.writeToStream(out);
}
- mSummaryFile.finishWrite(fos);
+ mNetworkFile.finishWrite(fos);
} catch (IOException e) {
if (fos != null) {
- mSummaryFile.failWrite(fos);
+ mNetworkFile.failWrite(fos);
+ }
+ }
+ }
+
+ private void writeUidStatsLocked() {
+ if (LOGV) Slog.v(TAG, "writeUidStatsLocked()");
+
+ // TODO: consider duplicating stats and releasing lock while writing
+
+ FileOutputStream fos = null;
+ try {
+ fos = mUidFile.startWrite();
+ final DataOutputStream out = new DataOutputStream(fos);
+
+ out.writeInt(FILE_MAGIC);
+ out.writeInt(VERSION_CURRENT);
+
+ final int size = mUidStats.size();
+
+ out.writeInt(size);
+ for (int i = 0; i < size; i++) {
+ final int uid = mUidStats.keyAt(i);
+ final NetworkStatsHistory history = mUidStats.valueAt(i);
+ out.writeInt(uid);
+ history.writeToStream(out);
+ }
+
+ mUidFile.finishWrite(fos);
+ } catch (IOException e) {
+ if (fos != null) {
+ mUidFile.failWrite(fos);
}
}
}
@@ -590,7 +730,7 @@
}
if (argSet.contains("poll")) {
- performPollLocked();
+ performPollLocked(true);
pw.println("Forced poll");
return;
}
@@ -603,17 +743,20 @@
}
pw.println("Known historical stats:");
- for (InterfaceIdentity ident : mSummaryStats.keySet()) {
- final NetworkStatsHistory stats = mSummaryStats.get(ident);
+ for (InterfaceIdentity ident : mNetworkStats.keySet()) {
+ final NetworkStatsHistory stats = mNetworkStats.get(ident);
pw.print(" ident="); pw.println(ident.toString());
stats.dump(" ", pw);
}
if (argSet.contains("detail")) {
- pw.println("Known detail stats:");
- for (int i = 0; i < mDetailStats.size(); i++) {
- final int uid = mDetailStats.keyAt(i);
- final NetworkStatsHistory stats = mDetailStats.valueAt(i);
+ // since explicitly requested with argument, we're okay to load
+ // from disk if not already in memory.
+ ensureUidStatsLoadedLocked();
+ pw.println("Known UID stats:");
+ for (int i = 0; i < mUidStats.size(); i++) {
+ final int uid = mUidStats.keyAt(i);
+ final NetworkStatsHistory stats = mUidStats.valueAt(i);
pw.print(" UID="); pw.println(uid);
stats.dump(" ", pw);
}
@@ -627,47 +770,29 @@
@Deprecated
private void generateRandomLocked() {
long end = System.currentTimeMillis();
- long start = end - mSummaryMaxHistory.get();
+ long start = end - mSettings.getNetworkMaxHistory();
long rx = 3 * GB_IN_BYTES;
long tx = 2 * GB_IN_BYTES;
- mSummaryStats.clear();
+ mNetworkStats.clear();
for (InterfaceIdentity ident : mActiveIface.values()) {
- final NetworkStatsHistory stats = findOrCreateSummaryLocked(ident);
+ final NetworkStatsHistory stats = findOrCreateNetworkLocked(ident);
stats.generateRandom(start, end, rx, tx);
}
end = System.currentTimeMillis();
- start = end - mDetailMaxHistory.get();
+ start = end - mSettings.getUidMaxHistory();
rx = 500 * MB_IN_BYTES;
tx = 100 * MB_IN_BYTES;
- mDetailStats.clear();
+ mUidStats.clear();
for (ApplicationInfo info : mContext.getPackageManager().getInstalledApplications(0)) {
final int uid = info.uid;
- final NetworkStatsHistory stats = findOrCreateDetailLocked(uid);
+ final NetworkStatsHistory stats = findOrCreateUidLocked(uid);
stats.generateRandom(start, end, rx, tx);
}
}
- private class LongSecureSetting {
- private String mKey;
- private long mDefaultValue;
-
- public LongSecureSetting(String key, long defaultValue) {
- mKey = key;
- mDefaultValue = defaultValue;
- }
-
- public long get() {
- if (mContext != null) {
- return Settings.Secure.getLong(mContext.getContentResolver(), mKey, mDefaultValue);
- } else {
- return mDefaultValue;
- }
- }
- }
-
/**
* Return the delta between two {@link NetworkStats} snapshots, where {@code
* before} can be {@code null}.
@@ -686,4 +811,54 @@
return telephony.getSubscriberId();
}
+ private int estimateNetworkBuckets() {
+ return (int) (mSettings.getNetworkMaxHistory() / mSettings.getNetworkBucketDuration());
+ }
+
+ private int estimateUidBuckets() {
+ return (int) (mSettings.getUidMaxHistory() / mSettings.getUidBucketDuration());
+ }
+
+ private static int estimateResizeBuckets(NetworkStatsHistory existing, long newBucketDuration) {
+ return (int) (existing.bucketCount * existing.bucketDuration / newBucketDuration);
+ }
+
+ /**
+ * Default external settings that read from {@link Settings.Secure}.
+ */
+ private static class DefaultNetworkStatsSettings implements NetworkStatsSettings {
+ private final ContentResolver mResolver;
+
+ public DefaultNetworkStatsSettings(Context context) {
+ mResolver = checkNotNull(context.getContentResolver());
+ // TODO: adjust these timings for production builds
+ }
+
+ private long getSecureLong(String name, long def) {
+ return Settings.Secure.getLong(mResolver, name, def);
+ }
+
+ public long getPollInterval() {
+ return getSecureLong(NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
+ }
+ public long getPersistThreshold() {
+ return getSecureLong(NETSTATS_PERSIST_THRESHOLD, 16 * KB_IN_BYTES);
+ }
+ public long getNetworkBucketDuration() {
+ return getSecureLong(NETSTATS_NETWORK_BUCKET_DURATION, HOUR_IN_MILLIS);
+ }
+ public long getNetworkMaxHistory() {
+ return getSecureLong(NETSTATS_NETWORK_MAX_HISTORY, 90 * DAY_IN_MILLIS);
+ }
+ public long getUidBucketDuration() {
+ return getSecureLong(NETSTATS_UID_BUCKET_DURATION, 2 * HOUR_IN_MILLIS);
+ }
+ public long getUidMaxHistory() {
+ return getSecureLong(NETSTATS_UID_MAX_HISTORY, 90 * DAY_IN_MILLIS);
+ }
+ public long getTimeCacheMaxAge() {
+ return DAY_IN_MILLIS;
+ }
+ }
+
}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 1eeb56b..476aded 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -297,7 +297,7 @@
final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-11-05T00:00:00.000Z");
- final NetworkPolicy policy = new NetworkPolicy(5, 1024L, 1024L);
+ final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 5, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
@@ -307,7 +307,7 @@
final long currentTime = parseTime("2007-11-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-10-20T00:00:00.000Z");
- final NetworkPolicy policy = new NetworkPolicy(20, 1024L, 1024L);
+ final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 20, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
@@ -317,7 +317,7 @@
final long currentTime = parseTime("2007-02-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-01-30T00:00:00.000Z");
- final NetworkPolicy policy = new NetworkPolicy(30, 1024L, 1024L);
+ final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 30, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
@@ -327,7 +327,7 @@
final long currentTime = parseTime("2007-03-14T00:00:00.000Z");
final long expectedCycle = parseTime("2007-03-01T00:00:00.000Z");
- final NetworkPolicy policy = new NetworkPolicy(30, 1024L, 1024L);
+ final NetworkPolicy policy = new NetworkPolicy(TEMPLATE_WIFI, null, 30, 1024L, 1024L);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
assertEquals(expectedCycle, actualCycle);
}
@@ -357,8 +357,8 @@
expectTime(TIME_MAR_10 + elapsedRealtime);
// pretend that 512 bytes total have happened
- stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry(
- TEST_IFACE, UID_ALL, 256L, 256L).build();
+ stats = new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_ALL, 256L, 256L);
expect(mStatsService.getSummaryForNetwork(TIME_FEB_15, TIME_MAR_10, TEMPLATE_WIFI, null))
.andReturn(stats).atLeastOnce();
@@ -366,7 +366,7 @@
// TODO: write up NetworkManagementService mock
replay();
- mService.setNetworkPolicy(TEMPLATE_WIFI, null, new NetworkPolicy(CYCLE_DAY, 1024L, 2048L));
+ setNetworkPolicies(new NetworkPolicy(TEMPLATE_WIFI, null, CYCLE_DAY, 1024L, 2048L));
verifyAndReset();
}
@@ -376,6 +376,10 @@
return result.toMillis(true);
}
+ private void setNetworkPolicies(NetworkPolicy... policies) {
+ mService.setNetworkPolicies(policies);
+ }
+
private static NetworkState buildWifi() {
final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
info.setDetailedState(DetailedState.CONNECTED, null, null);
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 9846372..2457ff3 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -18,10 +18,13 @@
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.UID_ALL;
import static android.net.TrafficStats.TEMPLATE_WIFI;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
import static org.easymock.EasyMock.anyLong;
import static org.easymock.EasyMock.createMock;
@@ -47,6 +50,7 @@
import android.util.TrustedTime;
import com.android.server.net.NetworkStatsService;
+import com.android.server.net.NetworkStatsService.NetworkStatsSettings;
import org.easymock.EasyMock;
@@ -62,12 +66,16 @@
private static final String TEST_IFACE = "test0";
private static final long TEST_START = 1194220800000L;
+ private static final int TEST_UID_1 = 1001;
+ private static final int TEST_UID_2 = 1002;
+
private BroadcastInterceptingContext mServiceContext;
private File mStatsDir;
private INetworkManagementService mNetManager;
private IAlarmManager mAlarmManager;
private TrustedTime mTime;
+ private NetworkStatsSettings mSettings;
private IConnectivityManager mConnManager;
private NetworkStatsService mService;
@@ -82,12 +90,14 @@
mNetManager = createMock(INetworkManagementService.class);
mAlarmManager = createMock(IAlarmManager.class);
mTime = createMock(TrustedTime.class);
+ mSettings = createMock(NetworkStatsSettings.class);
mConnManager = createMock(IConnectivityManager.class);
mService = new NetworkStatsService(
- mServiceContext, mNetManager, mAlarmManager, mTime, mStatsDir);
+ mServiceContext, mNetManager, mAlarmManager, mTime, mStatsDir, mSettings);
mService.bindConnectivityManager(mConnManager);
+ expectDefaultSettings();
expectSystemReady();
replay();
@@ -114,115 +124,93 @@
super.tearDown();
}
- private static NetworkState buildWifi() {
- final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
- info.setDetailedState(DetailedState.CONNECTED, null, null);
- final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(TEST_IFACE);
- return new NetworkState(info, prop, null);
- }
-
- public void testHistoryForWifi() throws Exception {
+ public void testSummaryStatsWifi() throws Exception {
long elapsedRealtime = 0;
- NetworkState[] state = null;
- NetworkStats stats = null;
- NetworkStats detail = null;
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
- state = new NetworkState[] { buildWifi() };
- stats = new NetworkStats.Builder(elapsedRealtime, 0).build();
- detail = new NetworkStats.Builder(elapsedRealtime, 0).build();
-
- expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
- expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkState(buildWifiState());
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
- verifyAndReset();
// verify service has empty history for wifi
assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L);
+ verifyAndReset();
// modify some number on wifi, and trigger poll event
elapsedRealtime += HOUR_IN_MILLIS;
- stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry(
- TEST_IFACE, UID_ALL, 1024L, 2048L).build();
-
- expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
- expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_ALL, 1024L, 2048L));
+ expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
replay();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
- verifyAndReset();
// verify service recorded history
assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L);
+ verifyAndReset();
// and bump forward again, with counters going higher. this is
// important, since polling should correctly subtract last snapshot.
elapsedRealtime += DAY_IN_MILLIS;
- stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry(
- TEST_IFACE, UID_ALL, 4096L, 8192L).build();
-
- expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
- expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_ALL, 4096L, 8192L));
+ expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
replay();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
- verifyAndReset();
// verify service recorded history
assertNetworkTotal(TEMPLATE_WIFI, 4096L, 8192L);
+ verifyAndReset();
+
}
- public void testHistoryForRebootPersist() throws Exception {
+ public void testStatsRebootPersist() throws Exception {
long elapsedRealtime = 0;
- NetworkState[] state = null;
- NetworkStats stats = null;
- NetworkStats detail = null;
-
- // assert that no stats file exists
- final File statsFile = new File(mStatsDir, "netstats.bin");
- assertFalse(statsFile.exists());
+ assertStatsFilesExist(false);
// pretend that wifi network comes online; service should ask about full
// network state, and poll any existing interfaces before updating.
- state = new NetworkState[] { buildWifi() };
- stats = new NetworkStats.Builder(elapsedRealtime, 0).build();
- detail = new NetworkStats.Builder(elapsedRealtime, 0).build();
-
- expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
- expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
- expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkState(buildWifiState());
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
replay();
mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
- verifyAndReset();
// verify service has empty history for wifi
assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L);
+ verifyAndReset();
// modify some number on wifi, and trigger poll event
elapsedRealtime += HOUR_IN_MILLIS;
- stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry(
- TEST_IFACE, UID_ALL, 1024L, 2048L).build();
-
- expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
- expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
expectTime(TEST_START + elapsedRealtime);
+ expectDefaultSettings();
+ expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_ALL, 1024L, 2048L));
+ // TODO: switch these stats to specific iface
+ expectNetworkStatsDetail(new NetworkStats(elapsedRealtime, 2)
+ .addEntry(IFACE_ALL, TEST_UID_1, 512L, 256L)
+ .addEntry(IFACE_ALL, TEST_UID_2, 128L, 128L));
replay();
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
- verifyAndReset();
// verify service recorded history
assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L);
+ assertUidTotal(TEST_UID_1, TEMPLATE_WIFI, 512L, 256L);
+ assertUidTotal(TEST_UID_2, TEMPLATE_WIFI, 128L, 128L);
+ verifyAndReset();
// graceful shutdown system, which should trigger persist of stats, and
// clear any values in memory.
@@ -230,18 +218,84 @@
// talk with zombie service to assert stats have gone; and assert that
// we persisted them to file.
+ expectDefaultSettings();
+ replay();
assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L);
- assertTrue(statsFile.exists());
+ verifyAndReset();
+
+ assertStatsFilesExist(true);
// boot through serviceReady() again
+ expectDefaultSettings();
expectSystemReady();
replay();
mService.systemReady();
- verifyAndReset();
// after systemReady(), we should have historical stats loaded again
assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L);
+ assertUidTotal(TEST_UID_1, TEMPLATE_WIFI, 512L, 256L);
+ assertUidTotal(TEST_UID_2, TEMPLATE_WIFI, 128L, 128L);
+ verifyAndReset();
+
+ }
+
+ public void testStatsBucketResize() throws Exception {
+ long elapsedRealtime = 0;
+ NetworkStatsHistory history = null;
+ long[] total = null;
+
+ assertStatsFilesExist(false);
+
+ // pretend that wifi network comes online; service should ask about full
+ // network state, and poll any existing interfaces before updating.
+ expectTime(TEST_START + elapsedRealtime);
+ expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
+ expectNetworkState(buildWifiState());
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+ verifyAndReset();
+
+ // modify some number on wifi, and trigger poll event
+ elapsedRealtime += 2 * HOUR_IN_MILLIS;
+ expectTime(TEST_START + elapsedRealtime);
+ expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
+ expectNetworkStatsSummary(new NetworkStats(elapsedRealtime, 1)
+ .addEntry(TEST_IFACE, UID_ALL, 512L, 512L));
+ expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // verify service recorded history
+ history = mService.getHistoryForNetwork(TEMPLATE_WIFI);
+ total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
+ assertEquals(512L, total[0]);
+ assertEquals(512L, total[1]);
+ assertEquals(HOUR_IN_MILLIS, history.bucketDuration);
+ assertEquals(2, history.bucketCount);
+ verifyAndReset();
+
+ // now change bucket duration setting and trigger another poll with
+ // exact same values, which should resize existing buckets.
+ expectTime(TEST_START + elapsedRealtime);
+ expectSettings(0L, 30 * MINUTE_IN_MILLIS, WEEK_IN_MILLIS);
+ expectNetworkStatsSummary(buildEmptyStats(elapsedRealtime));
+ expectNetworkStatsDetail(buildEmptyStats(elapsedRealtime));
+
+ replay();
+ mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+
+ // verify identical stats, but spread across 4 buckets now
+ history = mService.getHistoryForNetwork(TEMPLATE_WIFI);
+ total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
+ assertEquals(512L, total[0]);
+ assertEquals(512L, total[1]);
+ assertEquals(30 * MINUTE_IN_MILLIS, history.bucketDuration);
+ assertEquals(4, history.bucketCount);
+ verifyAndReset();
}
@@ -252,6 +306,13 @@
assertEquals(tx, total[1]);
}
+ private void assertUidTotal(int uid, int template, long rx, long tx) {
+ final NetworkStatsHistory history = mService.getHistoryForUid(uid, template);
+ final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
+ assertEquals(rx, total[0]);
+ assertEquals(tx, total[1]);
+ }
+
private void expectSystemReady() throws Exception {
mAlarmManager.remove(isA(PendingIntent.class));
expectLastCall().anyTimes();
@@ -261,7 +322,34 @@
expectLastCall().atLeastOnce();
}
- public void expectTime(long currentTime) throws Exception {
+ private void expectNetworkState(NetworkState... state) throws Exception {
+ expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+ }
+
+ private void expectNetworkStatsSummary(NetworkStats summary) throws Exception {
+ expect(mNetManager.getNetworkStatsSummary()).andReturn(summary).atLeastOnce();
+ }
+
+ private void expectNetworkStatsDetail(NetworkStats detail) throws Exception {
+ expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
+ }
+
+ private void expectDefaultSettings() throws Exception {
+ expectSettings(0L, HOUR_IN_MILLIS, WEEK_IN_MILLIS);
+ }
+
+ private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory)
+ throws Exception {
+ expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes();
+ expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes();
+ expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes();
+ expect(mSettings.getNetworkMaxHistory()).andReturn(maxHistory).anyTimes();
+ expect(mSettings.getUidBucketDuration()).andReturn(bucketDuration).anyTimes();
+ expect(mSettings.getUidMaxHistory()).andReturn(maxHistory).anyTimes();
+ expect(mSettings.getTimeCacheMaxAge()).andReturn(DAY_IN_MILLIS).anyTimes();
+ }
+
+ private void expectTime(long currentTime) throws Exception {
expect(mTime.forceRefresh()).andReturn(false).anyTimes();
expect(mTime.hasCache()).andReturn(true).anyTimes();
expect(mTime.currentTimeMillis()).andReturn(currentTime).anyTimes();
@@ -269,12 +357,36 @@
expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
}
+ private void assertStatsFilesExist(boolean exist) {
+ final File summaryFile = new File(mStatsDir, "netstats.bin");
+ final File detailFile = new File(mStatsDir, "netstats_uid.bin");
+ if (exist) {
+ assertTrue(summaryFile.exists());
+ assertTrue(detailFile.exists());
+ } else {
+ assertFalse(summaryFile.exists());
+ assertFalse(detailFile.exists());
+ }
+ }
+
+ private static NetworkState buildWifiState() {
+ final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
+ info.setDetailedState(DetailedState.CONNECTED, null, null);
+ final LinkProperties prop = new LinkProperties();
+ prop.setInterfaceName(TEST_IFACE);
+ return new NetworkState(info, prop, null);
+ }
+
+ private static NetworkStats buildEmptyStats(long elapsedRealtime) {
+ return new NetworkStats(elapsedRealtime, 0);
+ }
+
private void replay() {
- EasyMock.replay(mNetManager, mAlarmManager, mTime, mConnManager);
+ EasyMock.replay(mNetManager, mAlarmManager, mTime, mSettings, mConnManager);
}
private void verifyAndReset() {
- EasyMock.verify(mNetManager, mAlarmManager, mTime, mConnManager);
- EasyMock.reset(mNetManager, mAlarmManager, mTime, mConnManager);
+ EasyMock.verify(mNetManager, mAlarmManager, mTime, mSettings, mConnManager);
+ EasyMock.reset(mNetManager, mAlarmManager, mTime, mSettings, mConnManager);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
index d1ee4f6..30afdd8 100644
--- a/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ThrottleServiceTest.java
@@ -288,11 +288,10 @@
*/
public void expectGetInterfaceCounter(long rx, long tx) throws Exception {
// TODO: provide elapsedRealtime mock to match TimeAuthority
- final NetworkStats.Builder stats = new NetworkStats.Builder(
- SystemClock.elapsedRealtime(), 1);
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
stats.addEntry(TEST_IFACE, NetworkStats.UID_ALL, rx, tx);
- expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats.build()).atLeastOnce();
+ expect(mMockNMService.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
}
/**
diff --git a/tests/GridLayoutTest/res/layout/grid3.xml b/tests/GridLayoutTest/res/layout/grid3.xml
index 31dc75a..5cdacf7 100644
--- a/tests/GridLayoutTest/res/layout/grid3.xml
+++ b/tests/GridLayoutTest/res/layout/grid3.xml
@@ -19,14 +19,17 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
+
android:useDefaultMargins="true"
android:marginsIncludedInAlignment="false"
+
android:columnCount="4"
>
<TextView
android:text="Email account"
android:textSize="48dip"
+
android:layout_columnSpan="4"
android:layout_gravity="center_horizontal"
/>
@@ -34,12 +37,14 @@
<TextView
android:text="You can configure email in just a few steps:"
android:textSize="20dip"
+
android:layout_columnSpan="4"
android:layout_gravity="left"
/>
<TextView
android:text="Email address:"
+
android:layout_gravity="right"
/>
@@ -49,6 +54,7 @@
<TextView
android:text="Password:"
+
android:layout_column="0"
android:layout_gravity="right"
/>
@@ -58,14 +64,15 @@
/>
<Space
- android:layout_rowWeight="1"
- android:layout_columnWeight="1"
android:layout_row="4"
android:layout_column="2"
+ android:layout_rowWeight="1"
+ android:layout_columnWeight="1"
/>
<Button
android:text="Manual setup"
+
android:layout_row="5"
android:layout_column="3"
android:layout_gravity="fill_horizontal"
@@ -73,6 +80,7 @@
<Button
android:text="Next"
+
android:layout_column="3"
android:layout_gravity="fill_horizontal"
/>
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
index 5e29cf1..32365d7 100644
--- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
+++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
@@ -20,11 +20,15 @@
import android.content.Context;
import android.os.Bundle;
import android.view.View;
-
-import android.widget.*;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.GridLayout;
+import android.widget.Space;
+import android.widget.TextView;
import static android.text.InputType.TYPE_CLASS_TEXT;
-import static android.view.inputmethod.EditorInfo.*;
+import static android.view.inputmethod.EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
+import static android.view.inputmethod.EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
import static android.widget.GridLayout.*;
public class Activity2 extends Activity {
@@ -42,12 +46,12 @@
Group row6 = new Group(6, CENTER);
Group row7 = new Group(7, CENTER);
- Group col1a = new Group(1, 5, CENTER);
- Group col1b = new Group(1, 5, LEFT);
+ Group col1a = new Group(1, 4, CENTER);
+ Group col1b = new Group(1, 4, LEFT);
Group col1c = new Group(1, RIGHT);
- Group col2 = new Group(2, LEFT);
- Group col3 = new Group(3, FILL);
- Group col4 = new Group(4, FILL);
+ Group col2 = new Group(2, LEFT);
+ Group col3 = new Group(3, FILL);
+ Group col4 = new Group(4, FILL);
{
TextView v = new TextView(context);
@@ -55,20 +59,17 @@
v.setText("Email setup");
vg.addView(v, new LayoutParams(row1, col1a));
}
-
{
TextView v = new TextView(context);
v.setTextSize(20);
v.setText("You can configure email in just a few steps:");
vg.addView(v, new LayoutParams(row2, col1b));
}
-
{
TextView v = new TextView(context);
v.setText("Email address:");
vg.addView(v, new LayoutParams(row3, col1c));
}
-
{
EditText v = new EditText(context);
v.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
@@ -78,13 +79,11 @@
vg.addView(v, lp);
}
}
-
{
TextView v = new TextView(context);
v.setText("Password:");
vg.addView(v, new LayoutParams(row4, col1c));
}
-
{
TextView v = new EditText(context);
v.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
@@ -94,7 +93,6 @@
vg.addView(v, lp);
}
}
-
{
Space v = new Space(context);
{
@@ -104,13 +102,11 @@
vg.addView(v, lp);
}
}
-
{
Button v = new Button(context);
v.setText("Manual setup");
vg.addView(v, new LayoutParams(row6, col4));
}
-
{
Button v = new Button(context);
v.setText("Next");
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index b5f1a17..4837eb9 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -528,12 +528,8 @@
public void answerCall(String sessionDescription, int timeout) {
synchronized (SipSessionGroup.this) {
if (mPeerProfile == null) return;
- try {
- processCommand(new MakeCallCommand(mPeerProfile,
- sessionDescription, timeout));
- } catch (SipException e) {
- onError(e);
- }
+ doCommandAsync(new MakeCallCommand(mPeerProfile,
+ sessionDescription, timeout));
}
}
diff --git a/wifi/java/android/net/wifi/SupplicantState.java b/wifi/java/android/net/wifi/SupplicantState.java
index 91e685f..509b02c 100644
--- a/wifi/java/android/net/wifi/SupplicantState.java
+++ b/wifi/java/android/net/wifi/SupplicantState.java
@@ -216,6 +216,28 @@
}
}
+ static boolean isDriverActive(SupplicantState state) {
+ switch(state) {
+ case DISCONNECTED:
+ case DORMANT:
+ case INACTIVE:
+ case AUTHENTICATING:
+ case ASSOCIATING:
+ case ASSOCIATED:
+ case SCANNING:
+ case FOUR_WAY_HANDSHAKE:
+ case GROUP_HANDSHAKE:
+ case COMPLETED:
+ return true;
+ case INTERFACE_DISABLED:
+ case UNINITIALIZED:
+ case INVALID:
+ return false;
+ default:
+ throw new IllegalArgumentException("Unknown supplicant state");
+ }
+ }
+
/** Implement the Parcelable interface {@hide} */
public int describeContents() {
return 0;
diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java
index 4a45825..4ec4cfc 100644
--- a/wifi/java/android/net/wifi/WifiMonitor.java
+++ b/wifi/java/android/net/wifi/WifiMonitor.java
@@ -107,7 +107,7 @@
* <pre>
* CTRL-EVENT-DRIVER-STATE state
* </pre>
- * <code>state</code> is either STARTED or STOPPED
+ * <code>state</code> can be HANGED
*/
private static final String driverStateEvent = "DRIVER-STATE";
/**
@@ -304,11 +304,7 @@
if (state == null) {
return;
}
- if (state.equals("STOPPED")) {
- mWifiStateMachine.notifyDriverStopped();
- } else if (state.equals("STARTED")) {
- mWifiStateMachine.notifyDriverStarted();
- } else if (state.equals("HANGED")) {
+ if (state.equals("HANGED")) {
mWifiStateMachine.notifyDriverHung();
}
}
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index 6df37bb..3df3736 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -212,22 +212,18 @@
static final int SUP_CONNECTION_EVENT = BASE + 31;
/* Connection to supplicant lost */
static final int SUP_DISCONNECTION_EVENT = BASE + 32;
- /* Driver start completed */
- static final int DRIVER_START_EVENT = BASE + 33;
- /* Driver stop completed */
- static final int DRIVER_STOP_EVENT = BASE + 34;
- /* Network connection completed */
- static final int NETWORK_CONNECTION_EVENT = BASE + 36;
+ /* Network connection completed */
+ static final int NETWORK_CONNECTION_EVENT = BASE + 33;
/* Network disconnection completed */
- static final int NETWORK_DISCONNECTION_EVENT = BASE + 37;
+ static final int NETWORK_DISCONNECTION_EVENT = BASE + 34;
/* Scan results are available */
- static final int SCAN_RESULTS_EVENT = BASE + 38;
+ static final int SCAN_RESULTS_EVENT = BASE + 35;
/* Supplicate state changed */
- static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 39;
+ static final int SUPPLICANT_STATE_CHANGE_EVENT = BASE + 36;
/* Password failure and EAP authentication failure */
- static final int AUTHENTICATION_FAILURE_EVENT = BASE + 40;
+ static final int AUTHENTICATION_FAILURE_EVENT = BASE + 37;
/* WPS overlap detected */
- static final int WPS_OVERLAP_EVENT = BASE + 41;
+ static final int WPS_OVERLAP_EVENT = BASE + 38;
/* Supplicant commands */
@@ -1421,6 +1417,35 @@
return mNetworkInfo.getDetailedState();
}
+
+ private SupplicantState handleSupplicantStateChange(Message message) {
+ StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
+ SupplicantState state = stateChangeResult.state;
+ // Supplicant state change
+ // [31-13] Reserved for future use
+ // [8 - 0] Supplicant state (as defined in SupplicantState.java)
+ // 50023 supplicant_state_changed (custom|1|5)
+ EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
+ mWifiInfo.setSupplicantState(state);
+ // Network id is only valid when we start connecting
+ if (SupplicantState.isConnecting(state)) {
+ mWifiInfo.setNetworkId(stateChangeResult.networkId);
+ } else {
+ mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
+ }
+
+ if (state == SupplicantState.ASSOCIATING) {
+ /* BSSID is valid only in ASSOCIATING state */
+ mWifiInfo.setBSSID(stateChangeResult.BSSID);
+ }
+ setNetworkDetailedState(WifiInfo.getDetailedStateOf(state));
+
+ mSupplicantStateTracker.sendMessage(Message.obtain(message));
+ mWpsStateMachine.sendMessage(Message.obtain(message));
+
+ return state;
+ }
+
/**
* Resets the Wi-Fi Connections by clearing any state, resetting any sockets
* using the interface, stopping DHCP & disabling interface
@@ -1674,14 +1699,6 @@
sendMessage(SCAN_RESULTS_EVENT);
}
- void notifyDriverStarted() {
- sendMessage(DRIVER_START_EVENT);
- }
-
- void notifyDriverStopped() {
- sendMessage(DRIVER_STOP_EVENT);
- }
-
void notifyDriverHung() {
setWifiEnabled(false);
setWifiEnabled(true);
@@ -1737,8 +1754,6 @@
case CMD_REASSOCIATE:
case SUP_CONNECTION_EVENT:
case SUP_DISCONNECTION_EVENT:
- case DRIVER_START_EVENT:
- case DRIVER_STOP_EVENT:
case NETWORK_CONNECTION_EVENT:
case NETWORK_DISCONNECTION_EVENT:
case SCAN_RESULTS_EVENT:
@@ -2284,13 +2299,19 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
- case DRIVER_START_EVENT:
- transitionTo(mDriverStartedState);
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ SupplicantState state = handleSupplicantStateChange(message);
+ /* If suplicant is exiting out of INTERFACE_DISABLED state into
+ * a state that indicates driver has started, it is ready to
+ * receive driver commands
+ */
+ if (SupplicantState.isDriverActive(state)) {
+ transitionTo(mDriverStartedState);
+ }
break;
/* Queue driver commands & connection events */
case CMD_START_DRIVER:
case CMD_STOP_DRIVER:
- case SUPPLICANT_STATE_CHANGE_EVENT:
case NETWORK_CONNECTION_EVENT:
case NETWORK_DISCONNECTION_EVENT:
case AUTHENTICATION_FAILURE_EVENT:
@@ -2429,8 +2450,11 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch(message.what) {
- case DRIVER_STOP_EVENT:
- transitionTo(mDriverStoppedState);
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ SupplicantState state = handleSupplicantStateChange(message);
+ if (state == SupplicantState.INTERFACE_DISABLED) {
+ transitionTo(mDriverStoppedState);
+ }
break;
/* Queue driver commands */
case CMD_START_DRIVER:
@@ -2465,11 +2489,23 @@
public boolean processMessage(Message message) {
if (DBG) Log.d(TAG, getName() + message.toString() + "\n");
switch (message.what) {
- case CMD_START_DRIVER:
- mWakeLock.acquire();
- WifiNative.startDriverCommand();
- transitionTo(mDriverStartingState);
- mWakeLock.release();
+ case CMD_START_DRIVER:
+ mWakeLock.acquire();
+ WifiNative.startDriverCommand();
+ mWakeLock.release();
+ break;
+ case SUPPLICANT_STATE_CHANGE_EVENT:
+ SupplicantState state = handleSupplicantStateChange(message);
+ /* A driver start causes supplicant to first report an INTERFACE_DISABLED
+ * state before transitioning out of it for connection. Stay in
+ * DriverStoppedState until we get an INTERFACE_DISABLED state and transition
+ * to DriverStarting upon getting that
+ * TODO: Fix this when the supplicant can be made to just transition out of
+ * INTERFACE_DISABLED state when driver gets started
+ */
+ if (state == SupplicantState.INTERFACE_DISABLED) {
+ transitionTo(mDriverStartingState);
+ }
break;
default:
return NOT_HANDLED;
@@ -2535,29 +2571,8 @@
sendErrorBroadcast(WifiManager.WPS_OVERLAP_ERROR);
break;
case SUPPLICANT_STATE_CHANGE_EVENT:
- stateChangeResult = (StateChangeResult) message.obj;
- SupplicantState state = stateChangeResult.state;
- // Supplicant state change
- // [31-13] Reserved for future use
- // [8 - 0] Supplicant state (as defined in SupplicantState.java)
- // 50023 supplicant_state_changed (custom|1|5)
- EventLog.writeEvent(EVENTLOG_SUPPLICANT_STATE_CHANGED, state.ordinal());
- mWifiInfo.setSupplicantState(state);
- // Network id is only valid when we start connecting
- if (SupplicantState.isConnecting(state)) {
- mWifiInfo.setNetworkId(stateChangeResult.networkId);
- } else {
- mWifiInfo.setNetworkId(WifiConfiguration.INVALID_NETWORK_ID);
- }
-
- if (state == SupplicantState.ASSOCIATING) {
- /* BSSID is valid only in ASSOCIATING state */
- mWifiInfo.setBSSID(stateChangeResult.BSSID);
- }
-
- mSupplicantStateTracker.sendMessage(Message.obtain(message));
- mWpsStateMachine.sendMessage(Message.obtain(message));
- break;
+ handleSupplicantStateChange(message);
+ break;
/* Do a redundant disconnect without transition */
case CMD_DISCONNECT:
WifiNative.disconnectCommand();
@@ -2964,12 +2979,7 @@
/* Ignore network disconnect */
case NETWORK_DISCONNECTION_EVENT:
break;
- case SUPPLICANT_STATE_CHANGE_EVENT:
- StateChangeResult stateChangeResult = (StateChangeResult) message.obj;
- setNetworkDetailedState(WifiInfo.getDetailedStateOf(stateChangeResult.state));
- /* DriverStartedState does the rest of the handling */
- return NOT_HANDLED;
- case CMD_START_SCAN:
+ case CMD_START_SCAN:
/* Disable background scan temporarily during a regular scan */
if (mEnableBackgroundScan) {
WifiNative.enableBackgroundScanCommand(false);