Merge "Add some extra debug information because this error is only happening on the build server." into ics-aah
diff --git a/api/current.txt b/api/current.txt
index b180f7d..808cb6f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18637,6 +18637,7 @@
method public android.os.Bundle getBundle();
method public java.lang.String getLocale();
method public void onCancel();
+ method public void onClose();
method public abstract void onCreate();
method public abstract android.view.textservice.SuggestionsInfo onGetSuggestions(android.view.textservice.TextInfo, int);
method public android.view.textservice.SuggestionsInfo[] onGetSuggestionsMultiple(android.view.textservice.TextInfo[], int, boolean);
diff --git a/core/java/android/net/EthernetDataTracker.java b/core/java/android/net/EthernetDataTracker.java
index 02e81b6..cff5df2 100644
--- a/core/java/android/net/EthernetDataTracker.java
+++ b/core/java/android/net/EthernetDataTracker.java
@@ -49,6 +49,7 @@
private LinkCapabilities mLinkCapabilities;
private NetworkInfo mNetworkInfo;
private InterfaceObserver mInterfaceObserver;
+ private String mHwAddr;
/* For sending events to connectivity service handler */
private Handler mCsHandler;
@@ -102,6 +103,7 @@
mLinkProperties = new LinkProperties();
mLinkCapabilities = new LinkCapabilities();
mLinkUp = false;
+ mHwAddr = null;
mNetworkInfo.setIsAvailable(false);
setTeardownRequested(false);
@@ -132,7 +134,7 @@
mLinkProperties.clear();
mNetworkInfo.setIsAvailable(false);
- mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
+ mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
Message msg = mCsHandler.obtainMessage(EVENT_CONFIGURATION_CHANGED, mNetworkInfo);
msg.sendToTarget();
@@ -169,7 +171,7 @@
mLinkProperties = dhcpInfoInternal.makeLinkProperties();
mLinkProperties.setInterfaceName(mIface);
- mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+ mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
Message msg = mCsHandler.obtainMessage(EVENT_STATE_CHANGED, mNetworkInfo);
msg.sendToTarget();
}
@@ -218,6 +220,12 @@
mIface = iface;
InterfaceConfiguration config = service.getInterfaceConfig(iface);
mLinkUp = config.isActive();
+ if (config != null && mHwAddr == null) {
+ mHwAddr = config.hwAddr;
+ if (mHwAddr != null) {
+ mNetworkInfo.setExtraInfo(mHwAddr);
+ }
+ }
reconnect();
break;
}
diff --git a/core/java/android/net/NetworkInfo.java b/core/java/android/net/NetworkInfo.java
index 537750a..1e645a1 100644
--- a/core/java/android/net/NetworkInfo.java
+++ b/core/java/android/net/NetworkInfo.java
@@ -346,6 +346,18 @@
}
/**
+ * Set the extraInfo field.
+ * @param extraInfo an optional {@code String} providing addditional network state
+ * information passed up from the lower networking layers.
+ * @hide
+ */
+ public void setExtraInfo(String extraInfo) {
+ synchronized (this) {
+ this.mExtraInfo = extraInfo;
+ }
+ }
+
+ /**
* Report the reason an attempt to establish connectivity failed,
* if one is available.
* @return the reason for failure, or null if not available
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 69ac1e7..5c6ef1a 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -16,10 +16,11 @@
package android.net;
+import static com.android.internal.util.Preconditions.checkNotNull;
+
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
-import android.util.Log;
import android.util.SparseBooleanArray;
import com.android.internal.util.Objects;
@@ -54,6 +55,8 @@
/** {@link #tag} value for total data across all tags. */
public static final int TAG_NONE = 0;
+ // TODO: move fields to "mVariable" notation
+
/**
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
* generated.
@@ -295,8 +298,33 @@
*/
public int findIndex(String iface, int uid, int set, int tag) {
for (int i = 0; i < size; i++) {
- if (Objects.equal(iface, this.iface[i]) && uid == this.uid[i] && set == this.set[i]
- && tag == this.tag[i]) {
+ if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
+ && Objects.equal(iface, this.iface[i])) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Find first stats index that matches the requested parameters, starting
+ * search around the hinted index as an optimization.
+ */
+ // @VisibleForTesting
+ public int findIndexHinted(String iface, int uid, int set, int tag, int hintIndex) {
+ for (int offset = 0; offset < size; offset++) {
+ final int halfOffset = offset / 2;
+
+ // search outwards from hint index, alternating forward and backward
+ final int i;
+ if (offset % 2 == 0) {
+ i = (hintIndex + halfOffset) % size;
+ } else {
+ i = (size + hintIndex - halfOffset - 1) % size;
+ }
+
+ if (uid == this.uid[i] && set == this.set[i] && tag == this.tag[i]
+ && Objects.equal(iface, this.iface[i])) {
return i;
}
}
@@ -423,40 +451,10 @@
* Subtract the given {@link NetworkStats}, effectively leaving the delta
* between two snapshots in time. Assumes that statistics rows collect over
* time, and that none of them have disappeared.
- *
- * @throws IllegalArgumentException when given {@link NetworkStats} is
- * non-monotonic.
*/
- public NetworkStats subtract(NetworkStats value) {
- return subtract(value, true, false);
- }
-
- /**
- * Subtract the given {@link NetworkStats}, effectively leaving the delta
- * between two snapshots in time. Assumes that statistics rows collect over
- * time, and that none of them have disappeared.
- * <p>
- * Instead of throwing when counters are non-monotonic, this variant clamps
- * results to never be negative.
- */
- public NetworkStats subtractClamped(NetworkStats value) {
- return subtract(value, false, true);
- }
-
- /**
- * Subtract the given {@link NetworkStats}, effectively leaving the delta
- * between two snapshots in time. Assumes that statistics rows collect over
- * time, and that none of them have disappeared.
- *
- * @param enforceMonotonic Validate that incoming value is strictly
- * monotonic compared to this object.
- * @param clampNegative Instead of throwing like {@code enforceMonotonic},
- * clamp resulting counters at 0 to prevent negative values.
- */
- private NetworkStats subtract(
- NetworkStats value, boolean enforceMonotonic, boolean clampNegative) {
+ public NetworkStats subtract(NetworkStats value) throws NonMonotonicException {
final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
- if (enforceMonotonic && deltaRealtime < 0) {
+ if (deltaRealtime < 0) {
throw new IllegalArgumentException("found non-monotonic realtime");
}
@@ -470,7 +468,7 @@
entry.tag = tag[i];
// find remote row that matches, and subtract
- final int j = value.findIndex(entry.iface, entry.uid, entry.set, entry.tag);
+ final int j = value.findIndexHinted(entry.iface, entry.uid, entry.set, entry.tag, i);
if (j == -1) {
// newly appearing row, return entire value
entry.rxBytes = rxBytes[i];
@@ -485,20 +483,10 @@
entry.txBytes = txBytes[i] - value.txBytes[j];
entry.txPackets = txPackets[i] - value.txPackets[j];
entry.operations = operations[i] - value.operations[j];
- if (enforceMonotonic
- && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
- || entry.txPackets < 0 || entry.operations < 0)) {
- Log.v(TAG, "lhs=" + this);
- Log.v(TAG, "rhs=" + value);
- throw new IllegalArgumentException(
- "found non-monotonic values at lhs[" + i + "] - rhs[" + j + "]");
- }
- if (clampNegative) {
- entry.rxBytes = Math.max(0, entry.rxBytes);
- entry.rxPackets = Math.max(0, entry.rxPackets);
- entry.txBytes = Math.max(0, entry.txBytes);
- entry.txPackets = Math.max(0, entry.txPackets);
- entry.operations = Math.max(0, entry.operations);
+
+ if (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
+ || entry.txPackets < 0 || entry.operations < 0) {
+ throw new NonMonotonicException(this, i, value, j);
}
}
@@ -564,6 +552,24 @@
return stats;
}
+ /**
+ * Return all rows except those attributed to the requested UID; doesn't
+ * mutate the original structure.
+ */
+ public NetworkStats withoutUid(int uid) {
+ final NetworkStats stats = new NetworkStats(elapsedRealtime, 10);
+
+ Entry entry = new Entry();
+ for (int i = 0; i < size; i++) {
+ entry = getValues(i, entry);
+ if (entry.uid != uid) {
+ stats.addValues(entry);
+ }
+ }
+
+ return stats;
+ }
+
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
@@ -625,4 +631,19 @@
return new NetworkStats[size];
}
};
+
+ public static class NonMonotonicException extends Exception {
+ public final NetworkStats left;
+ public final NetworkStats right;
+ public final int leftIndex;
+ public final int rightIndex;
+
+ public NonMonotonicException(
+ NetworkStats left, int leftIndex, NetworkStats right, int rightIndex) {
+ this.left = checkNotNull(left, "missing left");
+ this.right = checkNotNull(right, "missing right");
+ this.leftIndex = leftIndex;
+ this.rightIndex = rightIndex;
+ }
+ }
}
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 18eb9f6..cd585b2 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -20,6 +20,7 @@
import android.app.backup.BackupManager;
import android.content.Context;
import android.media.MediaPlayer;
+import android.net.NetworkStats.NonMonotonicException;
import android.os.RemoteException;
import android.os.ServiceManager;
@@ -192,12 +193,15 @@
throw new IllegalStateException("not profiling data");
}
- // subtract starting values and return delta
- final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
- final NetworkStats profilingDelta = profilingStop.subtractClamped(
- sActiveProfilingStart);
- sActiveProfilingStart = null;
- return profilingDelta;
+ try {
+ // subtract starting values and return delta
+ final NetworkStats profilingStop = getDataLayerSnapshotForUid(context);
+ final NetworkStats profilingDelta = profilingStop.subtract(sActiveProfilingStart);
+ sActiveProfilingStart = null;
+ return profilingDelta;
+ } catch (NonMonotonicException e) {
+ throw new RuntimeException(e);
+ }
}
}
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 2ecf307..28251a6 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -146,6 +146,14 @@
public void onCancel() {}
/**
+ * Request to close this session.
+ * This function will run on the incoming IPC thread.
+ * So, this is not called on the main thread,
+ * but will be called in series on another thread.
+ */
+ public void onClose() {}
+
+ /**
* @return Locale for this session
*/
public String getLocale() {
@@ -162,7 +170,7 @@
// Preventing from exposing ISpellCheckerSession.aidl, create an internal class.
private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub {
- private final ISpellCheckerSessionListener mListener;
+ private ISpellCheckerSessionListener mListener;
private final Session mSession;
private final String mLocale;
private final Bundle mBundle;
@@ -192,6 +200,12 @@
mSession.onCancel();
}
+ @Override
+ public void onClose() {
+ mSession.onClose();
+ mListener = null;
+ }
+
public String getLocale() {
return mLocale;
}
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 5c3f089..01b114c 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -152,6 +152,7 @@
public void close() {
mIsUsed = false;
try {
+ mSpellCheckerSessionListenerImpl.close();
mTextServicesManager.finishSpellCheckerService(mSpellCheckerSessionListenerImpl);
} catch (RemoteException e) {
// do nothing
@@ -190,6 +191,7 @@
private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub {
private static final int TASK_CANCEL = 1;
private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
+ private static final int TASK_CLOSE = 3;
private final Queue<SpellCheckerParams> mPendingTasks =
new LinkedList<SpellCheckerParams>();
private final Handler mHandler;
@@ -224,6 +226,9 @@
case TASK_GET_SUGGESTIONS_MULTIPLE:
processGetSuggestionsMultiple(scp);
break;
+ case TASK_CLOSE:
+ processClose();
+ break;
}
}
@@ -247,6 +252,13 @@
suggestionsLimit, sequentialWords));
}
+ public void close() {
+ if (DBG) {
+ Log.w(TAG, "close");
+ }
+ processOrEnqueueTask(new SpellCheckerParams(TASK_CLOSE, null, 0, false));
+ }
+
public boolean isDisconnected() {
return mOpened && mISpellCheckerSession == null;
}
@@ -284,6 +296,21 @@
}
}
+ private void processClose() {
+ if (!checkOpenConnection()) {
+ return;
+ }
+ if (DBG) {
+ Log.w(TAG, "Close spell checker tasks.");
+ }
+ try {
+ mISpellCheckerSession.onClose();
+ mISpellCheckerSession = null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to close " + e);
+ }
+ }
+
private void processGetSuggestionsMultiple(SpellCheckerParams scp) {
if (!checkOpenConnection()) {
return;
diff --git a/core/java/android/webkit/HTML5VideoInline.java b/core/java/android/webkit/HTML5VideoInline.java
index 42581c2..fe5908e 100644
--- a/core/java/android/webkit/HTML5VideoInline.java
+++ b/core/java/android/webkit/HTML5VideoInline.java
@@ -74,11 +74,13 @@
public SurfaceTexture getSurfaceTexture(int videoLayerId) {
// Create the surface texture.
if (videoLayerId != mVideoLayerUsingSurfaceTexture
- || mSurfaceTexture == null) {
- if (mTextureNames == null) {
- mTextureNames = new int[1];
- GLES20.glGenTextures(1, mTextureNames, 0);
+ || mSurfaceTexture == null
+ || mTextureNames == null) {
+ if (mTextureNames != null) {
+ GLES20.glDeleteTextures(1, mTextureNames, 0);
}
+ mTextureNames = new int[1];
+ GLES20.glGenTextures(1, mTextureNames, 0);
mSurfaceTexture = new SurfaceTexture(mTextureNames[0]);
}
mVideoLayerUsingSurfaceTexture = videoLayerId;
diff --git a/core/java/com/android/internal/net/NetworkStatsFactory.java b/core/java/com/android/internal/net/NetworkStatsFactory.java
index ee3f23b..41993c4 100644
--- a/core/java/com/android/internal/net/NetworkStatsFactory.java
+++ b/core/java/com/android/internal/net/NetworkStatsFactory.java
@@ -25,12 +25,14 @@
import android.os.SystemClock;
import android.util.Slog;
+import com.android.internal.util.ProcFileReader;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
import com.google.android.collect.Sets;
import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
@@ -107,6 +109,7 @@
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
final NetworkStats.Entry entry = new NetworkStats.Entry();
+ // TODO: transition to ProcFileReader
// TODO: read directly from proc once headers are added
final ArrayList<String> keys = Lists.newArrayList(KEY_IFACE, KEY_ACTIVE, KEY_SNAP_RX_BYTES,
KEY_SNAP_RX_PACKETS, KEY_SNAP_TX_BYTES, KEY_SNAP_TX_PACKETS, KEY_RX_BYTES,
@@ -257,71 +260,58 @@
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
final NetworkStats.Entry entry = new NetworkStats.Entry();
- // TODO: remove knownLines check once 5087722 verified
- final HashSet<String> knownLines = Sets.newHashSet();
- // TODO: remove lastIdx check once 5270106 verified
- int lastIdx;
+ int idx = 1;
+ int lastIdx = 1;
- final ArrayList<String> keys = Lists.newArrayList();
- final ArrayList<String> values = Lists.newArrayList();
- final HashMap<String, String> parsed = Maps.newHashMap();
-
- BufferedReader reader = null;
- String line = null;
+ ProcFileReader reader = null;
try {
- reader = new BufferedReader(new FileReader(mStatsXtUid));
+ // open and consume header line
+ reader = new ProcFileReader(new FileInputStream(mStatsXtUid));
+ reader.finishLine();
- // parse first line as header
- line = reader.readLine();
- splitLine(line, keys);
- lastIdx = 1;
-
- // parse remaining lines
- while ((line = reader.readLine()) != null) {
- splitLine(line, values);
- parseLine(keys, values, parsed);
-
- if (!knownLines.add(line)) {
- throw new IllegalStateException("duplicate proc entry: " + line);
- }
-
- final int idx = getParsedInt(parsed, KEY_IDX);
+ while (reader.hasMoreData()) {
+ idx = reader.nextInt();
if (idx != lastIdx + 1) {
throw new IllegalStateException(
"inconsistent idx=" + idx + " after lastIdx=" + lastIdx);
}
lastIdx = idx;
- entry.iface = parsed.get(KEY_IFACE);
- entry.uid = getParsedInt(parsed, KEY_UID);
- entry.set = getParsedInt(parsed, KEY_COUNTER_SET);
- entry.tag = kernelToTag(parsed.get(KEY_TAG_HEX));
- entry.rxBytes = getParsedLong(parsed, KEY_RX_BYTES);
- entry.rxPackets = getParsedLong(parsed, KEY_RX_PACKETS);
- entry.txBytes = getParsedLong(parsed, KEY_TX_BYTES);
- entry.txPackets = getParsedLong(parsed, KEY_TX_PACKETS);
+ entry.iface = reader.nextString();
+ entry.tag = kernelToTag(reader.nextString());
+ entry.uid = reader.nextInt();
+ entry.set = reader.nextInt();
+ entry.rxBytes = reader.nextLong();
+ entry.rxPackets = reader.nextLong();
+ entry.txBytes = reader.nextLong();
+ entry.txPackets = reader.nextLong();
if (limitUid == UID_ALL || limitUid == entry.uid) {
stats.addValues(entry);
}
+
+ reader.finishLine();
}
} catch (NullPointerException e) {
- throw new IllegalStateException("problem parsing line: " + line, e);
+ throw new IllegalStateException("problem parsing idx " + idx, e);
} catch (NumberFormatException e) {
- throw new IllegalStateException("problem parsing line: " + line, e);
+ throw new IllegalStateException("problem parsing idx " + idx, e);
} catch (IOException e) {
- throw new IllegalStateException("problem parsing line: " + line, e);
+ throw new IllegalStateException("problem parsing idx " + idx, e);
} finally {
IoUtils.closeQuietly(reader);
}
+
return stats;
}
+ @Deprecated
private static int getParsedInt(HashMap<String, String> parsed, String key) {
final String value = parsed.get(key);
return value != null ? Integer.parseInt(value) : 0;
}
+ @Deprecated
private static long getParsedLong(HashMap<String, String> parsed, String key) {
final String value = parsed.get(key);
return value != null ? Long.parseLong(value) : 0;
@@ -330,6 +320,7 @@
/**
* Split given line into {@link ArrayList}.
*/
+ @Deprecated
private static void splitLine(String line, ArrayList<String> outSplit) {
outSplit.clear();
@@ -343,6 +334,7 @@
* Zip the two given {@link ArrayList} as key and value pairs into
* {@link HashMap}.
*/
+ @Deprecated
private static void parseLine(
ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
outParsed.clear();
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
index 5a00603..3c61968 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
@@ -25,4 +25,5 @@
void onGetSuggestionsMultiple(
in TextInfo[] textInfos, int suggestionsLimit, boolean multipleWords);
void onCancel();
+ void onClose();
}
diff --git a/core/java/com/android/internal/util/ProcFileReader.java b/core/java/com/android/internal/util/ProcFileReader.java
new file mode 100644
index 0000000..72e1f0f
--- /dev/null
+++ b/core/java/com/android/internal/util/ProcFileReader.java
@@ -0,0 +1,199 @@
+/*
+ * 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.internal.util;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charsets;
+
+/**
+ * Reader that specializes in parsing {@code /proc/} files quickly. Walks
+ * through the stream using a single space {@code ' '} as token separator, and
+ * requires each line boundary to be explicitly acknowledged using
+ * {@link #finishLine()}. Assumes {@link Charsets#US_ASCII} encoding.
+ * <p>
+ * Currently doesn't support formats based on {@code \0}, tabs, or repeated
+ * delimiters.
+ */
+public class ProcFileReader implements Closeable {
+ private final InputStream mStream;
+ private final byte[] mBuffer;
+
+ /** Write pointer in {@link #mBuffer}. */
+ private int mTail;
+ /** Flag when last read token finished current line. */
+ private boolean mLineFinished;
+
+ public ProcFileReader(InputStream stream) throws IOException {
+ this(stream, 4096);
+ }
+
+ public ProcFileReader(InputStream stream, int bufferSize) throws IOException {
+ mStream = stream;
+ mBuffer = new byte[bufferSize];
+
+ // read enough to answer hasMoreData
+ fillBuf();
+ }
+
+ /**
+ * Read more data from {@link #mStream} into internal buffer.
+ */
+ private int fillBuf() throws IOException {
+ final int length = mBuffer.length - mTail;
+ if (length == 0) {
+ throw new IOException("attempting to fill already-full buffer");
+ }
+
+ final int read = mStream.read(mBuffer, mTail, length);
+ if (read != -1) {
+ mTail += read;
+ }
+ return read;
+ }
+
+ /**
+ * Consume number of bytes from beginning of internal buffer. If consuming
+ * all remaining bytes, will attempt to {@link #fillBuf()}.
+ */
+ private void consumeBuf(int count) throws IOException {
+ // TODO: consider moving to read pointer, but for now traceview says
+ // these copies aren't a bottleneck.
+ System.arraycopy(mBuffer, count, mBuffer, 0, mTail - count);
+ mTail -= count;
+ if (mTail == 0) {
+ fillBuf();
+ }
+ }
+
+ /**
+ * Find buffer index of next token delimiter, usually space or newline. Will
+ * fill buffer as needed.
+ */
+ private int nextTokenIndex() throws IOException {
+ if (mLineFinished) {
+ throw new IOException("no tokens remaining on current line");
+ }
+
+ int i = 0;
+ do {
+ // scan forward for token boundary
+ for (; i < mTail; i++) {
+ final byte b = mBuffer[i];
+ if (b == '\n') {
+ mLineFinished = true;
+ return i;
+ }
+ if (b == ' ') {
+ return i;
+ }
+ }
+ } while (fillBuf() > 0);
+
+ throw new IOException("end of stream while looking for token boundary");
+ }
+
+ /**
+ * Check if stream has more data to be parsed.
+ */
+ public boolean hasMoreData() {
+ return mTail > 0;
+ }
+
+ /**
+ * Finish current line, skipping any remaining data.
+ */
+ public void finishLine() throws IOException {
+ // last token already finished line; reset silently
+ if (mLineFinished) {
+ mLineFinished = false;
+ return;
+ }
+
+ int i = 0;
+ do {
+ // scan forward for line boundary and consume
+ for (; i < mTail; i++) {
+ if (mBuffer[i] == '\n') {
+ consumeBuf(i + 1);
+ return;
+ }
+ }
+ } while (fillBuf() > 0);
+
+ throw new IOException("end of stream while looking for line boundary");
+ }
+
+ /**
+ * Parse and return next token as {@link String}.
+ */
+ public String nextString() throws IOException {
+ final int tokenIndex = nextTokenIndex();
+ final String s = new String(mBuffer, 0, tokenIndex, Charsets.US_ASCII);
+ consumeBuf(tokenIndex + 1);
+ return s;
+ }
+
+ /**
+ * Parse and return next token as base-10 encoded {@code long}.
+ */
+ public long nextLong() throws IOException {
+ final int tokenIndex = nextTokenIndex();
+ final boolean negative = mBuffer[0] == '-';
+
+ // TODO: refactor into something like IntegralToString
+ long result = 0;
+ for (int i = negative ? 1 : 0; i < tokenIndex; i++) {
+ final int digit = mBuffer[i] - '0';
+ if (digit < 0 || digit > 9) {
+ throw invalidLong(tokenIndex);
+ }
+
+ // always parse as negative number and apply sign later; this
+ // correctly handles MIN_VALUE which is "larger" than MAX_VALUE.
+ final long next = result * 10 - digit;
+ if (next > result) {
+ throw invalidLong(tokenIndex);
+ }
+ result = next;
+ }
+
+ consumeBuf(tokenIndex + 1);
+ return negative ? result : -result;
+ }
+
+ private NumberFormatException invalidLong(int tokenIndex) {
+ return new NumberFormatException(
+ "invalid long: " + new String(mBuffer, 0, tokenIndex, Charsets.US_ASCII));
+ }
+
+ /**
+ * Parse and return next token as base-10 encoded {@code int}.
+ */
+ public int nextInt() throws IOException {
+ final long value = nextLong();
+ if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
+ throw new NumberFormatException("parsed value larger than integer");
+ }
+ return (int) value;
+ }
+
+ public void close() throws IOException {
+ mStream.close();
+ }
+}
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
index 9eee2f0..0cc883f 100644
--- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -89,7 +89,7 @@
* Ensure that downloading on wifi reports reasonable stats.
*/
@LargeTest
- public void testWifiDownload() {
+ public void testWifiDownload() throws Exception {
assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
NetworkStats pre_test_stats = fetchDataFromProc(mUid);
String ts = Long.toString(System.currentTimeMillis());
@@ -123,7 +123,7 @@
* Ensure that downloading on wifi reports reasonable stats.
*/
@LargeTest
- public void testWifiUpload() {
+ public void testWifiUpload() throws Exception {
assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
// Download a file from the server.
String ts = Long.toString(System.currentTimeMillis());
@@ -160,7 +160,7 @@
* accounting still goes to the app making the call and that the numbers still make sense.
*/
@LargeTest
- public void testWifiDownloadWithDownloadManager() {
+ public void testWifiDownloadWithDownloadManager() throws Exception {
assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
// If we are using the download manager, then the data that is written to /proc/uid_stat/
// is accounted against download manager's uid, since it uses pre-ICS API.
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_extended b/core/tests/coretests/res/raw/xt_qtaguid_extended
deleted file mode 100644
index 2f3b4ec..0000000
--- a/core/tests/coretests/res/raw/xt_qtaguid_extended
+++ /dev/null
@@ -1,3 +0,0 @@
-acct_tag_hex uid_tag_int iface rx_bytes rx_packets tx_bytes tx_packets teleported_goats idx
-0x0 1000 test0 1024 10 2048 20 2716057 2
-0x0000F00D00000000 1000 test0 512 5 512 5 3370318 3
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_typical b/core/tests/coretests/res/raw/xt_qtaguid_typical
index 8df4b1b..c1b0d25 100644
--- a/core/tests/coretests/res/raw/xt_qtaguid_typical
+++ b/core/tests/coretests/res/raw/xt_qtaguid_typical
@@ -1,32 +1,71 @@
-idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes
-2 wlan0 0x0 0 14615 4270
-3 wlan0 0x0 1000 5175 915
-4 wlan0 0x0 1021 3381 903
-5 wlan0 0x0 10004 333821 53558
-6 wlan0 0x0 10010 4888 37363
-7 wlan0 0x0 10013 52 104
-8 wlan0 0x74182ada00000000 10004 18725 1066
-9 rmnet0 0x0 0 301274 30244
-10 rmnet0 0x0 1000 304 441
-11 rmnet0 0x0 1013 2880 2272
-12 rmnet0 0x0 1021 31407 8430
-13 rmnet0 0x0 10003 32665 3814
-14 rmnet0 0x0 10004 2373141 420112
-15 rmnet0 0x0 10010 870370 1111727
-16 rmnet0 0x0 10013 240 240
-17 rmnet0 0x0 10016 16703 13512
-18 rmnet0 0x0 10017 3990 3269
-19 rmnet0 0x0 10018 474504 14516062
-20 rmnet0 0x0 10019 782804 71077
-21 rmnet0 0x0 10022 70671 49684
-22 rmnet0 0x0 10029 5785354 397159
-23 rmnet0 0x0 10033 2102 1686
-24 rmnet0 0x0 10034 15495464 227694
-25 rmnet0 0x0 10037 31184994 684122
-26 rmnet0 0x0 10051 298687 113485
-27 rmnet0 0x0 10056 29504 20669
-28 rmnet0 0x0 10069 683 596
-29 rmnet0 0x0 10072 34051 12453
-30 rmnet0 0x0 10077 7025393 213866
-31 rmnet0 0x0 10081 354 1178
-32 rmnet0 0x74182ada00000000 10037 28507378 437004
+idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_bytes rx_tcp_packets rx_udp_bytes rx_udp_packets rx_other_bytes rx_other_packets tx_tcp_bytes tx_tcp_packets tx_udp_bytes tx_udp_packets tx_other_bytes tx_other_packets
+2 wlan0 0x0 0 0 18621 96 2898 44 312 6 15897 58 2412 32 312 6 1010 16 1576 22
+3 wlan0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+4 wlan0 0x0 1000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+5 wlan0 0x0 1000 1 1949 13 1078 14 0 0 1600 10 349 3 0 0 600 10 478 4
+6 wlan0 0x0 10005 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+7 wlan0 0x0 10005 1 32081 38 5315 50 32081 38 0 0 0 0 5315 50 0 0 0 0
+8 wlan0 0x0 10011 0 35777 53 5718 57 0 0 0 0 35777 53 0 0 0 0 5718 57
+9 wlan0 0x0 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+10 wlan0 0x0 10014 0 0 0 1098 13 0 0 0 0 0 0 0 0 0 0 1098 13
+11 wlan0 0x0 10014 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+12 wlan0 0x0 10021 0 562386 573 49228 549 0 0 0 0 562386 573 0 0 0 0 49228 549
+13 wlan0 0x0 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+14 wlan0 0x0 10031 0 3425 5 586 6 0 0 0 0 3425 5 0 0 0 0 586 6
+15 wlan0 0x0 10031 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+16 wlan0 0x7fffff0100000000 10021 0 562386 573 49228 549 0 0 0 0 562386 573 0 0 0 0 49228 549
+17 wlan0 0x7fffff0100000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+18 wlan0 0x7fffff0100000000 10031 0 3425 5 586 6 0 0 0 0 3425 5 0 0 0 0 586 6
+19 wlan0 0x7fffff0100000000 10031 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+20 rmnet2 0x0 0 0 547 5 118 2 40 1 243 1 264 3 0 0 62 1 56 1
+21 rmnet2 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+22 rmnet2 0x0 10001 0 1125899906842624 5 984 11 632 5 0 0 0 0 984 11 0 0 0 0
+23 rmnet2 0x0 10001 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+24 rmnet1 0x0 0 0 26736 174 7098 130 7210 97 18382 64 1144 13 2932 64 4054 64 112 2
+25 rmnet1 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+26 rmnet1 0x0 1000 0 75774 77 18038 78 75335 72 439 5 0 0 17668 73 370 5 0 0
+27 rmnet1 0x0 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+28 rmnet1 0x0 10007 0 269945 578 111632 586 269945 578 0 0 0 0 111632 586 0 0 0 0
+29 rmnet1 0x0 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+30 rmnet1 0x0 10011 0 1741256 6918 769778 7019 1741256 6918 0 0 0 0 769778 7019 0 0 0 0
+31 rmnet1 0x0 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+32 rmnet1 0x0 10014 0 0 0 786 12 0 0 0 0 0 0 786 12 0 0 0 0
+33 rmnet1 0x0 10014 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+34 rmnet1 0x0 10021 0 433533 1454 393420 1604 433533 1454 0 0 0 0 393420 1604 0 0 0 0
+35 rmnet1 0x0 10021 1 21215 33 10278 33 21215 33 0 0 0 0 10278 33 0 0 0 0
+36 rmnet1 0x0 10036 0 6310 25 3284 29 6310 25 0 0 0 0 3284 29 0 0 0 0
+37 rmnet1 0x0 10036 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+38 rmnet1 0x0 10047 0 34264 47 3936 34 34264 47 0 0 0 0 3936 34 0 0 0 0
+39 rmnet1 0x0 10047 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+40 rmnet1 0x4e7700000000 10011 0 9187 27 4248 33 9187 27 0 0 0 0 4248 33 0 0 0 0
+41 rmnet1 0x4e7700000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+42 rmnet1 0x1000000000000000 10007 0 2109 4 791 4 2109 4 0 0 0 0 791 4 0 0 0 0
+43 rmnet1 0x1000000000000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+44 rmnet1 0x1000000400000000 10007 0 9811 22 6286 22 9811 22 0 0 0 0 6286 22 0 0 0 0
+45 rmnet1 0x1000000400000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+46 rmnet1 0x1010000000000000 10021 0 164833 426 135392 527 164833 426 0 0 0 0 135392 527 0 0 0 0
+47 rmnet1 0x1010000000000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+48 rmnet1 0x1144000400000000 10011 0 10112 18 3334 17 10112 18 0 0 0 0 3334 17 0 0 0 0
+49 rmnet1 0x1144000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+50 rmnet1 0x1244000400000000 10011 0 1300 3 848 2 1300 3 0 0 0 0 848 2 0 0 0 0
+51 rmnet1 0x1244000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+52 rmnet1 0x3000000000000000 10007 0 10389 14 1521 12 10389 14 0 0 0 0 1521 12 0 0 0 0
+53 rmnet1 0x3000000000000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+54 rmnet1 0x3000000400000000 10007 0 238070 380 93938 404 238070 380 0 0 0 0 93938 404 0 0 0 0
+55 rmnet1 0x3000000400000000 10007 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+56 rmnet1 0x3010000000000000 10021 0 219110 578 227423 676 219110 578 0 0 0 0 227423 676 0 0 0 0
+57 rmnet1 0x3010000000000000 10021 1 742 3 1265 3 742 3 0 0 0 0 1265 3 0 0 0 0
+58 rmnet1 0x3020000000000000 10021 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+59 rmnet1 0x3020000000000000 10021 1 20473 30 9013 30 20473 30 0 0 0 0 9013 30 0 0 0 0
+60 rmnet1 0x3144000400000000 10011 0 43963 92 34414 116 43963 92 0 0 0 0 34414 116 0 0 0 0
+61 rmnet1 0x3144000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+62 rmnet1 0x3244000400000000 10011 0 3486 8 1520 9 3486 8 0 0 0 0 1520 9 0 0 0 0
+63 rmnet1 0x3244000400000000 10011 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+64 rmnet1 0x7fffff0100000000 10021 0 29102 56 8865 60 29102 56 0 0 0 0 8865 60 0 0 0 0
+65 rmnet1 0x7fffff0100000000 10021 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+66 rmnet1 0x7fffff0300000000 1000 0 995 13 14145 14 995 13 0 0 0 0 14145 14 0 0 0 0
+67 rmnet1 0x7fffff0300000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+68 rmnet0 0x0 0 0 4312 49 1288 23 0 0 0 0 4312 49 0 0 0 0 1288 23
+69 rmnet0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
+70 rmnet0 0x0 10080 0 22266 30 20976 30 0 0 0 0 22266 30 0 0 0 0 20976 30
+71 rmnet0 0x0 10080 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/core/tests/coretests/res/raw/xt_qtaguid_typical_with_set b/core/tests/coretests/res/raw/xt_qtaguid_typical_with_set
deleted file mode 100644
index b302bb7..0000000
--- a/core/tests/coretests/res/raw/xt_qtaguid_typical_with_set
+++ /dev/null
@@ -1,13 +0,0 @@
-idx iface acct_tag_hex uid_tag_int cnt_set rx_bytes rx_packets tx_bytes tx_packets rx_tcp_packets rx_tcp_bytes rx_udp_packets rx_udp_bytes rx_other_packets rx_other_bytes tx_tcp_packets tx_tcp_bytes tx_udp_packets tx_udp_bytes tx_other_packets tx_other_bytes
-2 rmnet0 0x0 0 0 14855 82 2804 47 2000 45 12799 35 56 2 676 13 2128 34 0 0
-3 rmnet0 0x0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-4 rmnet0 0x0 1000 0 278102 253 10487 182 277342 243 760 10 0 0 9727 172 760 10 0 0
-5 rmnet0 0x0 1000 1 26033 30 1401 26 25881 28 152 2 0 0 1249 24 152 2 0 0
-6 rmnet0 0x0 10012 0 40524 272 134138 293 40524 272 0 0 0 0 134138 293 0 0 0 0
-7 rmnet0 0x0 10012 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-8 rmnet0 0x0 10034 0 15791 59 9905 69 15791 59 0 0 0 0 9905 69 0 0 0 0
-9 rmnet0 0x0 10034 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-10 rmnet0 0x0 10055 0 3602 29 7739 59 3602 29 0 0 0 0 7739 59 0 0 0 0
-11 rmnet0 0x0 10055 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
-12 rmnet0 0x7fff000300000000 1000 0 483 4 1931 6 483 4 0 0 0 0 1931 6 0 0 0 0
-13 rmnet0 0x7fff000300000000 1000 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 7082deb..b37eb46 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -51,6 +51,27 @@
assertEquals(-1, stats.findIndex(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE));
}
+ public void testFindIndexHinted() {
+ final NetworkStats stats = new NetworkStats(TEST_START, 3)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 1024L, 8L, 0L, 0L, 10)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 0L, 0L, 1024L, 8L, 11)
+ .addValues(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 12)
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 1024L, 8L, 0L, 0L, 10)
+ .addValues(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, 0L, 0L, 1024L, 8L, 11)
+ .addValues(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 12);
+
+ // verify that we correctly find across regardless of hinting
+ for (int hint = 0; hint < stats.size(); hint++) {
+ assertEquals(0, stats.findIndexHinted(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, hint));
+ assertEquals(1, stats.findIndexHinted(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, hint));
+ assertEquals(2, stats.findIndexHinted(TEST_IFACE, 102, SET_DEFAULT, TAG_NONE, hint));
+ assertEquals(3, stats.findIndexHinted(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, hint));
+ assertEquals(4, stats.findIndexHinted(TEST_IFACE2, 101, SET_DEFAULT, 0xF00D, hint));
+ assertEquals(5, stats.findIndexHinted(TEST_IFACE2, 102, SET_DEFAULT, TAG_NONE, hint));
+ assertEquals(-1, stats.findIndexHinted(TEST_IFACE, 6, SET_DEFAULT, TAG_NONE, hint));
+ }
+ }
+
public void testAddEntryGrow() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 2);
@@ -257,6 +278,22 @@
assertValues(stats.getTotal(null, ifaces), 1024L, 64L, 0L, 0L, 0L);
}
+ public void testWithoutUid() throws Exception {
+ final NetworkStats before = new NetworkStats(TEST_START, 3)
+ .addValues(TEST_IFACE, 100, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 2L, 20L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_DEFAULT, 0xF00D, 64L, 4L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE2, 100, SET_FOREGROUND, TAG_NONE, 512L, 32L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L)
+ .addValues(TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+
+ final NetworkStats after = before.withoutUid(100);
+ assertEquals(6, before.size());
+ assertEquals(2, after.size());
+ assertValues(after, 0, TEST_IFACE, 101, SET_DEFAULT, TAG_NONE, 128L, 8L, 0L, 0L, 0L);
+ assertValues(after, 1, TEST_IFACE, 101, SET_DEFAULT, 0xF00D, 128L, 8L, 0L, 0L, 0L);
+ }
+
private static void assertValues(NetworkStats stats, int index, String iface, int uid, int set,
int tag, long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
final NetworkStats.Entry entry = stats.getValues(index, null);
diff --git a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
index 8a64f2b..ea94fa9 100644
--- a/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
+++ b/core/tests/coretests/src/com/android/internal/net/NetworkStatsFactoryTest.java
@@ -71,21 +71,12 @@
stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats"));
final NetworkStats stats = mFactory.readNetworkStatsDetail();
- assertEquals(31, stats.size());
- assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0, 14615L, 4270L);
- assertStatsEntry(stats, "wlan0", 10004, SET_DEFAULT, 0, 333821L, 53558L);
- assertStatsEntry(stats, "wlan0", 10004, SET_DEFAULT, 1947740890, 18725L, 1066L);
- assertStatsEntry(stats, "rmnet0", 10037, SET_DEFAULT, 0, 31184994L, 684122L);
- assertStatsEntry(stats, "rmnet0", 10037, SET_DEFAULT, 1947740890, 28507378L, 437004L);
- }
-
- public void testNetworkStatsDetailExtended() throws Exception {
- stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats"));
-
- final NetworkStats stats = mFactory.readNetworkStatsDetail();
- assertEquals(2, stats.size());
- assertStatsEntry(stats, "test0", 1000, SET_DEFAULT, 0, 1024L, 2048L);
- assertStatsEntry(stats, "test0", 1000, SET_DEFAULT, 0xF00D, 512L, 512L);
+ assertEquals(70, stats.size());
+ assertStatsEntry(stats, "wlan0", 0, SET_DEFAULT, 0x0, 18621L, 2898L);
+ assertStatsEntry(stats, "wlan0", 10011, SET_DEFAULT, 0x0, 35777L, 5718L);
+ assertStatsEntry(stats, "wlan0", 10021, SET_DEFAULT, 0x7fffff01, 562386L, 49228L);
+ assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 227423L);
+ assertStatsEntry(stats, "rmnet2", 10001, SET_DEFAULT, 0x0, 1125899906842624L, 984L);
}
public void testNetworkStatsSummary() throws Exception {
@@ -149,12 +140,12 @@
}
public void testNetworkStatsWithSet() throws Exception {
- stageFile(R.raw.xt_qtaguid_typical_with_set, new File(mTestProc, "net/xt_qtaguid/stats"));
+ stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats"));
final NetworkStats stats = mFactory.readNetworkStatsDetail();
- assertEquals(12, stats.size());
- assertStatsEntry(stats, "rmnet0", 1000, SET_DEFAULT, 0, 278102L, 253L, 10487L, 182L);
- assertStatsEntry(stats, "rmnet0", 1000, SET_FOREGROUND, 0, 26033L, 30L, 1401L, 26L);
+ assertEquals(70, stats.size());
+ assertStatsEntry(stats, "rmnet1", 10021, SET_DEFAULT, 0x30100000, 219110L, 578L, 227423L, 676L);
+ assertStatsEntry(stats, "rmnet1", 10021, SET_FOREGROUND, 0x30100000, 742L, 3L, 1265L, 3L);
}
public void testNetworkStatsSingle() throws Exception {
diff --git a/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java b/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java
new file mode 100644
index 0000000..386a78d
--- /dev/null
+++ b/core/tests/coretests/src/com/android/internal/util/ProcFileReaderTest.java
@@ -0,0 +1,163 @@
+/*
+ * 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.internal.util;
+
+import android.test.AndroidTestCase;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.charset.Charsets;
+
+/**
+ * Tests for {@link ProcFileReader}.
+ */
+public class ProcFileReaderTest extends AndroidTestCase {
+
+ public void testEmpty() throws Exception {
+ final ProcFileReader reader = buildReader("");
+
+ assertFalse(reader.hasMoreData());
+ try {
+ reader.finishLine();
+ fail("somehow finished line beyond end of stream?");
+ } catch (IOException e) {
+ // expected
+ }
+ assertFalse(reader.hasMoreData());
+ }
+
+ public void testSingleString() throws Exception {
+ final ProcFileReader reader = buildReader("a\nb\nc\n");
+
+ assertEquals("a", reader.nextString());
+ reader.finishLine();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals("b", reader.nextString());
+ reader.finishLine();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals("c", reader.nextString());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+ }
+
+ public void testMixedNumbersSkip() throws Exception {
+ final ProcFileReader reader = buildReader("1 2 3\n4 abc_def 5 6 7 8 9\n10\n");
+
+ assertEquals(1, reader.nextInt());
+ assertEquals(2, reader.nextInt());
+ assertEquals(3, reader.nextInt());
+ reader.finishLine();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals(4, reader.nextInt());
+ assertEquals("abc_def", reader.nextString());
+ assertEquals(5, reader.nextInt());
+ reader.finishLine();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals(10, reader.nextInt());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+ }
+
+ public void testBufferSize() throws Exception {
+ // read numbers using very small buffer size, exercising fillBuf()
+ final ProcFileReader reader = buildReader("1 21 3 41 5 61 7 81 9 10\n", 3);
+
+ assertEquals(1, reader.nextInt());
+ assertEquals(21, reader.nextInt());
+ assertEquals(3, reader.nextInt());
+ assertEquals(41, reader.nextInt());
+ assertEquals(5, reader.nextInt());
+ assertEquals(61, reader.nextInt());
+ assertEquals(7, reader.nextInt());
+ assertEquals(81, reader.nextInt());
+ assertEquals(9, reader.nextInt());
+ assertEquals(10, reader.nextInt());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+ }
+
+ public void testBlankLines() throws Exception {
+ final ProcFileReader reader = buildReader("1\n\n2\n\n3\n");
+
+ assertEquals(1, reader.nextInt());
+ reader.finishLine();
+ assertTrue(reader.hasMoreData());
+ reader.finishLine();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals(2, reader.nextInt());
+ reader.finishLine();
+ assertTrue(reader.hasMoreData());
+ reader.finishLine();
+ assertTrue(reader.hasMoreData());
+
+ assertEquals(3, reader.nextInt());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+ }
+
+ public void testMinMax() throws Exception {
+ final ProcFileReader reader = buildReader(
+ "1 -1024 9223372036854775807 -9223372036854775808\n");
+
+ assertEquals(1, reader.nextLong());
+ assertEquals(-1024, reader.nextLong());
+ assertEquals(Long.MAX_VALUE, reader.nextLong());
+ assertEquals(Long.MIN_VALUE, reader.nextLong());
+ reader.finishLine();
+ assertFalse(reader.hasMoreData());
+ }
+
+ public void testDelimiterNeverFound() throws Exception {
+ final ProcFileReader reader = buildReader("teststringwithoutdelimiters");
+
+ try {
+ reader.nextString();
+ fail("somehow read a string value?");
+ } catch (IOException e) {
+ // expected
+ assertTrue(e.getMessage().contains("end of stream"));
+ }
+ }
+
+ public void testLargerThanBuffer() throws Exception {
+ // try finishing line larger than buffer
+ final ProcFileReader reader = buildReader("1 teststringlongerthanbuffer\n", 4);
+
+ assertEquals(1, reader.nextLong());
+ try {
+ reader.finishLine();
+ fail("somehow finished line?");
+ } catch (IOException e) {
+ // expected
+ assertTrue(e.getMessage().contains("already-full buffer"));
+ }
+ }
+
+ private static ProcFileReader buildReader(String string) throws IOException {
+ return buildReader(string, 2048);
+ }
+
+ private static ProcFileReader buildReader(String string, int bufferSize) throws IOException {
+ return new ProcFileReader(
+ new ByteArrayInputStream(string.getBytes(Charsets.US_ASCII)), bufferSize);
+ }
+}
diff --git a/docs/html/guide/developing/device.jd b/docs/html/guide/developing/device.jd
index 9ce3649..deb7a2d 100644
--- a/docs/html/guide/developing/device.jd
+++ b/docs/html/guide/developing/device.jd
@@ -58,14 +58,17 @@
element.</p>
</li>
<li>Set up your device to allow installation of non-Market applications. <p>On
-the device, go to <strong>Settings > Applications</strong> and enable
+the device, go to <strong>Settings > Applications</strong> and enable
-<strong>Unknown sources</strong>.</p>
+<strong>Unknown sources</strong> (on an Android 4.0 device, the setting is
+located in <strong>Settings > Security</strong>).</p>
</li>
<li>Turn on "USB Debugging" on your device.
- <p>On the device, go to <strong>Settings > Applications > Development</strong>
- and enable <strong>USB debugging</strong>.</p>
+ <p>On the device, go to <strong>Settings > Applications > Development</strong>
+ and enable <strong>USB debugging</strong>
+ (on an Android 4.0 device, the setting is
+located in <strong>Settings > Developer options</strong>).</p>
</li>
<li>Set up your system to detect your device.
<ul>
diff --git a/docs/html/resources/dashboard/opengl.jd b/docs/html/resources/dashboard/opengl.jd
index 9089937..07a0e43 100644
--- a/docs/html/resources/dashboard/opengl.jd
+++ b/docs/html/resources/dashboard/opengl.jd
@@ -57,7 +57,7 @@
<div class="dashboard-panel">
<img alt="" width="400" height="250"
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1|GL%202.0%20%26%201.1&chd=t%3A9.2,90.8" />
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=GL%201.1|GL%202.0%20%26%201.1&chd=t%3A9.8,90.2" />
<table>
<tr>
@@ -66,14 +66,14 @@
</tr>
<tr>
<td>1.1</th>
-<td>9.2%</td>
+<td>9.8%</td>
</tr>
<tr>
<td>2.0</th>
-<td>90.8%</td>
+<td>90.2%</td>
</tr>
</table>
-<p><em>Data collected during a 7-day period ending on October 3, 2011</em></p>
+<p><em>Data collected during a 7-day period ending on November 3, 2011</em></p>
</div>
diff --git a/docs/html/resources/dashboard/platform-versions.jd b/docs/html/resources/dashboard/platform-versions.jd
index 135c6f2..8041096 100644
--- a/docs/html/resources/dashboard/platform-versions.jd
+++ b/docs/html/resources/dashboard/platform-versions.jd
@@ -52,7 +52,7 @@
<div class="dashboard-panel">
<img alt="" height="250" width="470"
-src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:1.1,1.4,11.7,45.3,0.5,38.2,0.2,0.9,0.7&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2&chco=c4df9b,6fad0c" />
+src="http://chart.apis.google.com/chart?&cht=p&chs=460x250&chd=t:0.9,1.4,10.7,40.7,0.5,43.9,0.1,0.9,0.9&chl=Android%201.5|Android%201.6|Android%202.1|Android%202.2|Android%202.3|Android%202.3.3|Android%203.0|Android%203.1|Android%203.2&chco=c4df9b,6fad0c" />
<table>
<tr>
@@ -61,21 +61,21 @@
<th>API Level</th>
<th>Distribution</th>
</tr>
-<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td> <td>3</td><td>1.1%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-1.5.html">Android 1.5</a></td><td>Cupcake</td> <td>3</td><td>0.9%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-1.6.html">Android 1.6</a></td><td>Donut</td> <td>4</td><td>1.4%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td> <td>7</td><td>11.7%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td> <td>8</td><td>45.3%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.1.html">Android 2.1</a></td><td>Eclair</td> <td>7</td><td>10.7%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-2.2.html">Android 2.2</a></td><td>Froyo</td> <td>8</td><td>40.7%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-2.3.html">Android 2.3 -<br/>
Android 2.3.2</a></td><td rowspan="2">Gingerbread</td> <td>9</td><td>0.5%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-2.3.3.html">Android 2.3.3 -<br/>
- Android 2.3.7</a></td><!-- Gingerbread --> <td>10</td><td>38.2%</td></tr>
+ Android 2.3.7</a></td><!-- Gingerbread --> <td>10</td><td>43.9%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-3.0.html">Android 3.0</a></td>
- <td rowspan="3">Honeycomb</td> <td>11</td><td>0.2%</td></tr>
+ <td rowspan="3">Honeycomb</td> <td>11</td><td>0.1%</td></tr>
<tr><td><a href="{@docRoot}sdk/android-3.1.html">Android 3.1</a></td><!-- Honeycomb --><td>12</td><td>0.9%</td></tr>
-<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>0.7%</td></tr>
+<tr><td><a href="{@docRoot}sdk/android-3.2.html">Android 3.2</a></td><!-- Honeycomb --><td>13</td><td>0.9%</td></tr>
</table>
-<p><em>Data collected during a 14-day period ending on October 3, 2011</em></p>
+<p><em>Data collected during a 14-day period ending on November 3, 2011</em></p>
<!--
<p style="font-size:.9em">* <em>Other: 0.1% of devices running obsolete versions</em></p>
-->
@@ -104,9 +104,9 @@
<div class="dashboard-panel">
<img alt="" height="250" width="660" style="padding:5px;background:#fff"
-src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C04/01%7C04/15%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.7,99.6,99.6,99.5,99.4,99.3,99.2,99.0,98.8,98.7,98.5,98.5,98.2|97.0,97.1,97.3,97.5,97.5,97.5,97.7,97.6,97.5,97.5,97.5,97.5,97.1|93.5,93.9,94.3,94.8,95.0,95.2,95.5,95.5,95.5,95.6,95.7,95.8,95.6|66.4,68.0,69.8,71.5,73.9,75.4,77.6,79.0,80.2,81.1,82.4,83.3,83.8|2.5,3.1,4.0,6.1,9.5,13.6,17.8,20.6,24.3,27.5,31.2,34.7,38.3|1.7,2.2,3.0,5.1,8.4,12.6,16.8,20.0,23.7,26.9,30.6,34.1,37.8&chm=b,c3df9b,0,1,0|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|tAndroid 2.3.3,131d02,5,5,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
+src="http://chart.apis.google.com/chart?&cht=lc&chs=660x250&chxt=x,x,y,r&chxr=0,0,12|1,0,12|2,0,100|3,0,100&chxl=0%3A%7C05/01%7C05/15%7C06/01%7C06/15%7C07/01%7C07/15%7C08/01%7C08/15%7C09/01%7C09/15%7C10/01%7C10/15%7C11/01%7C1%3A%7C2011%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C%7C2011%7C2%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25%7C3%3A%7C0%25%7C25%25%7C50%25%7C75%25%7C100%25&chxp=0,0,1,2,3,4,5,6,7,8,9,10,11,12&chxtc=0,5&chd=t:99.6,99.5,99.4,99.3,99.2,99.0,98.8,98.7,98.5,98.5,98.2,98.1,98.0|97.3,97.5,97.5,97.5,97.7,97.6,97.5,97.5,97.5,97.5,97.1,97.1,97.1|94.3,94.8,95.0,95.2,95.5,95.5,95.5,95.6,95.7,95.8,95.6,95.9,95.7|69.8,71.5,73.9,75.4,77.6,79.0,80.2,81.1,82.4,83.3,83.8,84.9,85.0|4.0,6.1,9.5,13.6,17.8,20.6,24.3,27.5,31.2,34.7,38.3,41.3,44.0|3.0,5.1,8.4,12.6,16.8,20.0,23.7,26.9,30.6,34.1,37.8,40.8,43.5&chm=b,c3df9b,0,1,0|b,b4db77,1,2,0|tAndroid 2.1,547a19,2,0,15,,t::-5|b,a5db51,2,3,0|tAndroid 2.2,3f5e0e,3,0,15,,t::-5|b,96dd28,3,4,0|b,83c916,4,5,0|tAndroid 2.3.3,131d02,5,3,15,,t::-5|B,6fad0c,5,6,0&chg=7,25&chdl=Android 1.5|Android 1.6|Android 2.1|Android 2.2|Android 2.3|Android 2.3.3&chco=add274,9dd14f,8ece2a,7ab61c,659b11,507d08" />
-<p><em>Last historical dataset collected during a 14-day period ending on October 3, 2011</em></p>
+<p><em>Last historical dataset collected during a 14-day period ending on November 3, 2011</em></p>
</div><!-- end dashboard-panel -->
diff --git a/docs/html/resources/dashboard/screens.jd b/docs/html/resources/dashboard/screens.jd
index 67b47d0..ec3034d 100644
--- a/docs/html/resources/dashboard/screens.jd
+++ b/docs/html/resources/dashboard/screens.jd
@@ -60,7 +60,7 @@
<div class="dashboard-panel">
<img alt="" width="400" height="250"
-src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A2.6,0.1,3.0,71.9,0.9,17.6,2.7,1.2" />
+src="http://chart.googleapis.com/chart?cht=p&chs=400x250&chco=c4df9b,6fad0c&chl=Xlarge%20/%20mdpi|Large%20/%20ldpi|Large%20/%20mdpi|Normal%20/%20hdpi|Normal%20/%20ldpi|Normal%20/%20mdpi|Small%20/%20hdpi|Small%20/%20ldpi&chd=t%3A2.9,0.1,3.1,70.8,1.0,17.7,3.0,1.3" />
<table>
<tr>
@@ -71,31 +71,31 @@
<th scope="col">xhdpi</th>
</tr>
<tr><th scope="row">small</th>
-<td>1.2%</td> <!-- small/ldpi -->
+<td>1.3%</td> <!-- small/ldpi -->
<td></td> <!-- small/mdpi -->
-<td>2.7%</td> <!-- small/hdpi -->
+<td>3.0%</td> <!-- small/hdpi -->
<td></td> <!-- small/xhdpi -->
</tr>
<tr><th scope="row">normal</th>
-<td>0.9%</td> <!-- normal/ldpi -->
-<td>17.6%</td> <!-- normal/mdpi -->
-<td>71.9%</td> <!-- normal/hdpi -->
+<td>1.0%</td> <!-- normal/ldpi -->
+<td>17.7%</td> <!-- normal/mdpi -->
+<td>70.8%</td> <!-- normal/hdpi -->
<td></td> <!-- normal/xhdpi -->
</tr>
<tr><th scope="row">large</th>
<td>0.1%</td> <!-- large/ldpi -->
-<td>3.0%</td> <!-- large/mdpi -->
+<td>3.1%</td> <!-- large/mdpi -->
<td></td> <!-- large/hdpi -->
<td></td> <!-- large/xhdpi -->
</tr>
<tr><th scope="row">xlarge</th>
<td></td> <!-- xlarge/ldpi -->
-<td>2.6%</td> <!-- xlarge/mdpi -->
+<td>2.9%</td> <!-- xlarge/mdpi -->
<td></td> <!-- xlarge/hdpi -->
<td></td> <!-- xlarge/xhdpi -->
</tr>
</table>
-<p><em>Data collected during a 7-day period ending on October 3, 2011</em></p>
+<p><em>Data collected during a 7-day period ending on November 3, 2011</em></p>
</div>
diff --git a/include/utils/BlobCache.h b/include/utils/BlobCache.h
index dc45ff0..4f342a2 100644
--- a/include/utils/BlobCache.h
+++ b/include/utils/BlobCache.h
@@ -19,19 +19,21 @@
#include <stddef.h>
+#include <utils/Flattenable.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.
+// A BlobCache is an in-memory cache for binary key/value pairs. A BlobCache
+// does NOT provide any thread-safety guarantees.
//
-// 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 {
+// The cache contents can be serialized to an in-memory buffer or mmap'd file
+// and then reloaded in a subsequent execution of the program. This
+// serialization is non-portable and the data should only be used by the device
+// that generated it.
+class BlobCache : public RefBase, public Flattenable {
public:
// Create an empty blob cache. The blob cache will cache key/value pairs
@@ -58,14 +60,13 @@
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.
+ // get 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
@@ -77,6 +78,37 @@
// 0 <= valueSize
size_t get(const void* key, size_t keySize, void* value, size_t valueSize);
+ // getFlattenedSize returns the number of bytes needed to store the entire
+ // serialized cache.
+ virtual size_t getFlattenedSize() const;
+
+ // getFdCount returns the number of file descriptors that will result from
+ // flattening the cache. This will always return 0 so as to allow the
+ // flattened cache to be saved to disk and then later restored.
+ virtual size_t getFdCount() const;
+
+ // flatten serializes the current contents of the cache into the memory
+ // pointed to by 'buffer'. The serialized cache contents can later be
+ // loaded into a BlobCache object using the unflatten method. The contents
+ // of the BlobCache object will not be modified.
+ //
+ // Preconditions:
+ // size >= this.getFlattenedSize()
+ // count == 0
+ virtual status_t flatten(void* buffer, size_t size, int fds[],
+ size_t count) const;
+
+ // unflatten replaces the contents of the cache with the serialized cache
+ // contents in the memory pointed to by 'buffer'. The previous contents of
+ // the BlobCache will be evicted from the cache. If an error occurs while
+ // unflattening the serialized cache contents then the BlobCache will be
+ // left in an empty state.
+ //
+ // Preconditions:
+ // count == 0
+ virtual status_t unflatten(void const* buffer, size_t size, int fds[],
+ size_t count);
+
private:
// Copying is disallowed.
BlobCache(const BlobCache&);
@@ -144,6 +176,46 @@
sp<Blob> mValue;
};
+ // A Header is the header for the entire BlobCache serialization format. No
+ // need to make this portable, so we simply write the struct out.
+ struct Header {
+ // mMagicNumber is the magic number that identifies the data as
+ // serialized BlobCache contents. It must always contain 'Blb$'.
+ uint32_t mMagicNumber;
+
+ // mBlobCacheVersion is the serialization format version.
+ uint32_t mBlobCacheVersion;
+
+ // mDeviceVersion is the device-specific version of the cache. This can
+ // be used to invalidate the cache.
+ uint32_t mDeviceVersion;
+
+ // mNumEntries is number of cache entries following the header in the
+ // data.
+ size_t mNumEntries;
+ };
+
+ // An EntryHeader is the header for a serialized cache entry. No need to
+ // make this portable, so we simply write the struct out. Each EntryHeader
+ // is followed imediately by the key data and then the value data.
+ //
+ // The beginning of each serialized EntryHeader is 4-byte aligned, so the
+ // number of bytes that a serialized cache entry will occupy is:
+ //
+ // ((sizeof(EntryHeader) + keySize + valueSize) + 3) & ~3
+ //
+ struct EntryHeader {
+ // mKeySize is the size of the entry key in bytes.
+ size_t mKeySize;
+
+ // mValueSize is the size of the entry value in bytes.
+ size_t mValueSize;
+
+ // mData contains both the key and value data for the cache entry. The
+ // key comes first followed immediately by the value.
+ uint8_t mData[];
+ };
+
// 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.
@@ -166,17 +238,12 @@
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.
+ // nrand48 to generate random numbers when needed.
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;
};
}
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index c72a45b..6f84206 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -116,7 +116,7 @@
// Choose a name using the PID and a process-unique ID.
mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId());
- ST_LOGV("SurfaceTexture::SurfaceTexture");
+ ST_LOGV("SurfaceTexture");
sp<ISurfaceComposer> composer(ComposerService::getComposerService());
mGraphicBufferAlloc = composer->createGraphicBufferAlloc();
mNextCrop.makeInvalid();
@@ -125,7 +125,7 @@
}
SurfaceTexture::~SurfaceTexture() {
- ST_LOGV("SurfaceTexture::~SurfaceTexture");
+ ST_LOGV("~SurfaceTexture");
freeAllBuffersLocked();
}
@@ -169,7 +169,7 @@
}
status_t SurfaceTexture::setBufferCount(int bufferCount) {
- ST_LOGV("SurfaceTexture::setBufferCount");
+ ST_LOGV("setBufferCount: count=%d", bufferCount);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -217,6 +217,7 @@
status_t SurfaceTexture::setDefaultBufferSize(uint32_t w, uint32_t h)
{
+ ST_LOGV("setDefaultBufferSize: w=%d, h=%d", w, h);
if (!w || !h) {
ST_LOGE("setDefaultBufferSize: dimensions cannot be 0 (w=%d, h=%d)",
w, h);
@@ -230,7 +231,7 @@
}
status_t SurfaceTexture::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
- ST_LOGV("SurfaceTexture::requestBuffer");
+ ST_LOGV("requestBuffer: slot=%d", slot);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
ST_LOGE("requestBuffer: SurfaceTexture has been abandoned!");
@@ -248,7 +249,7 @@
status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
uint32_t format, uint32_t usage) {
- ST_LOGV("SurfaceTexture::dequeueBuffer");
+ ST_LOGV("dequeueBuffer: w=%d h=%d fmt=%#x usage=%#x", w, h, format, usage);
if ((w && !h) || (!w && h)) {
ST_LOGE("dequeueBuffer: invalid size: w=%u, h=%u", w, h);
@@ -342,6 +343,8 @@
// clients are not allowed to dequeue more than one buffer
// if they didn't set a buffer count.
if (!mClientBufferCount && dequeuedCount) {
+ ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without "
+ "setting the buffer count");
return -EINVAL;
}
@@ -375,6 +378,8 @@
}
if (found == INVALID_BUFFER_SLOT) {
+ // This should not happen.
+ ST_LOGE("dequeueBuffer: no available buffer slots");
return -EBUSY;
}
@@ -427,10 +432,13 @@
}
returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
}
+ ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf,
+ mSlots[buf].mGraphicBuffer->handle, returnFlags);
return returnFlags;
}
status_t SurfaceTexture::setSynchronousMode(bool enabled) {
+ ST_LOGV("setSynchronousMode: enabled=%d", enabled);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -462,7 +470,7 @@
status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp,
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
- ST_LOGV("SurfaceTexture::queueBuffer");
+ ST_LOGV("queueBuffer: slot=%d time=%lld", buf, timestamp);
sp<FrameAvailableListener> listener;
@@ -534,7 +542,7 @@
}
void SurfaceTexture::cancelBuffer(int buf) {
- ST_LOGV("SurfaceTexture::cancelBuffer");
+ ST_LOGV("cancelBuffer: slot=%d", buf);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -556,7 +564,9 @@
}
status_t SurfaceTexture::setCrop(const Rect& crop) {
- ST_LOGV("SurfaceTexture::setCrop");
+ ST_LOGV("setCrop: crop=[%d,%d,%d,%d]", crop.left, crop.top, crop.right,
+ crop.bottom);
+
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
ST_LOGE("setCrop: SurfaceTexture has been abandoned!");
@@ -567,7 +577,7 @@
}
status_t SurfaceTexture::setTransform(uint32_t transform) {
- ST_LOGV("SurfaceTexture::setTransform");
+ ST_LOGV("setTransform: xform=%#x", transform);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
ST_LOGE("setTransform: SurfaceTexture has been abandoned!");
@@ -579,7 +589,7 @@
status_t SurfaceTexture::connect(int api,
uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) {
- ST_LOGV("SurfaceTexture::connect(this=%p, %d)", this, api);
+ ST_LOGV("connect: api=%d", api);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -612,7 +622,7 @@
}
status_t SurfaceTexture::disconnect(int api) {
- ST_LOGV("SurfaceTexture::disconnect(this=%p, %d)", this, api);
+ ST_LOGV("disconnect: api=%d", api);
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -640,6 +650,7 @@
}
break;
default:
+ ST_LOGE("disconnect: unknown API %d", api);
err = -EINVAL;
break;
}
@@ -647,13 +658,14 @@
}
status_t SurfaceTexture::setScalingMode(int mode) {
- ST_LOGV("SurfaceTexture::setScalingMode(%d)", mode);
+ ST_LOGV("setScalingMode: mode=%d", mode);
switch (mode) {
case NATIVE_WINDOW_SCALING_MODE_FREEZE:
case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
break;
default:
+ ST_LOGE("unknown scaling mode: %d", mode);
return BAD_VALUE;
}
@@ -663,7 +675,7 @@
}
status_t SurfaceTexture::updateTexImage() {
- ST_LOGV("SurfaceTexture::updateTexImage");
+ ST_LOGV("updateTexImage");
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
@@ -713,6 +725,10 @@
return -EINVAL;
}
+ ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture,
+ mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf,
+ mSlots[buf].mGraphicBuffer->handle);
+
if (mCurrentTexture != INVALID_BUFFER_SLOT) {
// The current buffer becomes FREE if it was still in the queued
// state. If it has already been given to the client
@@ -771,7 +787,7 @@
}
void SurfaceTexture::computeCurrentTransformMatrix() {
- ST_LOGV("SurfaceTexture::computeCurrentTransformMatrix");
+ ST_LOGV("computeCurrentTransformMatrix");
float xform[16];
for (int i = 0; i < 16; i++) {
@@ -862,14 +878,14 @@
}
nsecs_t SurfaceTexture::getTimestamp() {
- ST_LOGV("SurfaceTexture::getTimestamp");
+ ST_LOGV("getTimestamp");
Mutex::Autolock lock(mMutex);
return mCurrentTimestamp;
}
void SurfaceTexture::setFrameAvailableListener(
const sp<FrameAvailableListener>& listener) {
- ST_LOGV("SurfaceTexture::setFrameAvailableListener");
+ ST_LOGV("setFrameAvailableListener");
Mutex::Autolock lock(mMutex);
mFrameAvailableListener = listener;
}
diff --git a/libs/utils/BlobCache.cpp b/libs/utils/BlobCache.cpp
index 590576a..d38aae9 100644
--- a/libs/utils/BlobCache.cpp
+++ b/libs/utils/BlobCache.cpp
@@ -21,10 +21,20 @@
#include <string.h>
#include <utils/BlobCache.h>
+#include <utils/Errors.h>
#include <utils/Log.h>
namespace android {
+// BlobCache::Header::mMagicNumber value
+static const uint32_t blobCacheMagic = '_Bb$';
+
+// BlobCache::Header::mBlobCacheVersion value
+static const uint32_t blobCacheVersion = 1;
+
+// BlobCache::Header::mDeviceVersion value
+static const uint32_t blobCacheDeviceVersion = 1;
+
BlobCache::BlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxTotalSize):
mMaxKeySize(maxKeySize),
mMaxValueSize(maxValueSize),
@@ -67,12 +77,10 @@
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.
@@ -129,7 +137,6 @@
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);
@@ -152,6 +159,133 @@
return valueBlobSize;
}
+static inline size_t align4(size_t size) {
+ return (size + 3) & ~3;
+}
+
+size_t BlobCache::getFlattenedSize() const {
+ size_t size = sizeof(Header);
+ for (size_t i = 0; i < mCacheEntries.size(); i++) {
+ const CacheEntry& e(mCacheEntries[i]);
+ sp<Blob> keyBlob = e.getKey();
+ sp<Blob> valueBlob = e.getValue();
+ size = align4(size);
+ size += sizeof(EntryHeader) + keyBlob->getSize() +
+ valueBlob->getSize();
+ }
+ return size;
+}
+
+size_t BlobCache::getFdCount() const {
+ return 0;
+}
+
+status_t BlobCache::flatten(void* buffer, size_t size, int fds[], size_t count)
+ const {
+ if (count != 0) {
+ LOGE("flatten: nonzero fd count: %d", count);
+ return BAD_VALUE;
+ }
+
+ // Write the cache header
+ if (size < sizeof(Header)) {
+ LOGE("flatten: not enough room for cache header");
+ return BAD_VALUE;
+ }
+ Header* header = reinterpret_cast<Header*>(buffer);
+ header->mMagicNumber = blobCacheMagic;
+ header->mBlobCacheVersion = blobCacheVersion;
+ header->mDeviceVersion = blobCacheDeviceVersion;
+ header->mNumEntries = mCacheEntries.size();
+
+ // Write cache entries
+ uint8_t* byteBuffer = reinterpret_cast<uint8_t*>(buffer);
+ off_t byteOffset = align4(sizeof(Header));
+ for (size_t i = 0; i < mCacheEntries.size(); i++) {
+ const CacheEntry& e(mCacheEntries[i]);
+ sp<Blob> keyBlob = e.getKey();
+ sp<Blob> valueBlob = e.getValue();
+ size_t keySize = keyBlob->getSize();
+ size_t valueSize = valueBlob->getSize();
+
+ size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+ if (byteOffset + entrySize > size) {
+ LOGE("flatten: not enough room for cache entries");
+ return BAD_VALUE;
+ }
+
+ EntryHeader* eheader = reinterpret_cast<EntryHeader*>(
+ &byteBuffer[byteOffset]);
+ eheader->mKeySize = keySize;
+ eheader->mValueSize = valueSize;
+
+ memcpy(eheader->mData, keyBlob->getData(), keySize);
+ memcpy(eheader->mData + keySize, valueBlob->getData(), valueSize);
+
+ byteOffset += align4(entrySize);
+ }
+
+ return OK;
+}
+
+status_t BlobCache::unflatten(void const* buffer, size_t size, int fds[],
+ size_t count) {
+ // All errors should result in the BlobCache being in an empty state.
+ mCacheEntries.clear();
+
+ if (count != 0) {
+ LOGE("unflatten: nonzero fd count: %d", count);
+ return BAD_VALUE;
+ }
+
+ // Read the cache header
+ if (size < sizeof(Header)) {
+ LOGE("unflatten: not enough room for cache header");
+ return BAD_VALUE;
+ }
+ const Header* header = reinterpret_cast<const Header*>(buffer);
+ if (header->mMagicNumber != blobCacheMagic) {
+ LOGE("unflatten: bad magic number: %d", header->mMagicNumber);
+ return BAD_VALUE;
+ }
+ if (header->mBlobCacheVersion != blobCacheVersion ||
+ header->mDeviceVersion != blobCacheDeviceVersion) {
+ // We treat version mismatches as an empty cache.
+ return OK;
+ }
+
+ // Read cache entries
+ const uint8_t* byteBuffer = reinterpret_cast<const uint8_t*>(buffer);
+ off_t byteOffset = align4(sizeof(Header));
+ size_t numEntries = header->mNumEntries;
+ for (size_t i = 0; i < numEntries; i++) {
+ if (byteOffset + sizeof(EntryHeader) > size) {
+ mCacheEntries.clear();
+ LOGE("unflatten: not enough room for cache entry headers");
+ return BAD_VALUE;
+ }
+
+ const EntryHeader* eheader = reinterpret_cast<const EntryHeader*>(
+ &byteBuffer[byteOffset]);
+ size_t keySize = eheader->mKeySize;
+ size_t valueSize = eheader->mValueSize;
+ size_t entrySize = sizeof(EntryHeader) + keySize + valueSize;
+
+ if (byteOffset + entrySize > size) {
+ mCacheEntries.clear();
+ LOGE("unflatten: not enough room for cache entry headers");
+ return BAD_VALUE;
+ }
+
+ const uint8_t* data = eheader->mData;
+ set(data, keySize, data + keySize, valueSize);
+
+ byteOffset += align4(entrySize);
+ }
+
+ return OK;
+}
+
long int BlobCache::blob_random() {
#ifdef _WIN32
return rand();
@@ -179,7 +313,7 @@
mData(copyData ? malloc(size) : data),
mSize(size),
mOwnsData(copyData) {
- if (copyData) {
+ if (data != NULL && copyData) {
memcpy(const_cast<void*>(mData), data, size);
}
}
diff --git a/libs/utils/tests/BlobCache_test.cpp b/libs/utils/tests/BlobCache_test.cpp
index 653ea5e..b64cc39 100644
--- a/libs/utils/tests/BlobCache_test.cpp
+++ b/libs/utils/tests/BlobCache_test.cpp
@@ -14,9 +14,13 @@
** limitations under the License.
*/
+#include <fcntl.h>
+#include <stdio.h>
+
#include <gtest/gtest.h>
#include <utils/BlobCache.h>
+#include <utils/Errors.h>
namespace android {
@@ -254,4 +258,164 @@
ASSERT_EQ(maxEntries/2 + 1, numCached);
}
+class BlobCacheFlattenTest : public BlobCacheTest {
+protected:
+ virtual void SetUp() {
+ BlobCacheTest::SetUp();
+ mBC2 = new BlobCache(MAX_KEY_SIZE, MAX_VALUE_SIZE, MAX_TOTAL_SIZE);
+ }
+
+ virtual void TearDown() {
+ mBC2.clear();
+ BlobCacheTest::TearDown();
+ }
+
+ void roundTrip() {
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+ delete[] flat;
+ }
+
+ sp<BlobCache> mBC2;
+};
+
+TEST_F(BlobCacheFlattenTest, FlattenOneValue) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+ roundTrip();
+ ASSERT_EQ(size_t(4), mBC2->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(BlobCacheFlattenTest, FlattenFullCache) {
+ // 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, &k, 1);
+ }
+
+ roundTrip();
+
+ // Verify the deserialized cache
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ uint8_t v = 0xee;
+ ASSERT_EQ(size_t(1), mBC2->get(&k, 1, &v, 1));
+ ASSERT_EQ(k, v);
+ }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenDoesntChangeCache) {
+ // 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, &k, 1);
+ }
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ delete[] flat;
+
+ // Verify the cache that we just serialized
+ for (int i = 0; i < maxEntries; i++) {
+ uint8_t k = i;
+ uint8_t v = 0xee;
+ ASSERT_EQ(size_t(1), mBC->get(&k, 1, &v, 1));
+ ASSERT_EQ(k, v);
+ }
+}
+
+TEST_F(BlobCacheFlattenTest, FlattenCatchesBufferTooSmall) {
+ // 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, &k, 1);
+ }
+
+ size_t size = mBC->getFlattenedSize() - 1;
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(BAD_VALUE, mBC->flatten(flat, size, NULL, 0));
+ delete[] flat;
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadMagic) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ flat[1] = ~flat[1];
+
+ // Bad magic should cause an error.
+ ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size, NULL, 0));
+ delete[] flat;
+
+ // The error should cause the unflatten to result in an empty cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheVersion) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ flat[5] = ~flat[5];
+
+ // Version mismatches shouldn't cause errors, but should not use the
+ // serialized entries
+ ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+ delete[] flat;
+
+ // The version mismatch should cause the unflatten to result in an empty
+ // cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBadBlobCacheDeviceVersion) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+ flat[10] = ~flat[10];
+
+ // Version mismatches shouldn't cause errors, but should not use the
+ // serialized entries
+ ASSERT_EQ(OK, mBC2->unflatten(flat, size, NULL, 0));
+ delete[] flat;
+
+ // The version mismatch should cause the unflatten to result in an empty
+ // cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
+TEST_F(BlobCacheFlattenTest, UnflattenCatchesBufferTooSmall) {
+ char buf[4] = { 0xee, 0xee, 0xee, 0xee };
+ mBC->set("abcd", 4, "efgh", 4);
+
+ size_t size = mBC->getFlattenedSize();
+ uint8_t* flat = new uint8_t[size];
+ ASSERT_EQ(OK, mBC->flatten(flat, size, NULL, 0));
+
+ // A buffer truncation shouldt cause an error
+ ASSERT_EQ(BAD_VALUE, mBC2->unflatten(flat, size-1, NULL, 0));
+ delete[] flat;
+
+ // The error should cause the unflatten to result in an empty cache
+ ASSERT_EQ(size_t(0), mBC2->get("abcd", 4, buf, 4));
+}
+
} // namespace android
diff --git a/media/jni/android_media_MediaScanner.cpp b/media/jni/android_media_MediaScanner.cpp
index b88296f..09152f5 100644
--- a/media/jni/android_media_MediaScanner.cpp
+++ b/media/jni/android_media_MediaScanner.cpp
@@ -56,6 +56,53 @@
return OK;
}
+// stolen from dalvik/vm/checkJni.cpp
+static bool isValidUtf8(const char* bytes) {
+ while (*bytes != '\0') {
+ unsigned char utf8 = *(bytes++);
+ // Switch on the high four bits.
+ switch (utf8 >> 4) {
+ case 0x00:
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ // Bit pattern 0xxx. No need for any extra bytes.
+ break;
+ case 0x08:
+ case 0x09:
+ case 0x0a:
+ case 0x0b:
+ case 0x0f:
+ /*
+ * Bit pattern 10xx or 1111, which are illegal start bytes.
+ * Note: 1111 is valid for normal UTF-8, but not the
+ * modified UTF-8 used here.
+ */
+ return false;
+ case 0x0e:
+ // Bit pattern 1110, so there are two additional bytes.
+ utf8 = *(bytes++);
+ if ((utf8 & 0xc0) != 0x80) {
+ return false;
+ }
+ // Fall through to take care of the final byte.
+ case 0x0c:
+ case 0x0d:
+ // Bit pattern 110x, so there is one additional byte.
+ utf8 = *(bytes++);
+ if ((utf8 & 0xc0) != 0x80) {
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
class MyMediaScannerClient : public MediaScannerClient
{
public:
@@ -123,7 +170,22 @@
mEnv->ExceptionClear();
return NO_MEMORY;
}
- if ((valueStr = mEnv->NewStringUTF(value)) == NULL) {
+ char *cleaned = NULL;
+ if (!isValidUtf8(value)) {
+ cleaned = strdup(value);
+ char *chp = cleaned;
+ char ch;
+ while ((ch = *chp)) {
+ if (ch & 0x80) {
+ *chp = '?';
+ }
+ chp++;
+ }
+ value = cleaned;
+ }
+ valueStr = mEnv->NewStringUTF(value);
+ free(cleaned);
+ if (valueStr == NULL) {
mEnv->DeleteLocalRef(nameStr);
mEnv->ExceptionClear();
return NO_MEMORY;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 7cdb76c..70208f8 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -282,7 +282,7 @@
if (err == -EWOULDBLOCK) {
if (mSource->feedMoreTSData() == OK) {
- msg->post();
+ msg->post(10000ll);
}
}
} else if (what == ACodec::kWhatEOS) {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
index e3aa8cf..1fa5c0d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaBassBoostTest.java
@@ -44,13 +44,7 @@
*/
public class MediaBassBoostTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
private String TAG = "MediaBassBoostTest";
- private final static int MIN_ENERGY_RATIO_2 = 3;
private final static short TEST_STRENGTH = 500;
- private final static int TEST_VOLUME = 4;
- // Implementor UUID for volume controller effect defined in
- // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
- private final static UUID VOLUME_EFFECT_UUID =
- UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
private BassBoost mBassBoost = null;
private int mSession = -1;
@@ -184,85 +178,6 @@
}
//-----------------------------------------------------------------
- // 2 - Effect action
- //----------------------------------
-
- //Test case 2.0: test actual bass boost influence on sound
- @LargeTest
- public void test2_0SoundModification() throws Exception {
- boolean result = false;
- String msg = "test2_0SoundModification()";
- EnergyProbe probe = null;
- AudioEffect vc = null;
- MediaPlayer mp = null;
- AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- TEST_VOLUME,
- 0);
-
- try {
- probe = new EnergyProbe(0);
- // creating a volume controller on output mix ensures that ro.audio.silent mutes
- // audio after the effects and not before
- vc = new AudioEffect(
- AudioEffect.EFFECT_TYPE_NULL,
- VOLUME_EFFECT_UUID,
- 0,
- 0);
- vc.setEnabled(true);
-
- mp = new MediaPlayer();
- mp.setDataSource(MediaNames.SINE_200_1000);
- mp.setLooping(true);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- getBassBoost(mp.getAudioSessionId());
- mp.prepare();
- mp.start();
- Thread.sleep(200);
- // measure reference energy around 1kHz
- int refEnergy200 = probe.capture(200);
- int refEnergy1000 = probe.capture(1000);
- mBassBoost.setStrength((short)1000);
- mBassBoost.setEnabled(true);
- Thread.sleep(4000);
- // measure energy around 1kHz with band level at min
- int energy200 = probe.capture(200);
- int energy1000 = probe.capture(1000);
- // verify that the energy ration between low and high frequencies is at least
- // MIN_ENERGY_RATIO_2 times higher with bassboost on.
- assertTrue(msg + ": bass boost has no effect",
- ((float)energy200/(float)energy1000) >
- (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
- result = true;
- } catch (IllegalArgumentException e) {
- msg = msg.concat(": Bad parameter value");
- loge(msg, "Bad parameter value");
- } catch (UnsupportedOperationException e) {
- msg = msg.concat(": get parameter() rejected");
- loge(msg, "get parameter() rejected");
- } catch (IllegalStateException e) {
- msg = msg.concat("get parameter() called in wrong state");
- loge(msg, "get parameter() called in wrong state");
- } catch (InterruptedException e) {
- loge(msg, "sleep() interrupted");
- }
- finally {
- releaseBassBoost();
- if (mp != null) {
- mp.release();
- }
- if (vc != null) {
- vc.release();
- }
- if (probe != null) {
- probe.release();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
- }
- assertTrue(msg, result);
- }
- //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
index ee91bbb..da9089d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaEqualizerTest.java
@@ -49,11 +49,6 @@
private final static int MAX_BAND_LEVEL = 1500;
private final static int TEST_FREQUENCY_MILLIHERTZ = 1000000;
private final static int MIN_NUMBER_OF_PRESETS = 4;
- private final static int TEST_VOLUME = 4;
- // Implementor UUID for volume controller effect defined in
- // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
- private final static UUID VOLUME_EFFECT_UUID =
- UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
private Equalizer mEqualizer = null;
private int mSession = -1;
@@ -252,80 +247,6 @@
}
//-----------------------------------------------------------------
- // 2 - Effect action
- //----------------------------------
-
- //Test case 2.0: test that the equalizer actually alters the sound
- @LargeTest
- public void test2_0SoundModification() throws Exception {
- boolean result = false;
- String msg = "test2_0SoundModification()";
- EnergyProbe probe = null;
- AudioEffect vc = null;
- MediaPlayer mp = null;
- AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- TEST_VOLUME,
- 0);
- try {
- probe = new EnergyProbe(0);
- // creating a volume controller on output mix ensures that ro.audio.silent mutes
- // audio after the effects and not before
- vc = new AudioEffect(
- AudioEffect.EFFECT_TYPE_NULL,
- VOLUME_EFFECT_UUID,
- 0,
- 0);
- vc.setEnabled(true);
-
- mp = new MediaPlayer();
- mp.setDataSource(MediaNames.SINE_200_1000);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- getEqualizer(mp.getAudioSessionId());
- mp.prepare();
- mp.start();
- Thread.sleep(500);
- // measure reference energy around 1kHz
- int refEnergy = probe.capture(1000);
- short band = mEqualizer.getBand(1000000);
- short[] levelRange = mEqualizer.getBandLevelRange();
- mEqualizer.setBandLevel(band, levelRange[0]);
- mEqualizer.setEnabled(true);
- Thread.sleep(500);
- // measure energy around 1kHz with band level at min
- int energy = probe.capture(1000);
- assertTrue(msg + ": equalizer has no effect at 1kHz", energy < refEnergy/4);
- result = true;
- } catch (IllegalArgumentException e) {
- msg = msg.concat(": Bad parameter value");
- loge(msg, "Bad parameter value");
- } catch (UnsupportedOperationException e) {
- msg = msg.concat(": get parameter() rejected");
- loge(msg, "get parameter() rejected");
- } catch (IllegalStateException e) {
- msg = msg.concat("get parameter() called in wrong state");
- loge(msg, "get parameter() called in wrong state");
- } catch (InterruptedException e) {
- loge(msg, "sleep() interrupted");
- }
- finally {
- releaseEqualizer();
- if (mp != null) {
- mp.release();
- }
- if (vc != null) {
- vc.release();
- }
- if (probe != null) {
- probe.release();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
- }
- assertTrue(msg, result);
- }
-
- //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
index b74e525..122545f 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/audio/MediaVirtualizerTest.java
@@ -44,13 +44,7 @@
*/
public class MediaVirtualizerTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
private String TAG = "MediaVirtualizerTest";
- private final static int MIN_ENERGY_RATIO_2 = 2;
private final static short TEST_STRENGTH = 500;
- private final static int TEST_VOLUME = 4;
- // Implementor UUID for volume controller effect defined in
- // frameworks/base/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
- private final static UUID VOLUME_EFFECT_UUID =
- UUID.fromString("119341a0-8469-11df-81f9-0002a5d5c51b");
private Virtualizer mVirtualizer = null;
private int mSession = -1;
@@ -185,89 +179,6 @@
}
//-----------------------------------------------------------------
- // 2 - Effect action
- //----------------------------------
-
- //Test case 2.0: test actual virtualizer influence on sound
- @LargeTest
- public void test2_0SoundModification() throws Exception {
- boolean result = false;
- String msg = "test2_0SoundModification()";
- EnergyProbe probe = null;
- AudioEffect vc = null;
- MediaPlayer mp = null;
- AudioManager am = (AudioManager) getActivity().getSystemService(Context.AUDIO_SERVICE);
- int volume = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- am.setStreamVolume(AudioManager.STREAM_MUSIC,
- TEST_VOLUME,
- 0);
-
- try {
- probe = new EnergyProbe(0);
- // creating a volume controller on output mix ensures that ro.audio.silent mutes
- // audio after the effects and not before
- vc = new AudioEffect(
- AudioEffect.EFFECT_TYPE_NULL,
- VOLUME_EFFECT_UUID,
- 0,
- 0);
- vc.setEnabled(true);
-
- mp = new MediaPlayer();
- mp.setDataSource(MediaNames.SINE_200_1000);
- mp.setLooping(true);
- mp.setAudioStreamType(AudioManager.STREAM_MUSIC);
- getVirtualizer(mp.getAudioSessionId());
- mp.prepare();
- mp.start();
- Thread.sleep(200);
- // measure reference energy around 1kHz
- int refEnergy200 = probe.capture(200);
- int refEnergy1000 = probe.capture(1000);
- mVirtualizer.setStrength((short)1000);
- mVirtualizer.setEnabled(true);
- Thread.sleep(4000);
- // measure energy around 1kHz with band level at min
- int energy200 = probe.capture(200);
- int energy1000 = probe.capture(1000);
- // verify that the energy ration between low and high frequencies is at least
- // MIN_ENERGY_RATIO_2 times higher with virtualizer on.
- // NOTE: this is what is observed with current virtualizer implementation and the test
- // audio file but is not the primary effect of the virtualizer. A better way would
- // be to have a stereo PCM capture and check that a strongly paned input is centered
- // when output. However, we cannot capture stereo with the visualizer.
- assertTrue(msg + ": virtualizer has no effect",
- ((float)energy200/(float)energy1000) >
- (MIN_ENERGY_RATIO_2 * ((float)refEnergy200/(float)refEnergy1000)));
- result = true;
- } catch (IllegalArgumentException e) {
- msg = msg.concat(": Bad parameter value");
- loge(msg, "Bad parameter value");
- } catch (UnsupportedOperationException e) {
- msg = msg.concat(": get parameter() rejected");
- loge(msg, "get parameter() rejected");
- } catch (IllegalStateException e) {
- msg = msg.concat("get parameter() called in wrong state");
- loge(msg, "get parameter() called in wrong state");
- } catch (InterruptedException e) {
- loge(msg, "sleep() interrupted");
- }
- finally {
- releaseVirtualizer();
- if (mp != null) {
- mp.release();
- }
- if (vc != null) {
- vc.release();
- }
- if (probe != null) {
- probe.release();
- }
- am.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
- }
- assertTrue(msg, result);
- }
- //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 7be2349..9ed7c98 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -390,8 +390,8 @@
boolean mLockScreenTimerActive;
// visual screen saver support
- int mScreenSaverTimeout;
- boolean mScreenSaverEnabled = false;
+ int mScreenSaverTimeout = 0;
+ boolean mScreenSaverEnabled = true;
// Behavior of ENDCALL Button. (See Settings.System.END_BUTTON_BEHAVIOR.)
int mEndcallBehavior;
@@ -455,7 +455,7 @@
Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
resolver.registerContentObserver(Settings.System.getUriFor(
"fancy_rotation_anim"), false, this);
- resolver.registerContentObserver(Settings.System.getUriFor(
+ resolver.registerContentObserver(Settings.Secure.getUriFor(
Settings.Secure.DREAM_TIMEOUT), false, this);
updateSettings();
}
@@ -914,9 +914,8 @@
updateRotation = true;
}
- mScreenSaverTimeout = Settings.System.getInt(resolver,
+ mScreenSaverTimeout = Settings.Secure.getInt(resolver,
Settings.Secure.DREAM_TIMEOUT, 0);
- mScreenSaverEnabled = true;
updateScreenSaverTimeoutLocked();
}
if (updateRotation) {
@@ -3440,70 +3439,59 @@
}
}
- // Turn this off for now, screen savers not currently enabled.
- if (false) {
- synchronized (mLock) {
- updateScreenSaverTimeoutLocked();
- }
+ synchronized (mLock) {
+ // Only posts messages; holds no additional locks.
+ updateScreenSaverTimeoutLocked();
}
}
- Runnable mScreenSaverActivator = null;
- /*new Runnable() {
+ Runnable mScreenSaverActivator = new Runnable() {
public void run() {
- synchronized (this) {
- if (!(mScreenSaverEnabled && mScreenOn)) {
- Log.w(TAG, "mScreenSaverActivator ran, but the screensaver should not be showing. Who's driving this thing?");
- return;
- }
+ if (!(mScreenSaverEnabled && mScreenOnEarly)) {
+ Log.w(TAG, "mScreenSaverActivator ran, but the screensaver should not be showing. Who's driving this thing?");
+ return;
+ }
- if (localLOGV) Log.v(TAG, "mScreenSaverActivator entering dreamland");
- try {
- String component = Settings.System.getString(
- mContext.getContentResolver(), Settings.Secure.DREAM_COMPONENT);
- if (component != null) {
- ComponentName cn = ComponentName.unflattenFromString(component);
- Intent intent = new Intent(Intent.ACTION_MAIN)
- .setComponent(cn)
- .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_NO_USER_ACTION
- | Intent.FLAG_ACTIVITY_SINGLE_TOP);
- mContext.startActivity(intent);
- } else {
- Log.e(TAG, "Couldn't start screen saver: none selected");
- }
- } catch (android.content.ActivityNotFoundException exc) {
- // no screensaver? give up
- Log.e(TAG, "Couldn't start screen saver: none installed");
+ if (localLOGV) Log.v(TAG, "mScreenSaverActivator entering dreamland");
+ try {
+ String component = Settings.Secure.getString(
+ mContext.getContentResolver(), Settings.Secure.DREAM_COMPONENT);
+ if (component != null) {
+ ComponentName cn = ComponentName.unflattenFromString(component);
+ Intent intent = new Intent(Intent.ACTION_MAIN)
+ .setComponent(cn)
+ .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_NO_USER_ACTION
+ );
+ mContext.startActivity(intent);
+ } else {
+ Log.e(TAG, "Couldn't start screen saver: none selected");
}
+ } catch (android.content.ActivityNotFoundException exc) {
+ // no screensaver? give up
+ Log.e(TAG, "Couldn't start screen saver: none installed");
}
}
};
- */
// Must call while holding mLock
private void updateScreenSaverTimeoutLocked() {
if (mScreenSaverActivator == null) return;
- // GAH... acquiring a lock within a lock? Please let's fix this.
- // (Also note this is called from userActivity, with the power manager
- // lock held. Not good.)
- synchronized (mScreenSaverActivator) {
- mHandler.removeCallbacks(mScreenSaverActivator);
- if (mScreenSaverEnabled && mScreenOnEarly && mScreenSaverTimeout > 0) {
- if (localLOGV)
- Log.v(TAG, "scheduling screensaver for " + mScreenSaverTimeout + "ms from now");
- mHandler.postDelayed(mScreenSaverActivator, mScreenSaverTimeout);
- } else {
- if (localLOGV) {
- if (mScreenSaverTimeout == 0)
- Log.v(TAG, "screen saver disabled by user");
- else if (!mScreenOnEarly)
- Log.v(TAG, "screen saver disabled while screen off");
- else
- Log.v(TAG, "screen saver disabled by wakelock");
- }
+ mHandler.removeCallbacks(mScreenSaverActivator);
+ if (mScreenSaverEnabled && mScreenOnEarly && mScreenSaverTimeout > 0) {
+ if (localLOGV)
+ Log.v(TAG, "scheduling screensaver for " + mScreenSaverTimeout + "ms from now");
+ mHandler.postDelayed(mScreenSaverActivator, mScreenSaverTimeout);
+ } else {
+ if (localLOGV) {
+ if (mScreenSaverTimeout == 0)
+ Log.v(TAG, "screen saver disabled by user");
+ else if (!mScreenOnEarly)
+ Log.v(TAG, "screen saver disabled while screen off");
+ else
+ Log.v(TAG, "screen saver disabled by wakelock");
}
}
}
diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java
index eb75ebc..2af5103 100644
--- a/services/java/com/android/server/AppWidgetService.java
+++ b/services/java/com/android/server/AppWidgetService.java
@@ -751,10 +751,13 @@
return;
}
ArrayList<AppWidgetId> instances = p.instances;
+ final int callingUid = getCallingUid();
final int N = instances.size();
for (int i=0; i<N; i++) {
AppWidgetId id = instances.get(i);
- updateAppWidgetInstanceLocked(id, views);
+ if (canAccessAppWidgetId(id, callingUid)) {
+ updateAppWidgetInstanceLocked(id, views);
+ }
}
}
}
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index e9bc41d..74fdcde 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -300,7 +300,7 @@
/**
* Historical data of past broadcasts, for debugging.
*/
- static final int MAX_BROADCAST_HISTORY = 100;
+ static final int MAX_BROADCAST_HISTORY = 25;
final BroadcastRecord[] mBroadcastHistory
= new BroadcastRecord[MAX_BROADCAST_HISTORY];
@@ -13941,7 +13941,7 @@
if (curLevel >= ComponentCallbacks2.TRIM_MEMORY_COMPLETE) {
// For these apps we will also finish their activities
// to help them free memory.
- mMainStack.destroyActivitiesLocked(app, false);
+ mMainStack.destroyActivitiesLocked(app, false, "trim");
}
}
app.trimMemoryLevel = curLevel;
@@ -14005,7 +14005,7 @@
}
if (mAlwaysFinishActivities) {
- mMainStack.destroyActivitiesLocked(null, false);
+ mMainStack.destroyActivitiesLocked(null, false, "always-finish");
}
}
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index a47502e..8435eaa 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -947,7 +947,7 @@
r.state = ActivityState.STOPPED;
if (!r.finishing) {
if (r.configDestroy) {
- destroyActivityLocked(r, true, false);
+ destroyActivityLocked(r, true, false, "stop-config");
resumeTopActivityLocked(null);
}
}
@@ -976,7 +976,7 @@
// instance right now, we need to first completely stop
// the current instance before starting the new one.
if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev);
- destroyActivityLocked(prev, true, false);
+ destroyActivityLocked(prev, true, false, "pause-config");
} else {
mStoppingActivities.add(prev);
if (mStoppingActivities.size() > 3) {
@@ -1364,7 +1364,8 @@
}
}
- if (!prev.finishing && prev.app != null && prev.app != next.app) {
+ if (!prev.finishing && prev.app != null && prev.app != next.app
+ && prev.app != mService.mHomeProcess) {
// We are switching to a new activity that is in a different
// process than the previous one. Note the previous process,
// so we can try to keep it around.
@@ -3113,7 +3114,7 @@
if (DEBUG_STATES) Slog.v(TAG, "Stop failed; moving to STOPPED: " + r);
r.state = ActivityState.STOPPED;
if (r.configDestroy) {
- destroyActivityLocked(r, true, false);
+ destroyActivityLocked(r, true, false, "stop-except");
}
}
}
@@ -3288,7 +3289,7 @@
for (i=0; i<NF; i++) {
ActivityRecord r = (ActivityRecord)finishes.get(i);
synchronized (mService) {
- destroyActivityLocked(r, true, false);
+ destroyActivityLocked(r, true, false, "finish-idle");
}
}
@@ -3487,7 +3488,7 @@
|| prevState == ActivityState.INITIALIZING) {
// If this activity is already stopped, we can just finish
// it right now.
- return destroyActivityLocked(r, true, true) ? null : r;
+ return destroyActivityLocked(r, true, true, "finish-imm") ? null : r;
} else {
// Need to go through the full pause cycle to get this
// activity into the stopped state and then finish it.
@@ -3593,7 +3594,7 @@
}
}
- final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj) {
+ final void destroyActivitiesLocked(ProcessRecord owner, boolean oomAdj, String reason) {
for (int i=mHistory.size()-1; i>=0; i--) {
ActivityRecord r = mHistory.get(i);
if (owner != null && r.app != owner) {
@@ -3604,7 +3605,7 @@
if (r.app != null && r.haveState && !r.visible && r.stopped && !r.finishing
&& r.state != ActivityState.DESTROYING
&& r.state != ActivityState.DESTROYED) {
- destroyActivityLocked(r, true, oomAdj);
+ destroyActivityLocked(r, true, oomAdj, "trim");
}
}
}
@@ -3616,13 +3617,13 @@
* but then create a new client-side object for this same HistoryRecord.
*/
final boolean destroyActivityLocked(ActivityRecord r,
- boolean removeFromApp, boolean oomAdj) {
+ boolean removeFromApp, boolean oomAdj, String reason) {
if (DEBUG_SWITCH) Slog.v(
TAG, "Removing activity: token=" + r
+ ", app=" + (r.app != null ? r.app.processName : "(null)"));
EventLog.writeEvent(EventLogTags.AM_DESTROY_ACTIVITY,
System.identityHashCode(r),
- r.task.taskId, r.shortComponentName);
+ r.task.taskId, r.shortComponentName, reason);
boolean removedFromHistory = false;
@@ -4109,7 +4110,7 @@
if (r.app == null || r.app.thread == null) {
if (DEBUG_SWITCH || DEBUG_CONFIGURATION) Slog.v(TAG,
"Switch is destroying non-running " + r);
- destroyActivityLocked(r, true, false);
+ destroyActivityLocked(r, true, false, "config");
} else if (r.state == ActivityState.PAUSING) {
// A little annoying: we are waiting for this activity to
// finish pausing. Let's not do anything now, but just
diff --git a/services/java/com/android/server/am/EventLogTags.logtags b/services/java/com/android/server/am/EventLogTags.logtags
index aadd37d..a579f44 100644
--- a/services/java/com/android/server/am/EventLogTags.logtags
+++ b/services/java/com/android/server/am/EventLogTags.logtags
@@ -48,7 +48,7 @@
# Reporting to applications that memory is low
30017 am_low_memory (Num Processes|1|1)
# An activity is being destroyed:
-30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
+30018 am_destroy_activity (Token|1|5),(Task ID|1|5),(Component Name|3),(Reason|3)
# An activity has been relaunched, resumed, and is now in the foreground:
30019 am_relaunch_resume_activity (Token|1|5),(Task ID|1|5),(Component Name|3)
# An activity has been relaunched:
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index bdad82a..289ea1f 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -19,7 +19,6 @@
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
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_NETWORK_USAGE_HISTORY;
import static android.Manifest.permission.READ_PHONE_STATE;
@@ -93,6 +92,7 @@
import android.os.INetworkManagementService;
import android.os.IPowerManager;
import android.os.Message;
+import android.os.MessageQueue.IdleHandler;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.provider.Settings;
@@ -1583,6 +1583,11 @@
return intent;
}
+ // @VisibleForTesting
+ public void addIdleHandler(IdleHandler handler) {
+ mHandler.getLooper().getQueue().addIdleHandler(handler);
+ }
+
private static void collectKeys(SparseIntArray source, SparseBooleanArray target) {
final int size = source.size();
for (int i = 0; i < size; i++) {
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 789681e..494c655 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -32,7 +32,6 @@
import static android.net.NetworkStats.SET_FOREGROUND;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkStatsHistory.randomLong;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.UID_REMOVED;
@@ -49,7 +48,6 @@
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
-import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static com.android.internal.util.Preconditions.checkNotNull;
import static com.android.server.NetworkManagementService.LIMIT_GLOBAL_ALERT;
import static com.android.server.NetworkManagementSocketTagger.resetKernelUidStats;
@@ -73,9 +71,11 @@
import android.net.NetworkInfo;
import android.net.NetworkState;
import android.net.NetworkStats;
+import android.net.NetworkStats.NonMonotonicException;
import android.net.NetworkStatsHistory;
import android.net.NetworkTemplate;
import android.os.Binder;
+import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
@@ -150,6 +150,12 @@
/** Sample recent usage after each poll event. */
private static final boolean ENABLE_SAMPLE_AFTER_POLL = true;
+ private static final String TAG_NETSTATS_ERROR = "netstats_error";
+
+ private static final String DEV = "dev";
+ private static final String XT = "xt";
+ private static final String UID = "uid";
+
private final Context mContext;
private final INetworkManagementService mNetworkManager;
private final IAlarmManager mAlarmManager;
@@ -160,6 +166,7 @@
private final PowerManager.WakeLock mWakeLock;
private IConnectivityManager mConnManager;
+ private DropBoxManager mDropBox;
// @VisibleForTesting
public static final String ACTION_NETWORK_STATS_POLL =
@@ -306,6 +313,8 @@
// bootstrap initial stats to prevent double-counting later
bootstrapStats();
+
+ mDropBox = (DropBoxManager) mContext.getSystemService(Context.DROPBOX_SERVICE);
}
private void shutdownLocked() {
@@ -621,7 +630,6 @@
// broadcast.
final int uid = intent.getIntExtra(EXTRA_UID, 0);
synchronized (mStatsLock) {
- // TODO: perform one last stats poll for UID
mWakeLock.acquire();
try {
removeUidLocked(uid);
@@ -829,9 +837,9 @@
// persist when enough network data has occurred
final long persistNetworkDevDelta = computeStatsDelta(
- mLastPersistNetworkDevSnapshot, networkDevSnapshot, true).getTotalBytes();
+ mLastPersistNetworkDevSnapshot, networkDevSnapshot, true, DEV).getTotalBytes();
final long persistNetworkXtDelta = computeStatsDelta(
- mLastPersistNetworkXtSnapshot, networkXtSnapshot, true).getTotalBytes();
+ mLastPersistNetworkXtSnapshot, networkXtSnapshot, true, XT).getTotalBytes();
final boolean networkOverThreshold = persistNetworkDevDelta > threshold
|| persistNetworkXtDelta > threshold;
if (persistForce || (persistNetwork && networkOverThreshold)) {
@@ -842,8 +850,8 @@
}
// persist when enough uid data has occurred
- final long persistUidDelta = computeStatsDelta(mLastPersistUidSnapshot, uidSnapshot, true)
- .getTotalBytes();
+ final long persistUidDelta = computeStatsDelta(
+ mLastPersistUidSnapshot, uidSnapshot, true, UID).getTotalBytes();
if (persistForce || (persistUid && persistUidDelta > threshold)) {
writeUidStatsLocked();
mLastPersistUidSnapshot = uidSnapshot;
@@ -872,7 +880,7 @@
final HashSet<String> unknownIface = Sets.newHashSet();
final NetworkStats delta = computeStatsDelta(
- mLastPollNetworkDevSnapshot, networkDevSnapshot, false);
+ mLastPollNetworkDevSnapshot, networkDevSnapshot, false, DEV);
final long timeStart = currentTime - delta.getElapsedRealtime();
NetworkStats.Entry entry = null;
@@ -902,7 +910,7 @@
final HashSet<String> unknownIface = Sets.newHashSet();
final NetworkStats delta = computeStatsDelta(
- mLastPollNetworkXtSnapshot, networkXtSnapshot, false);
+ mLastPollNetworkXtSnapshot, networkXtSnapshot, false, XT);
final long timeStart = currentTime - delta.getElapsedRealtime();
NetworkStats.Entry entry = null;
@@ -931,9 +939,10 @@
private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
ensureUidStatsLoadedLocked();
- final NetworkStats delta = computeStatsDelta(mLastPollUidSnapshot, uidSnapshot, false);
+ final NetworkStats delta = computeStatsDelta(
+ mLastPollUidSnapshot, uidSnapshot, false, UID);
final NetworkStats operationsDelta = computeStatsDelta(
- mLastPollOperationsSnapshot, mOperations, false);
+ mLastPollOperationsSnapshot, mOperations, false, UID);
final long timeStart = currentTime - delta.getElapsedRealtime();
NetworkStats.Entry entry = null;
@@ -1014,6 +1023,9 @@
private void removeUidLocked(int uid) {
ensureUidStatsLoadedLocked();
+ // perform one last poll before removing
+ performPollLocked(FLAG_PERSIST_ALL);
+
final ArrayList<UidStatsKey> knownKeys = Lists.newArrayList();
knownKeys.addAll(mUidStats.keySet());
@@ -1031,6 +1043,10 @@
}
}
+ // clear UID from current stats snapshot
+ mLastPollUidSnapshot = mLastPollUidSnapshot.withoutUid(uid);
+ mLastPollNetworkXtSnapshot = computeNetworkXtSnapshotFromUid(mLastPollUidSnapshot);
+
// clear kernel stats associated with UID
resetKernelUidStats(uid);
@@ -1490,10 +1506,25 @@
* Return the delta between two {@link NetworkStats} snapshots, where {@code
* before} can be {@code null}.
*/
- private static NetworkStats computeStatsDelta(
- NetworkStats before, NetworkStats current, boolean collectStale) {
+ private NetworkStats computeStatsDelta(
+ NetworkStats before, NetworkStats current, boolean collectStale, String type) {
if (before != null) {
- return current.subtractClamped(before);
+ try {
+ return current.subtract(before);
+ } catch (NonMonotonicException e) {
+ Log.w(TAG, "found non-monotonic values; saving to dropbox");
+
+ // record error for debugging
+ final StringBuilder builder = new StringBuilder();
+ builder.append("found non-monotonic " + type + "values at left[" + e.leftIndex
+ + "] - right[" + e.rightIndex + "]\n");
+ builder.append("left=").append(e.left).append('\n');
+ builder.append("right=").append(e.right).append('\n');
+ mDropBox.addText(TAG_NETSTATS_ERROR, builder.toString());
+
+ // return empty delta to avoid recording broken stats
+ return new NetworkStats(0L, 10);
+ }
} else if (collectStale) {
// caller is okay collecting stale stats for first call.
return current;
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index e892b5e..368595f 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -66,6 +66,7 @@
import android.os.Binder;
import android.os.INetworkManagementService;
import android.os.IPowerManager;
+import android.os.MessageQueue.IdleHandler;
import android.test.AndroidTestCase;
import android.test.mock.MockPackageManager;
import android.test.suitebuilder.annotation.LargeTest;
@@ -87,6 +88,7 @@
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
+import java.util.logging.Handler;
import libcore.io.IoUtils;
@@ -100,6 +102,10 @@
private static final long TEST_START = 1194220800000L;
private static final String TEST_IFACE = "test0";
+ 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 NetworkTemplate sTemplateWifi = NetworkTemplate.buildTemplateWifi();
private BroadcastInterceptingContext mServiceContext;
@@ -255,31 +261,37 @@
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, false);
+ waitUntilIdle();
assertFalse(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// push one of the shared pids into foreground
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
+ waitUntilIdle();
assertTrue(mService.isUidForeground(UID_A));
assertFalse(mService.isUidForeground(UID_B));
// and swap another uid into foreground
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
mProcessObserver.onForegroundActivitiesChanged(PID_3, UID_B, true);
+ waitUntilIdle();
assertFalse(mService.isUidForeground(UID_A));
assertTrue(mService.isUidForeground(UID_B));
// push both pid into foreground
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, true);
+ waitUntilIdle();
assertTrue(mService.isUidForeground(UID_A));
// pull one out, should still be foreground
mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+ waitUntilIdle();
assertTrue(mService.isUidForeground(UID_A));
// pull final pid out, should now be background
mProcessObserver.onForegroundActivitiesChanged(PID_2, UID_A, false);
+ waitUntilIdle();
assertFalse(mService.isUidForeground(UID_A));
}
@@ -528,13 +540,14 @@
// TODO: consider making strongly ordered mock
expectRemoveInterfaceQuota(TEST_IFACE);
- expectSetInterfaceQuota(TEST_IFACE, 1536L);
+ expectSetInterfaceQuota(TEST_IFACE, (2 * MB_IN_BYTES) - 512);
expectClearNotifications();
future = expectMeteredIfacesChanged(TEST_IFACE);
replay();
- setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L, SNOOZE_NEVER));
+ setNetworkPolicies(new NetworkPolicy(
+ sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER));
future.get();
verifyAndReset();
}
@@ -590,8 +603,8 @@
future = expectMeteredIfacesChanged();
replay();
- setNetworkPolicies(
- new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L, SNOOZE_NEVER));
+ setNetworkPolicies(new NetworkPolicy(
+ sTemplateWifi, CYCLE_DAY, 1 * MB_IN_BYTES, 2 * MB_IN_BYTES, SNOOZE_NEVER));
future.get();
verifyAndReset();
}
@@ -609,7 +622,7 @@
.andReturn(stats).atLeastOnce();
expectRemoveInterfaceQuota(TEST_IFACE);
- expectSetInterfaceQuota(TEST_IFACE, 2048L);
+ expectSetInterfaceQuota(TEST_IFACE, 2 * MB_IN_BYTES);
expectClearNotifications();
future = expectMeteredIfacesChanged(TEST_IFACE);
@@ -623,7 +636,7 @@
// go over warning, which should kick notification
incrementCurrentTime(MINUTE_IN_MILLIS);
stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 1536L, 15L, 0L, 0L);
+ .addIfaceValues(TEST_IFACE, 1536 * KB_IN_BYTES, 15L, 0L, 0L);
{
expectCurrentTime();
@@ -643,7 +656,7 @@
// go over limit, which should kick notification and dialog
incrementCurrentTime(MINUTE_IN_MILLIS);
stats = new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 5120L, 512L, 0L, 0L);
+ .addIfaceValues(TEST_IFACE, 5 * MB_IN_BYTES, 512L, 0L, 0L);
{
expectCurrentTime();
@@ -799,6 +812,32 @@
}
}
+ private static class IdleFuture extends AbstractFuture<Void> implements IdleHandler {
+ @Override
+ public Void get() throws InterruptedException, ExecutionException {
+ try {
+ return get(5, TimeUnit.SECONDS);
+ } catch (TimeoutException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean queueIdle() {
+ set(null);
+ return false;
+ }
+ }
+
+ /**
+ * Wait until {@link #mService} internal {@link Handler} is idle.
+ */
+ private void waitUntilIdle() throws Exception {
+ final IdleFuture future = new IdleFuture();
+ mService.addIdleHandler(future);
+ future.get();
+ }
+
private static void assertTimeEquals(long expected, long actual) {
if (expected != actual) {
fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index f7dff23..fbc171b 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -83,6 +83,7 @@
private static final String TAG = "NetworkStatsServiceTest";
private static final String TEST_IFACE = "test0";
+ private static final String TEST_IFACE2 = "test1";
private static final long TEST_START = 1194220800000L;
private static final String IMSI_1 = "310004";
@@ -418,8 +419,12 @@
expectCurrentTime();
expectDefaultSettings();
expectNetworkState(buildMobile3gState(IMSI_2));
- expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 2048L, 16L, 512L, 4L));
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 3)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 512L, 4L, 0L, 0L, 0L));
expectNetworkStatsPoll();
replay();
@@ -432,9 +437,11 @@
expectCurrentTime();
expectDefaultSettings();
expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
- .addIfaceValues(TEST_IFACE, 128L, 1L, 1024L, 8L));
+ .addIfaceValues(TEST_IFACE, 2176L, 17L, 1536L, 12L));
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 128L, 1L, 1024L, 8L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1536L, 12L, 512L, 4L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 640L, 5L, 1024L, 8L, 0L)
.addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, 0xFAAD, 128L, 1L, 1024L, 8L, 0L));
expectNetworkStatsPoll();
@@ -499,6 +506,15 @@
// special "removed" bucket.
expectCurrentTime();
expectDefaultSettings();
+ expectNetworkStatsSummary(new NetworkStats(getElapsedRealtime(), 1)
+ .addIfaceValues(TEST_IFACE, 4128L, 258L, 544L, 34L));
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 16L, 1L, 16L, 1L, 0L)
+ .addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 4096L, 258L, 512L, 32L, 0L)
+ .addValues(TEST_IFACE, UID_GREEN, SET_DEFAULT, TAG_NONE, 16L, 1L, 16L, 1L, 0L));
+ expectNetworkStatsPoll();
+
replay();
final Intent intent = new Intent(ACTION_UID_REMOVED);
intent.putExtra(EXTRA_UID, UID_BLUE);
@@ -553,9 +569,11 @@
incrementCurrentTime(HOUR_IN_MILLIS);
expectCurrentTime();
expectDefaultSettings();
- expectNetworkState(buildMobile4gState());
+ expectNetworkState(buildMobile4gState(TEST_IFACE2));
expectNetworkStatsSummary(buildEmptyStats());
- expectNetworkStatsUidDetail(buildEmptyStats());
+ expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L));
expectNetworkStatsPoll();
replay();
@@ -569,8 +587,10 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
- .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 1024L, 8L, 1024L, 8L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 512L, 4L, 512L, 4L, 0L)
+ .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, TAG_NONE, 512L, 4L, 256L, 2L, 0L)
+ .addValues(TEST_IFACE2, UID_RED, SET_DEFAULT, 0xFAAD, 512L, 4L, 256L, 2L, 0L));
expectNetworkStatsPoll();
mService.incrementOperationCount(UID_RED, 0xFAAD, 5);
@@ -625,6 +645,8 @@
expectDefaultSettings();
expectNetworkStatsSummary(buildEmptyStats());
expectNetworkStatsUidDetail(new NetworkStats(getElapsedRealtime(), 1)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, TAG_NONE, 50L, 5L, 50L, 5L, 0L)
+ .addValues(TEST_IFACE, UID_RED, SET_DEFAULT, 0xF00D, 10L, 1L, 10L, 1L, 0L)
.addValues(TEST_IFACE, UID_BLUE, SET_DEFAULT, TAG_NONE, 2048L, 16L, 1024L, 8L, 0L));
expectNetworkStatsPoll();
@@ -881,11 +903,11 @@
return new NetworkState(info, prop, null, subscriberId);
}
- private static NetworkState buildMobile4gState() {
+ private static NetworkState buildMobile4gState(String iface) {
final NetworkInfo info = new NetworkInfo(TYPE_WIMAX, 0, null, null);
info.setDetailedState(DetailedState.CONNECTED, null, null);
final LinkProperties prop = new LinkProperties();
- prop.setInterfaceName(TEST_IFACE);
+ prop.setInterfaceName(iface);
return new NetworkState(info, prop, null);
}
diff --git a/tests/FrameworkPerf/res/layout/button_layout.xml b/tests/FrameworkPerf/res/layout/button_layout.xml
new file mode 100644
index 0000000..7786a25
--- /dev/null
+++ b/tests/FrameworkPerf/res/layout/button_layout.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+ <Button android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:text="FooBarYou" />
+</LinearLayout>
diff --git a/tests/FrameworkPerf/res/layout/image_button_layout.xml b/tests/FrameworkPerf/res/layout/image_button_layout.xml
new file mode 100644
index 0000000..65b12b3
--- /dev/null
+++ b/tests/FrameworkPerf/res/layout/image_button_layout.xml
@@ -0,0 +1,119 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+ <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
+ android:src="@drawable/stat_happy"/>
+</LinearLayout>
diff --git a/tests/FrameworkPerf/res/layout/large_layout.xml b/tests/FrameworkPerf/res/layout/large_layout.xml
index b6ac88c..39bbe34 100644
--- a/tests/FrameworkPerf/res/layout/large_layout.xml
+++ b/tests/FrameworkPerf/res/layout/large_layout.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- 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.
diff --git a/tests/FrameworkPerf/res/layout/small_layout.xml b/tests/FrameworkPerf/res/layout/small_layout.xml
index 9fcbb26..e78a176 100644
--- a/tests/FrameworkPerf/res/layout/small_layout.xml
+++ b/tests/FrameworkPerf/res/layout/small_layout.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2009 The Android Open Source Project
+<!-- 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.
diff --git a/tests/FrameworkPerf/res/layout/view_layout.xml b/tests/FrameworkPerf/res/layout/view_layout.xml
new file mode 100644
index 0000000..0171eef
--- /dev/null
+++ b/tests/FrameworkPerf/res/layout/view_layout.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" >
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+ <View android:layout_width="wrap_content" android:layout_height="wrap_content" />
+</LinearLayout>
diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
index 5e15224..3979902 100644
--- a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
+++ b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java
@@ -20,6 +20,8 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
@@ -29,13 +31,16 @@
import android.os.PowerManager;
import android.os.Process;
import android.os.SystemClock;
+import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.util.Xml;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;
@@ -46,6 +51,9 @@
import java.io.RandomAccessFile;
import java.util.ArrayList;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
/**
* So you thought sync used up your battery life.
*/
@@ -57,8 +65,10 @@
Spinner mFgSpinner;
Spinner mBgSpinner;
- TextView mLog;
TextView mTestTime;
+ Button mStartButton;
+ Button mStopButton;
+ TextView mLog;
PowerManager.WakeLock mPartialWakeLock;
long mMaxRunTime = 5000;
@@ -100,12 +110,21 @@
new WriteFileOp(), new ReadFileOp(),
new ReadFileOp(), new WriteFileOp(),
new ReadFileOp(), new ReadFileOp(),
+ new OpenXmlResOp(), new NoOp(),
+ new ReadXmlAttrsOp(), new NoOp(),
new ParseXmlResOp(), new NoOp(),
new ParseLargeXmlResOp(), new NoOp(),
new LayoutInflaterOp(), new NoOp(),
new LayoutInflaterLargeOp(), new NoOp(),
+ new LayoutInflaterViewOp(), new NoOp(),
+ new LayoutInflaterButtonOp(), new NoOp(),
+ new LayoutInflaterImageButtonOp(), new NoOp(),
+ new CreateBitmapOp(), new NoOp(),
+ new CreateRecycleBitmapOp(), new NoOp(),
new LoadSmallBitmapOp(), new NoOp(),
+ new LoadRecycleSmallBitmapOp(), new NoOp(),
new LoadLargeBitmapOp(), new NoOp(),
+ new LoadRecycleLargeBitmapOp(), new NoOp(),
new LoadSmallScaledBitmapOp(), new NoOp(),
new LoadLargeScaledBitmapOp(), new NoOp(),
};
@@ -122,12 +141,21 @@
new CreateWriteSyncFileOp(),
new WriteFileOp(),
new ReadFileOp(),
+ new OpenXmlResOp(),
+ new ReadXmlAttrsOp(),
new ParseXmlResOp(),
new ParseLargeXmlResOp(),
new LayoutInflaterOp(),
new LayoutInflaterLargeOp(),
+ new LayoutInflaterViewOp(),
+ new LayoutInflaterButtonOp(),
+ new LayoutInflaterImageButtonOp(),
+ new CreateBitmapOp(),
+ new CreateRecycleBitmapOp(),
new LoadSmallBitmapOp(),
+ new LoadRecycleSmallBitmapOp(),
new LoadLargeBitmapOp(),
+ new LoadRecycleLargeBitmapOp(),
new LoadSmallScaledBitmapOp(),
new LoadLargeScaledBitmapOp(),
};
@@ -208,17 +236,22 @@
mBgSpinner.setAdapter(adapter);
mBgSpinner.setOnItemSelectedListener(this);
- findViewById(R.id.start).setOnClickListener(new View.OnClickListener() {
+ mTestTime = (TextView)findViewById(R.id.testtime);
+
+ mStartButton = (Button)findViewById(R.id.start);
+ mStartButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
startRunning();
}
});
- findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() {
+ mStopButton = (Button)findViewById(R.id.stop);
+ mStopButton.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
stopRunning();
}
});
- mTestTime = (TextView)findViewById(R.id.testtime);
+ mStopButton.setEnabled(false);
+
mLog = (TextView)findViewById(R.id.log);
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
@@ -267,9 +300,17 @@
fgOp = mFgTest;
bgOp = mBgTest;
} else if (mFgTest != null) {
+ // Skip null test.
+ if (mCurOpIndex == 0) {
+ mCurOpIndex = 1;
+ }
fgOp = mFgTest;
bgOp = mAvailOps[mCurOpIndex];
} else {
+ // Skip null test.
+ if (mCurOpIndex == 0) {
+ mCurOpIndex = 1;
+ }
fgOp = mAvailOps[mCurOpIndex];
bgOp = mBgTest;
}
@@ -297,12 +338,13 @@
stopRunning();
return;
}
- }
- mCurOpIndex++;
- if (mCurOpIndex >= mAvailOps.length) {
- log("Finished");
- stopRunning();
- return;
+ } else {
+ mCurOpIndex++;
+ if (mCurOpIndex >= mAvailOps.length) {
+ log("Finished");
+ stopRunning();
+ return;
+ }
}
startCurOp();
}
@@ -313,6 +355,11 @@
if (!mStarted) {
log("Start");
mStarted = true;
+ mStartButton.setEnabled(false);
+ mStopButton.setEnabled(true);
+ mTestTime.setEnabled(false);
+ mFgSpinner.setEnabled(false);
+ mBgSpinner.setEnabled(false);
updateWakeLock();
startService(new Intent(this, SchedulerService.class));
mCurOpIndex = 0;
@@ -325,6 +372,11 @@
void stopRunning() {
if (mStarted) {
mStarted = false;
+ mStartButton.setEnabled(true);
+ mStopButton.setEnabled(false);
+ mTestTime.setEnabled(true);
+ mFgSpinner.setEnabled(true);
+ mBgSpinner.setEnabled(true);
updateWakeLock();
stopService(new Intent(this, SchedulerService.class));
for (int i=0; i<mResults.size(); i++) {
@@ -333,7 +385,7 @@
float bgMsPerOp = result.getBgMsPerOp();
String fgMsPerOpStr = fgMsPerOp != 0 ? Float.toString(fgMsPerOp) : "";
String bgMsPerOpStr = bgMsPerOp != 0 ? Float.toString(bgMsPerOp) : "";
- Log.i(TAG, "\t" + result.name + "\t" + result.fgOps
+ Log.i("PerfRes", "\t" + result.name + "\t" + result.fgOps
+ "\t" + result.getFgMsPerOp() + "\t" + result.fgTime
+ "\t" + result.fgLongName + "\t" + result.bgOps
+ "\t" + result.getBgMsPerOp() + "\t" + result.bgTime
@@ -662,6 +714,71 @@
}
}
+ static class OpenXmlResOp extends Op {
+ Context mContext;
+
+ OpenXmlResOp() {
+ super("OpenXmlRes", "Open (and close) an XML resource");
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mContext = context;
+ }
+
+ boolean onRun() {
+ XmlResourceParser parser = mContext.getResources().getLayout(R.xml.simple);
+ parser.close();
+ return true;
+ }
+ }
+
+ static class ReadXmlAttrsOp extends Op {
+ Context mContext;
+ XmlResourceParser mParser;
+ AttributeSet mAttrs;
+
+ ReadXmlAttrsOp() {
+ super("ReadXmlAttrs", "Read attributes from an XML tag");
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mContext = context;
+ mParser = mContext.getResources().getLayout(R.xml.simple);
+ mAttrs = Xml.asAttributeSet(mParser);
+
+ int eventType;
+ try {
+ // Find the first <item> tag.
+ eventType = mParser.getEventType();
+ String tagName;
+ do {
+ if (eventType == XmlPullParser.START_TAG) {
+ tagName = mParser.getName();
+ if (tagName.equals("item")) {
+ break;
+ }
+ }
+ eventType = mParser.next();
+ } while (eventType != XmlPullParser.END_DOCUMENT);
+ } catch (XmlPullParserException e) {
+ throw new RuntimeException("I died", e);
+ } catch (IOException e) {
+ throw new RuntimeException("I died", e);
+ }
+ }
+
+ void onTerm(Context context) {
+ mParser.close();
+ }
+
+ boolean onRun() {
+ TypedArray a = mContext.obtainStyledAttributes(mAttrs,
+ com.android.internal.R.styleable.MenuItem);
+ a.recycle();
+ return true;
+ }
+ }
+
static class ParseXmlResOp extends Op {
Context mContext;
@@ -702,7 +819,7 @@
Context mContext;
LayoutInflaterOp() {
- super("LayoutInflaterOp", "Inflate layout resource");
+ super("LayoutInflater", "Inflate layout resource");
}
void onInit(Context context, boolean foreground) {
@@ -724,7 +841,7 @@
Context mContext;
LayoutInflaterLargeOp() {
- super("LayoutInflaterLargeOp", "Inflate large layout resource");
+ super("LayoutInflaterLarge", "Inflate large layout resource");
}
void onInit(Context context, boolean foreground) {
@@ -742,6 +859,111 @@
}
}
+ static class LayoutInflaterViewOp extends Op {
+ Context mContext;
+
+ LayoutInflaterViewOp() {
+ super("LayoutInflaterView", "Inflate layout with 50 View objects");
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mContext = context;
+ }
+
+ boolean onRun() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ LayoutInflater inf = (LayoutInflater)mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inf.inflate(R.layout.view_layout, null);
+ return true;
+ }
+ }
+
+ static class LayoutInflaterButtonOp extends Op {
+ Context mContext;
+
+ LayoutInflaterButtonOp() {
+ super("LayoutInflaterButton", "Inflate layout with 50 Button objects");
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mContext = context;
+ }
+
+ boolean onRun() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ LayoutInflater inf = (LayoutInflater)mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inf.inflate(R.layout.button_layout, null);
+ return true;
+ }
+ }
+
+ static class LayoutInflaterImageButtonOp extends Op {
+ Context mContext;
+
+ LayoutInflaterImageButtonOp() {
+ super("LayoutInflaterImageButton", "Inflate layout with 50 ImageButton objects");
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mContext = context;
+ }
+
+ boolean onRun() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ LayoutInflater inf = (LayoutInflater)mContext.getSystemService(
+ Context.LAYOUT_INFLATER_SERVICE);
+ inf.inflate(R.layout.image_button_layout, null);
+ return true;
+ }
+ }
+
+ static class CreateBitmapOp extends Op {
+ Context mContext;
+
+ CreateBitmapOp() {
+ super("CreateBitmap", "Create a Bitmap");
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mContext = context;
+ }
+
+ boolean onRun() {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+ Bitmap bm = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
+ return true;
+ }
+ }
+
+ static class CreateRecycleBitmapOp extends Op {
+ Context mContext;
+
+ CreateRecycleBitmapOp() {
+ super("CreateRecycleBitmap", "Create and recycle a Bitmap");
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mContext = context;
+ }
+
+ boolean onRun() {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+ Bitmap bm = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
+ bm.recycle();
+ return true;
+ }
+ }
+
static class LoadSmallBitmapOp extends Op {
Context mContext;
@@ -758,6 +980,26 @@
opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.stat_sample, opts);
+ return true;
+ }
+ }
+
+ static class LoadRecycleSmallBitmapOp extends Op {
+ Context mContext;
+
+ LoadRecycleSmallBitmapOp() {
+ super("LoadRecycleSmallBitmap", "Load and recycle small raw bitmap");
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mContext = context;
+ }
+
+ boolean onRun() {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+ Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
+ R.drawable.stat_sample, opts);
bm.recycle();
return true;
}
@@ -779,6 +1021,26 @@
opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.wallpaper_goldengate, opts);
+ return true;
+ }
+ }
+
+ static class LoadRecycleLargeBitmapOp extends Op {
+ Context mContext;
+
+ LoadRecycleLargeBitmapOp() {
+ super("LoadRecycleLargeBitmap", "Load and recycle large raw bitmap");
+ }
+
+ void onInit(Context context, boolean foreground) {
+ mContext = context;
+ }
+
+ boolean onRun() {
+ BitmapFactory.Options opts = new BitmapFactory.Options();
+ opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
+ Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
+ R.drawable.wallpaper_goldengate, opts);
bm.recycle();
return true;
}
@@ -800,7 +1062,6 @@
opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.stat_sample_scale, opts);
- bm.recycle();
return true;
}
}
@@ -821,7 +1082,6 @@
opts.inScreenDensity = DisplayMetrics.DENSITY_DEVICE;
Bitmap bm = BitmapFactory.decodeResource(mContext.getResources(),
R.drawable.wallpaper_goldengate_scale, opts);
- bm.recycle();
return true;
}
}
diff --git a/tools/aidl/aidl_language_y.y b/tools/aidl/aidl_language_y.y
index cc04d15..3c16e15 100644
--- a/tools/aidl/aidl_language_y.y
+++ b/tools/aidl/aidl_language_y.y
@@ -87,7 +87,7 @@
b->name = $2.buffer;
b->package = g_currentPackage ? strdup(g_currentPackage) : NULL;
b->semicolon_token = $3.buffer;
- b->flattening_methods = PARCELABLE_DATA;
+ b->flattening_methods = PARCELABLE_DATA | RPC_DATA;
$$.user_data = b;
}
| PARCELABLE ';' {
diff --git a/tools/aidl/generate_java_rpc.cpp b/tools/aidl/generate_java_rpc.cpp
index e4867e4..852b0c1 100644
--- a/tools/aidl/generate_java_rpc.cpp
+++ b/tools/aidl/generate_java_rpc.cpp
@@ -5,7 +5,7 @@
#include <stdlib.h>
#include <string.h>
-Type* SERVICE_CONTEXT_TYPE = new Type("android.content",
+Type* ANDROID_CONTEXT_TYPE = new Type("android.content",
"Context", Type::BUILT_IN, false, false, false);
Type* PRESENTER_BASE_TYPE = new Type("com.android.athome.connector",
"EventListener", Type::BUILT_IN, false, false, false);
@@ -13,8 +13,6 @@
"EventListener.Listener", Type::BUILT_IN, false, false, false);
Type* RPC_BROKER_TYPE = new Type("com.android.athome.connector", "Broker",
Type::BUILT_IN, false, false, false);
-Type* RPC_CONTAINER_TYPE = new Type("com.android.athome.connector", "ConnectorContainer",
- Type::BUILT_IN, false, false, false);
// TODO: Just use Endpoint, so this works for all endpoints.
Type* RPC_CONNECTOR_TYPE = new Type("com.android.athome.connector", "Connector",
Type::BUILT_IN, false, false, false);
@@ -458,7 +456,7 @@
void
EndpointBaseClass::generate_ctor()
{
- Variable* container = new Variable(RPC_CONTAINER_TYPE, "container");
+ Variable* container = new Variable(ANDROID_CONTEXT_TYPE, "context");
Variable* broker = new Variable(RPC_BROKER_TYPE, "broker");
Method* ctor = new Method;
ctor->modifiers = PUBLIC;