Merge "LayoutLib: Delegate for Display and IWindowManager implementation."
diff --git a/api/current.txt b/api/current.txt
index 46cf2af..f3dda785 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -16007,6 +16007,7 @@
protected static abstract interface ContactsContract.GroupsColumns {
field public static final java.lang.String AUTO_ADD = "auto_add";
+ field public static final java.lang.String DATA_SET = "data_set";
field public static final java.lang.String DELETED = "deleted";
field public static final java.lang.String FAVORITES = "favorites";
field public static final java.lang.String GROUP_IS_READ_ONLY = "group_is_read_only";
@@ -16134,6 +16135,7 @@
protected static abstract interface ContactsContract.RawContactsColumns {
field public static final java.lang.String AGGREGATION_MODE = "aggregation_mode";
field public static final java.lang.String CONTACT_ID = "contact_id";
+ field public static final java.lang.String DATA_SET = "data_set";
field public static final java.lang.String DELETED = "deleted";
field public static final java.lang.String RAW_CONTACT_IS_READ_ONLY = "raw_contact_is_read_only";
field public static final java.lang.String RAW_CONTACT_IS_USER_PROFILE = "raw_contact_is_user_profile";
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 479b70a..3fb1736 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -157,6 +157,11 @@
String value = nextArgRequired();
intent.putExtra(key, Integer.valueOf(value));
hasIntentInfo = true;
+ } else if (opt.equals("--eu")) {
+ String key = nextArgRequired();
+ String value = nextArgRequired();
+ intent.putExtra(key, Uri.parse(value));
+ hasIntentInfo = true;
} else if (opt.equals("--eia")) {
String key = nextArgRequired();
String value = nextArgRequired();
@@ -1119,6 +1124,7 @@
" [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" +
" [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" +
" [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" +
+ " [--eu <EXTRA_KEY> <EXTRA_URI_VALUE> ...]\n" +
" [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" +
" [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" +
" [-n <COMPONENT>] [-f <FLAGS>]\n" +
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index d77dbdc..e6c2a0f 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -87,11 +87,13 @@
private AnimatorSetListener mSetListener = null;
/**
- * Flag indicating that the AnimatorSet has been canceled (by calling cancel() or end()).
+ * Flag indicating that the AnimatorSet has been manually
+ * terminated (by calling cancel() or end()).
* This flag is used to avoid starting other animations when currently-playing
- * child animations of this AnimatorSet end.
+ * child animations of this AnimatorSet end. It also determines whether cancel/end
+ * notifications are sent out via the normal AnimatorSetListener mechanism.
*/
- boolean mCanceled = false;
+ boolean mTerminated = false;
// The amount of time in ms to delay starting the animation after start() is called
private long mStartDelay = 0;
@@ -271,31 +273,29 @@
@SuppressWarnings("unchecked")
@Override
public void cancel() {
- mCanceled = true;
- if (mListeners != null) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- for (AnimatorListener listener : tmpListeners) {
- listener.onAnimationCancel(this);
- }
- }
- if (mDelayAnim != null && mDelayAnim.isRunning()) {
- // If we're currently in the startDelay period, just cancel that animator and
- // send out the end event to all listeners
- mDelayAnim.cancel();
+ mTerminated = true;
+ if (isRunning()) {
+ ArrayList<AnimatorListener> tmpListeners = null;
if (mListeners != null) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
+ tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone();
+ for (AnimatorListener listener : tmpListeners) {
+ listener.onAnimationCancel(this);
+ }
+ }
+ if (mDelayAnim != null && mDelayAnim.isRunning()) {
+ // If we're currently in the startDelay period, just cancel that animator and
+ // send out the end event to all listeners
+ mDelayAnim.cancel();
+ } else if (mSortedNodes.size() > 0) {
+ for (Node node : mSortedNodes) {
+ node.animation.cancel();
+ }
+ }
+ if (tmpListeners != null) {
for (AnimatorListener listener : tmpListeners) {
listener.onAnimationEnd(this);
}
}
- return;
- }
- if (mSortedNodes.size() > 0) {
- for (Node node : mSortedNodes) {
- node.animation.cancel();
- }
}
}
@@ -307,23 +307,32 @@
*/
@Override
public void end() {
- mCanceled = true;
- if (mSortedNodes.size() != mNodes.size()) {
- // hasn't been started yet - sort the nodes now, then end them
- sortNodes();
- for (Node node : mSortedNodes) {
- if (mSetListener == null) {
- mSetListener = new AnimatorSetListener(this);
+ mTerminated = true;
+ if (isRunning()) {
+ if (mSortedNodes.size() != mNodes.size()) {
+ // hasn't been started yet - sort the nodes now, then end them
+ sortNodes();
+ for (Node node : mSortedNodes) {
+ if (mSetListener == null) {
+ mSetListener = new AnimatorSetListener(this);
+ }
+ node.animation.addListener(mSetListener);
}
- node.animation.addListener(mSetListener);
}
- }
- if (mDelayAnim != null) {
- mDelayAnim.cancel();
- }
- if (mSortedNodes.size() > 0) {
- for (Node node : mSortedNodes) {
- node.animation.end();
+ if (mDelayAnim != null) {
+ mDelayAnim.cancel();
+ }
+ if (mSortedNodes.size() > 0) {
+ for (Node node : mSortedNodes) {
+ node.animation.end();
+ }
+ }
+ if (mListeners != null) {
+ ArrayList<AnimatorListener> tmpListeners =
+ (ArrayList<AnimatorListener>) mListeners.clone();
+ for (AnimatorListener listener : tmpListeners) {
+ listener.onAnimationEnd(this);
+ }
}
}
}
@@ -424,7 +433,7 @@
@SuppressWarnings("unchecked")
@Override
public void start() {
- mCanceled = false;
+ mTerminated = false;
// First, sort the nodes (if necessary). This will ensure that sortedNodes
// contains the animation nodes in the correct order.
@@ -437,7 +446,8 @@
ArrayList<AnimatorListener> oldListeners = node.animation.getListeners();
if (oldListeners != null && oldListeners.size() > 0) {
for (AnimatorListener listener : oldListeners) {
- if (listener instanceof DependencyListener) {
+ if (listener instanceof DependencyListener ||
+ listener instanceof AnimatorSetListener) {
node.animation.removeListener(listener);
}
}
@@ -522,7 +532,7 @@
* and will populate any appropriate lists, when it is started.
*/
anim.mNeedsSort = true;
- anim.mCanceled = false;
+ anim.mTerminated = false;
anim.mPlayingSet = new ArrayList<Animator>();
anim.mNodeMap = new HashMap<Animator, Node>();
anim.mNodes = new ArrayList<Node>();
@@ -640,7 +650,7 @@
* @param dependencyAnimation the animation that sent the event.
*/
private void startIfReady(Animator dependencyAnimation) {
- if (mAnimatorSet.mCanceled) {
+ if (mAnimatorSet.mTerminated) {
// if the parent AnimatorSet was canceled, then don't start any dependent anims
return;
}
@@ -676,11 +686,15 @@
}
public void onAnimationCancel(Animator animation) {
- if (mPlayingSet.size() == 0) {
- if (mListeners != null) {
- int numListeners = mListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- mListeners.get(i).onAnimationCancel(mAnimatorSet);
+ if (!mTerminated) {
+ // Listeners are already notified of the AnimatorSet canceling in cancel().
+ // The logic below only kicks in when animations end normally
+ if (mPlayingSet.size() == 0) {
+ if (mListeners != null) {
+ int numListeners = mListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ mListeners.get(i).onAnimationCancel(mAnimatorSet);
+ }
}
}
}
@@ -692,24 +706,28 @@
mPlayingSet.remove(animation);
Node animNode = mAnimatorSet.mNodeMap.get(animation);
animNode.done = true;
- ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
- boolean allDone = true;
- int numSortedNodes = sortedNodes.size();
- for (int i = 0; i < numSortedNodes; ++i) {
- if (!sortedNodes.get(i).done) {
- allDone = false;
- break;
+ if (!mTerminated) {
+ // Listeners are already notified of the AnimatorSet ending in cancel() or
+ // end(); the logic below only kicks in when animations end normally
+ ArrayList<Node> sortedNodes = mAnimatorSet.mSortedNodes;
+ boolean allDone = true;
+ int numSortedNodes = sortedNodes.size();
+ for (int i = 0; i < numSortedNodes; ++i) {
+ if (!sortedNodes.get(i).done) {
+ allDone = false;
+ break;
+ }
}
- }
- if (allDone) {
- // If this was the last child animation to end, then notify listeners that this
- // AnimatorSet has ended
- if (mListeners != null) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- int numListeners = tmpListeners.size();
- for (int i = 0; i < numListeners; ++i) {
- tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
+ if (allDone) {
+ // If this was the last child animation to end, then notify listeners that this
+ // AnimatorSet has ended
+ if (mListeners != null) {
+ ArrayList<AnimatorListener> tmpListeners =
+ (ArrayList<AnimatorListener>) mListeners.clone();
+ int numListeners = tmpListeners.size();
+ for (int i = 0; i < numListeners; ++i) {
+ tmpListeners.get(i).onAnimationEnd(mAnimatorSet);
+ }
}
}
}
@@ -791,6 +809,8 @@
}
}
}
+ // nodes are 'done' by default; they become un-done when started, and done
+ // again when ended
node.done = false;
}
}
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 1dcaa04..90d676e 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -933,17 +933,17 @@
@Override
public void cancel() {
- if (mListeners != null) {
- ArrayList<AnimatorListener> tmpListeners =
- (ArrayList<AnimatorListener>) mListeners.clone();
- for (AnimatorListener listener : tmpListeners) {
- listener.onAnimationCancel(this);
- }
- }
// Only cancel if the animation is actually running or has been started and is about
// to run
if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
sDelayedAnims.get().contains(this)) {
+ if (mListeners != null) {
+ ArrayList<AnimatorListener> tmpListeners =
+ (ArrayList<AnimatorListener>) mListeners.clone();
+ for (AnimatorListener listener : tmpListeners) {
+ listener.onAnimationCancel(this);
+ }
+ }
endAnimation();
}
}
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index a6658cc..d207a0a 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -1471,6 +1471,24 @@
}
/**
+ * Returns the usage statistics of each installed package.
+ *
+ * @hide
+ */
+ public PkgUsageStats[] getAllPackageUsageStats() {
+ try {
+ IUsageStats usageStatsService = IUsageStats.Stub.asInterface(
+ ServiceManager.getService("usagestats"));
+ if (usageStatsService != null) {
+ return usageStatsService.getAllPkgUsageStats();
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "Could not query usage stats", e);
+ }
+ return new PkgUsageStats[0];
+ }
+
+ /**
* @param userid the user's id. Zero indicates the default user
* @hide
*/
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 9d40c42..a4c66e4 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -43,17 +43,41 @@
/** {@link #tag} value for without tag. */
public static final int TAG_NONE = 0;
+ // TODO: move public fields to Entry accessors, then undeprecate
+ // TODO: refactor rx/tx to rxBytes/txBytes
+
/**
* {@link SystemClock#elapsedRealtime()} timestamp when this data was
* generated.
*/
+ @Deprecated
public final long elapsedRealtime;
+ @Deprecated
public int size;
+ @Deprecated
public String[] iface;
+ @Deprecated
public int[] uid;
+ @Deprecated
public int[] tag;
+ @Deprecated
public long[] rx;
+ @Deprecated
+ public long[] rxPackets;
+ @Deprecated
public long[] tx;
+ @Deprecated
+ public long[] txPackets;
+
+ public static class Entry {
+ public String iface;
+ public int uid;
+ public int tag;
+ public long rxBytes;
+ public long rxPackets;
+ public long txBytes;
+ public long txPackets;
+ }
public NetworkStats(long elapsedRealtime, int initialSize) {
this.elapsedRealtime = elapsedRealtime;
@@ -62,7 +86,9 @@
this.uid = new int[initialSize];
this.tag = new int[initialSize];
this.rx = new long[initialSize];
+ this.rxPackets = new long[initialSize];
this.tx = new long[initialSize];
+ this.txPackets = new long[initialSize];
}
public NetworkStats(Parcel parcel) {
@@ -72,38 +98,82 @@
uid = parcel.createIntArray();
tag = parcel.createIntArray();
rx = parcel.createLongArray();
+ rxPackets = parcel.createLongArray();
tx = parcel.createLongArray();
+ txPackets = parcel.createLongArray();
}
/**
* Add new stats entry with given values.
*/
public NetworkStats addEntry(String iface, int uid, int tag, long rx, long tx) {
+ final Entry entry = new Entry();
+ entry.iface = iface;
+ entry.uid = uid;
+ entry.tag = tag;
+ entry.rxBytes = rx;
+ entry.txBytes = tx;
+ return addValues(entry);
+ }
+
+ /**
+ * Add new stats entry, copying from given {@link Entry}. The {@link Entry}
+ * object can be recycled across multiple calls.
+ */
+ public NetworkStats addValues(Entry entry) {
if (size >= this.iface.length) {
- final int newLength = Math.max(this.iface.length, 10) * 3 / 2;
- this.iface = Arrays.copyOf(this.iface, newLength);
- this.uid = Arrays.copyOf(this.uid, newLength);
- this.tag = Arrays.copyOf(this.tag, newLength);
- this.rx = Arrays.copyOf(this.rx, newLength);
- this.tx = Arrays.copyOf(this.tx, newLength);
+ final int newLength = Math.max(iface.length, 10) * 3 / 2;
+ iface = Arrays.copyOf(iface, newLength);
+ uid = Arrays.copyOf(uid, newLength);
+ tag = Arrays.copyOf(tag, newLength);
+ rx = Arrays.copyOf(rx, newLength);
+ rxPackets = Arrays.copyOf(rxPackets, newLength);
+ tx = Arrays.copyOf(tx, newLength);
+ txPackets = Arrays.copyOf(txPackets, newLength);
}
- this.iface[size] = iface;
- this.uid[size] = uid;
- this.tag[size] = tag;
- this.rx[size] = rx;
- this.tx[size] = tx;
+ iface[size] = entry.iface;
+ uid[size] = entry.uid;
+ tag[size] = entry.tag;
+ rx[size] = entry.rxBytes;
+ rxPackets[size] = entry.rxPackets;
+ tx[size] = entry.txBytes;
+ txPackets[size] = entry.txPackets;
size++;
return this;
}
/**
+ * Return specific stats entry.
+ */
+ public Entry getValues(int i, Entry recycle) {
+ final Entry entry = recycle != null ? recycle : new Entry();
+ entry.iface = iface[i];
+ entry.uid = uid[i];
+ entry.tag = tag[i];
+ entry.rxBytes = rx[i];
+ entry.rxPackets = rxPackets[i];
+ entry.txBytes = tx[i];
+ entry.txPackets = txPackets[i];
+ return entry;
+ }
+
+ public long getElapsedRealtime() {
+ return elapsedRealtime;
+ }
+
+ public int size() {
+ return size;
+ }
+
+ /**
* Combine given values with an existing row, or create a new row if
* {@link #findIndex(String, int, int)} is unable to find match. Can also be
* used to subtract values from existing rows.
*/
public NetworkStats combineEntry(String iface, int uid, int tag, long rx, long tx) {
+ // TODO: extent to accept rxPackets/txPackets
final int i = findIndex(iface, uid, tag);
if (i == -1) {
// only create new entry when positive contribution
@@ -199,30 +269,41 @@
}
// result will have our rows, and elapsed time between snapshots
+ final Entry entry = new Entry();
final NetworkStats result = new NetworkStats(deltaRealtime, size);
for (int i = 0; i < size; i++) {
- final String iface = this.iface[i];
- final int uid = this.uid[i];
- final int tag = this.tag[i];
+ entry.iface = iface[i];
+ entry.uid = uid[i];
+ entry.tag = tag[i];
// find remote row that matches, and subtract
- final int j = value.findIndex(iface, uid, tag);
+ final int j = value.findIndex(entry.iface, entry.uid, entry.tag);
if (j == -1) {
// newly appearing row, return entire value
- result.addEntry(iface, uid, tag, this.rx[i], this.tx[i]);
+ entry.rxBytes = rx[i];
+ entry.rxPackets = rxPackets[i];
+ entry.txBytes = tx[i];
+ entry.txPackets = txPackets[i];
} else {
// existing row, subtract remote value
- long rx = this.rx[i] - value.rx[j];
- long tx = this.tx[i] - value.tx[j];
- if (enforceMonotonic && (rx < 0 || tx < 0)) {
+ entry.rxBytes = rx[i] - value.rx[j];
+ entry.rxPackets = rxPackets[i] - value.rxPackets[j];
+ entry.txBytes = tx[i] - value.tx[j];
+ entry.txPackets = txPackets[i] - value.txPackets[j];
+ if (enforceMonotonic
+ && (entry.rxBytes < 0 || entry.rxPackets < 0 || entry.txBytes < 0
+ || entry.txPackets < 0)) {
throw new IllegalArgumentException("found non-monotonic values");
}
if (clampNegative) {
- rx = Math.max(0, rx);
- tx = Math.max(0, tx);
+ 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);
}
- result.addEntry(iface, uid, tag, rx, tx);
}
+
+ result.addValues(entry);
}
return result;
@@ -235,13 +316,15 @@
public void dump(String prefix, PrintWriter pw) {
pw.print(prefix);
pw.print("NetworkStats: elapsedRealtime="); pw.println(elapsedRealtime);
- for (int i = 0; i < iface.length; i++) {
+ for (int i = 0; i < size; i++) {
pw.print(prefix);
pw.print(" iface="); pw.print(iface[i]);
pw.print(" uid="); pw.print(uid[i]);
pw.print(" tag="); pw.print(tag[i]);
- pw.print(" rx="); pw.print(rx[i]);
- pw.print(" tx="); pw.println(tx[i]);
+ pw.print(" rxBytes="); pw.print(rx[i]);
+ pw.print(" rxPackets="); pw.print(rxPackets[i]);
+ pw.print(" txBytes="); pw.print(tx[i]);
+ pw.print(" txPackets="); pw.println(txPackets[i]);
}
}
@@ -265,7 +348,9 @@
dest.writeIntArray(uid);
dest.writeIntArray(tag);
dest.writeLongArray(rx);
+ dest.writeLongArray(rxPackets);
dest.writeLongArray(tx);
+ dest.writeLongArray(txPackets);
}
public static final Creator<NetworkStats> CREATOR = new Creator<NetworkStats>() {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 269e50e..5b1f563 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -270,13 +270,12 @@
* @param targetSdkVersion The target SDK version for the app.
* @param zygoteArgs Additional arguments to supply to the zygote process.
*
- * @return int If > 0 the pid of the new process; if 0 the process is
- * being emulated by a thread
+ * @return An object that describes the result of the attempt to start the process.
* @throws RuntimeException on fatal start failure
*
* {@hide}
*/
- public static final int start(final String processClass,
+ public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int debugFlags, int targetSdkVersion,
@@ -376,14 +375,11 @@
* and returns the child's pid. Please note: the present implementation
* replaces newlines in the argument list with spaces.
* @param args argument list
- * @return PID of new child process
+ * @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
- private static int zygoteSendArgsAndGetPid(ArrayList<String> args)
+ private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList<String> args)
throws ZygoteStartFailedEx {
-
- int pid;
-
openZygoteSocketIfNeeded();
try {
@@ -394,7 +390,8 @@
* b) a number of newline-separated argument strings equal to count
*
* After the zygote process reads these it will write the pid of
- * the child or -1 on failure.
+ * the child or -1 on failure, followed by boolean to
+ * indicate whether a wrapper process was used.
*/
sZygoteWriter.write(Integer.toString(args.size()));
@@ -414,11 +411,13 @@
sZygoteWriter.flush();
// Should there be a timeout on this?
- pid = sZygoteInputStream.readInt();
-
- if (pid < 0) {
+ ProcessStartResult result = new ProcessStartResult();
+ result.pid = sZygoteInputStream.readInt();
+ if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
+ result.usingWrapper = sZygoteInputStream.readBoolean();
+ return result;
} catch (IOException ex) {
try {
if (sZygoteSocket != null) {
@@ -433,8 +432,6 @@
throw new ZygoteStartFailedEx(ex);
}
-
- return pid;
}
/**
@@ -449,18 +446,16 @@
* @param debugFlags Additional flags.
* @param targetSdkVersion The target SDK version for the app.
* @param extraArgs Additional arguments to supply to the zygote process.
- * @return PID
+ * @return An object that describes the result of the attempt to start the process.
* @throws ZygoteStartFailedEx if process start failed for any reason
*/
- private static int startViaZygote(final String processClass,
+ private static ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int debugFlags, int targetSdkVersion,
String[] extraArgs)
throws ZygoteStartFailedEx {
- int pid;
-
synchronized(Process.class) {
ArrayList<String> argsForZygote = new ArrayList<String>();
@@ -516,15 +511,9 @@
argsForZygote.add(arg);
}
}
-
- pid = zygoteSendArgsAndGetPid(argsForZygote);
- }
- if (pid <= 0) {
- throw new ZygoteStartFailedEx("zygote start failed:" + pid);
+ return zygoteSendArgsAndGetResult(argsForZygote);
}
-
- return pid;
}
/**
@@ -808,4 +797,21 @@
* @hide
*/
public static final native long getPss(int pid);
+
+ /**
+ * Specifies the outcome of having started a process.
+ * @hide
+ */
+ public static final class ProcessStartResult {
+ /**
+ * The PID of the newly started process.
+ * Always >= 0. (If the start failed, an exception will have been thrown instead.)
+ */
+ public int pid;
+
+ /**
+ * True if the process was started with a wrapper attached.
+ */
+ public boolean usingWrapper;
+ }
}
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 61deea4..ec67683 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -135,14 +135,6 @@
public static final String ALLOW_PROFILE = "allow_profile";
/**
- * A query parameter key used to specify the package that is requesting a query.
- * This is used for restricting data based on package name.
- *
- * @hide
- */
- public static final String REQUESTING_PACKAGE_PARAM_KEY = "requesting_package";
-
- /**
* Query parameter that should be used by the client to access a specific
* {@link Directory}. The parameter value should be the _ID of the corresponding
* directory, e.g.
@@ -271,8 +263,6 @@
* <li>The URI authority is replaced with the corresponding {@link #DIRECTORY_AUTHORITY}.</li>
* <li>The {@code accountName=} and {@code accountType=} parameters are added or
* replaced using the corresponding {@link #ACCOUNT_TYPE} and {@link #ACCOUNT_NAME} values.</li>
- * <li>If the URI is missing a ContactsContract.REQUESTING_PACKAGE_PARAM_KEY
- * parameter, this parameter is added.</li>
* </ul>
* </p>
* <p>
@@ -1881,13 +1871,16 @@
public static final String CONTACT_ID = "contact_id";
/**
- * Flag indicating that this {@link RawContacts} entry and its children have
- * been restricted to specific platform apps.
- * <P>Type: INTEGER (boolean)</P>
+ * The data set within the account that this row belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' data.
*
- * @hide until finalized in future platform release
+ * This is empty by default, and is completely optional. It only needs to
+ * be populated if multiple sync adapters are entering distinct data for
+ * the same account type and account name.
+ * <P>Type: TEXT</P>
*/
- public static final String IS_RESTRICTED = "is_restricted";
+ public static final String DATA_SET = "data_set";
/**
* The aggregation mode for this contact.
@@ -2211,8 +2204,8 @@
* <td>The name of the account instance to which this row belongs, which when paired with
* {@link #ACCOUNT_TYPE} identifies a specific account.
* For example, this will be the Gmail address if it is a Google account.
- * It should be set at the time
- * the raw contact is inserted and never changed afterwards.</td>
+ * It should be set at the time the raw contact is inserted and never
+ * changed afterwards.</td>
* </tr>
* <tr>
* <td>String</td>
@@ -2222,8 +2215,8 @@
* <p>
* The type of account to which this row belongs, which when paired with
* {@link #ACCOUNT_NAME} identifies a specific account.
- * It should be set at the time
- * the raw contact is inserted and never changed afterwards.
+ * It should be set at the time the raw contact is inserted and never
+ * changed afterwards.
* </p>
* <p>
* To ensure uniqueness, new account types should be chosen according to the
@@ -2233,15 +2226,38 @@
* </tr>
* <tr>
* <td>String</td>
+ * <td>{@link #DATA_SET}</td>
+ * <td>read/write-once</td>
+ * <td>
+ * <p>
+ * The data set within the account that this row belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' data. The combination of {@link #ACCOUNT_TYPE},
+ * {@link #ACCOUNT_NAME}, and {@link #DATA_SET} identifies a set of data
+ * that is associated with a single sync adapter.
+ * </p>
+ * <p>
+ * This is empty by default, and is completely optional. It only needs to
+ * be populated if multiple sync adapters are entering distinct data for
+ * the same account type and account name.
+ * </p>
+ * <p>
+ * It should be set at the time the raw contact is inserted and never
+ * changed afterwards.
+ * </p>
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>String</td>
* <td>{@link #SOURCE_ID}</td>
* <td>read/write</td>
* <td>String that uniquely identifies this row to its source account.
* Typically it is set at the time the raw contact is inserted and never
* changed afterwards. The one notable exception is a new raw contact: it
- * will have an account name and type, but no source id. This
- * indicates to the sync adapter that a new contact needs to be created
- * server-side and its ID stored in the corresponding SOURCE_ID field on
- * the phone.
+ * will have an account name and type (and possibly a data set), but no
+ * source id. This indicates to the sync adapter that a new contact needs
+ * to be created server-side and its ID stored in the corresponding
+ * SOURCE_ID field on the phone.
* </td>
* </tr>
* <tr>
@@ -2537,7 +2553,6 @@
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DELETED);
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, CONTACT_ID);
DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, STARRED);
- DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, IS_RESTRICTED);
DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, NAME_VERIFIED);
android.content.Entity contact = new android.content.Entity(cv);
@@ -3814,27 +3829,6 @@
/**
* <p>
- * If {@link #FOR_EXPORT_ONLY} is explicitly set to "1", returned Cursor toward
- * Data.CONTENT_URI contains only exportable data.
- * </p>
- * <p>
- * This flag is useful (currently) only for vCard exporter in Contacts app, which
- * needs to exclude "un-exportable" data from available data to export, while
- * Contacts app itself has priviledge to access all data including "un-exportable"
- * ones and providers return all of them regardless of the callers' intention.
- * </p>
- * <p>
- * Type: INTEGER
- * </p>
- *
- * @hide Maybe available only in Eclair and not really ready for public use.
- * TODO: remove, or implement this feature completely. As of now (Eclair),
- * we only use this flag in queryEntities(), not query().
- */
- public static final String FOR_EXPORT_ONLY = "for_export_only";
-
- /**
- * <p>
* Build a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}
* style {@link Uri} for the parent {@link android.provider.ContactsContract.Contacts}
* entry of the given {@link ContactsContract.Data} entry.
@@ -6213,6 +6207,18 @@
*/
protected interface GroupsColumns {
/**
+ * The data set within the account that this group belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' group data.
+ *
+ * This is empty by default, and is completely optional. It only needs to
+ * be populated if multiple sync adapters are entering distinct group data
+ * for the same account type and account name.
+ * <P>Type: TEXT</P>
+ */
+ public static final String DATA_SET = "data_set";
+
+ /**
* The display title of this group.
* <p>
* Type: TEXT
@@ -6338,6 +6344,29 @@
* In other words, it would be a really bad idea to delete and reinsert a
* group. A sync adapter should always do an update instead.</td>
* </tr>
+ # <tr>
+ * <td>String</td>
+ * <td>{@link #DATA_SET}</td>
+ * <td>read/write-once</td>
+ * <td>
+ * <p>
+ * The data set within the account that this group belongs to. This allows
+ * multiple sync adapters for the same account type to distinguish between
+ * each others' group data. The combination of {@link #ACCOUNT_TYPE},
+ * {@link #ACCOUNT_NAME}, and {@link #DATA_SET} identifies a set of data
+ * that is associated with a single sync adapter.
+ * </p>
+ * <p>
+ * This is empty by default, and is completely optional. It only needs to
+ * be populated if multiple sync adapters are entering distinct data for
+ * the same account type and account name.
+ * </p>
+ * <p>
+ * It should be set at the time the group is inserted and never changed
+ * afterwards.
+ * </p>
+ * </td>
+ * </tr>
* <tr>
* <td>String</td>
* <td>{@link #TITLE}</td>
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 1638c74..011e44c 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -128,10 +128,19 @@
abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException;
/**
- * Invoked whenever the size of the target surface changes. This will
- * not be invoked when the surface is first created.
+ * This method should be invoked whenever the current hardware renderer
+ * context should be reset.
*/
- abstract void preapareSurfaceForResize();
+ abstract void invalidate();
+
+ /**
+ * This method should be invoked to ensure the hardware renderer is in
+ * valid state (for instance, to ensure the correct EGL context is bound
+ * to the current thread.)
+ *
+ * @return true if the renderer is now valid, false otherwise
+ */
+ abstract boolean validate();
/**
* Setup the hardware renderer for drawing. This is called whenever the
@@ -629,11 +638,16 @@
}
@Override
- void preapareSurfaceForResize() {
+ void invalidate() {
// Cancels any existing buffer to ensure we'll get a buffer
// of the right size before we call eglSwapBuffers
sEgl.eglMakeCurrent(sEglDisplay, EGL10.EGL_NO_SURFACE,
- EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+ }
+
+ @Override
+ boolean validate() {
+ return checkCurrent() != SURFACE_STATE_ERROR;
}
@Override
@@ -662,7 +676,7 @@
attachInfo.mDrawingTime = SystemClock.uptimeMillis();
view.mPrivateFlags |= View.DRAWN;
-
+
final int surfaceState = checkCurrent();
if (surfaceState != SURFACE_STATE_ERROR) {
// We had to change the current surface and/or context, redraw everything
@@ -723,10 +737,21 @@
}
}
}
-
+
+ /**
+ * Ensures the currnet EGL context is the one we expect.
+ *
+ * @return {@link #SURFACE_STATE_ERROR} if the correct EGL context cannot be made current,
+ * {@link #SURFACE_STATE_UPDATED} if the EGL context was changed or
+ * {@link #SURFACE_STATE_SUCCESS} if the EGL context was the correct one
+ */
private int checkCurrent() {
- // TODO: Don't check the current context when we have one per UI thread
- // TODO: Use a threadlocal flag to know whether the surface has changed
+ if (sEglThread != Thread.currentThread()) {
+ throw new IllegalStateException("Hardware acceleration can only be used with a " +
+ "single UI thread.\nOriginal thread: " + sEglThread + "\n" +
+ "Current thread: " + Thread.currentThread());
+ }
+
if (!sEglContext.equals(sEgl.eglGetCurrentContext()) ||
!mEglSurface.equals(sEgl.eglGetCurrentSurface(EGL10.EGL_DRAW))) {
if (!sEgl.eglMakeCurrent(sEglDisplay, mEglSurface, mEglSurface, sEglContext)) {
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index b4f323c..1dcbc26 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -901,6 +901,7 @@
!mAttachInfo.mTurnOffWindowResizeAnim &&
mAttachInfo.mHardwareRenderer != null &&
mAttachInfo.mHardwareRenderer.isEnabled() &&
+ mAttachInfo.mHardwareRenderer.validate() &&
lp != null && !PixelFormat.formatHasAlpha(lp.format)) {
disposeResizeBuffer();
@@ -1314,10 +1315,10 @@
if (hwInitialized || ((windowShouldResize || params != null) &&
mAttachInfo.mHardwareRenderer != null &&
mAttachInfo.mHardwareRenderer.isEnabled())) {
- if (!hwInitialized) {
- mAttachInfo.mHardwareRenderer.preapareSurfaceForResize();
- }
mAttachInfo.mHardwareRenderer.setup(mWidth, mHeight);
+ if (!hwInitialized) {
+ mAttachInfo.mHardwareRenderer.invalidate();
+ }
}
if (!mStopped) {
diff --git a/core/java/android/webkit/L10nUtils.java b/core/java/android/webkit/L10nUtils.java
index f9d0067d..a1c6a53 100644
--- a/core/java/android/webkit/L10nUtils.java
+++ b/core/java/android/webkit/L10nUtils.java
@@ -75,7 +75,18 @@
com.android.internal.R.string.autofill_area_code_notext_re, // IDS_AUTOFILL_AREA_CODE_NOTEXT_RE
com.android.internal.R.string.autofill_phone_prefix_separator_re, // IDS_AUTOFILL_PHONE_PREFIX_SEPARATOR_RE
com.android.internal.R.string.autofill_phone_suffix_separator_re, // IDS_AUTOFILL_PHONE_SUFFIX_SEPARATOR_RE
- com.android.internal.R.string.credit_card_number_preview_format // IDS_CREDIT_CARD_NUMBER_PREVIEW_FORMAT
+ com.android.internal.R.string.autofill_province, // IDS_AUTOFILL_DIALOG_PROVINCE
+ com.android.internal.R.string.autofill_postal_code, // IDS_AUTOFILL_DIALOG_POSTAL_CODE
+ com.android.internal.R.string.autofill_state, // IDS_AUTOFILL_DIALOG_STATE
+ com.android.internal.R.string.autofill_zip_code, // IDS_AUTOFILL_DIALOG_ZIP_CODE
+ com.android.internal.R.string.autofill_county, // IDS_AUTOFILL_DIALOG_COUNTY
+ com.android.internal.R.string.autofill_island, // IDS_AUTOFILL_DIALOG_ISLAND
+ com.android.internal.R.string.autofill_district, // IDS_AUTOFILL_DIALOG_DISTRICT
+ com.android.internal.R.string.autofill_department, // IDS_AUTOFILL_DIALOG_DEPARTMENT
+ com.android.internal.R.string.autofill_prefecture, // IDS_AUTOFILL_DIALOG_PREFECTURE
+ com.android.internal.R.string.autofill_parish, // IDS_AUTOFILL_DIALOG_PARISH
+ com.android.internal.R.string.autofill_area, // IDS_AUTOFILL_DIALOG_AREA
+ com.android.internal.R.string.autofill_emirate // IDS_AUTOFILL_DIALOG_EMIRATE
};
private static Context mApplicationContext;
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 7d3f313..49ea944 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -344,6 +344,8 @@
public final void setInitialScaleInPercent(int scaleInPercent) {
mInitialScale = scaleInPercent * 0.01f;
+ mActualScale = mInitialScale > 0 ? mInitialScale : mDefaultScale;
+ mInvActualScale = 1 / mActualScale;
}
public final float computeScaleWithLimits(float scale) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e350ec4..766b520 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6212,6 +6212,15 @@
int scrollx, scrolly;
+ // Convert to left, center, or right alignment.
+ if (a == Layout.Alignment.ALIGN_NORMAL) {
+ a = dir == Layout.DIR_LEFT_TO_RIGHT ? Layout.Alignment.ALIGN_LEFT :
+ Layout.Alignment.ALIGN_RIGHT;
+ } else if (a == Layout.Alignment.ALIGN_OPPOSITE){
+ a = dir == Layout.DIR_LEFT_TO_RIGHT ? Layout.Alignment.ALIGN_RIGHT :
+ Layout.Alignment.ALIGN_LEFT;
+ }
+
if (a == Layout.Alignment.ALIGN_CENTER) {
/*
* Keep centered if possible, or, if it is too wide to fit,
@@ -6230,28 +6239,11 @@
scrollx = left;
}
}
- } else if (a == Layout.Alignment.ALIGN_NORMAL) {
- /*
- * Keep leading edge in view.
- */
-
- if (dir < 0) {
- int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
- scrollx = right - hspace;
- } else {
- scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line));
- }
- } else /* a == Layout.Alignment.ALIGN_OPPOSITE */ {
- /*
- * Keep trailing edge in view.
- */
-
- if (dir < 0) {
- scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line));
- } else {
- int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
- scrollx = right - hspace;
- }
+ } else if (a == Layout.Alignment.ALIGN_LEFT) {
+ scrollx = (int) FloatMath.floor(mLayout.getLineLeft(line));
+ } else { // a == Layout.Alignment.ALIGN_RIGHT
+ int right = (int) FloatMath.ceil(mLayout.getLineRight(line));
+ scrollx = right - hspace;
}
if (ht < vspace) {
@@ -6293,20 +6285,24 @@
int grav;
switch (mLayout.getParagraphAlignment(line)) {
- case ALIGN_NORMAL:
+ case ALIGN_LEFT:
grav = 1;
break;
-
- case ALIGN_OPPOSITE:
+ case ALIGN_RIGHT:
grav = -1;
break;
-
+ case ALIGN_NORMAL:
+ grav = mLayout.getParagraphDirection(line);
+ break;
+ case ALIGN_OPPOSITE:
+ grav = -mLayout.getParagraphDirection(line);
+ break;
+ case ALIGN_CENTER:
default:
grav = 0;
+ break;
}
- grav *= mLayout.getParagraphDirection(line);
-
int hspace = mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight();
int vspace = mBottom - mTop - getExtendedPaddingTop() - getExtendedPaddingBottom();
diff --git a/core/java/com/android/internal/os/PkgUsageStats.java b/core/java/com/android/internal/os/PkgUsageStats.java
index 1ac191b..8c2c405 100755
--- a/core/java/com/android/internal/os/PkgUsageStats.java
+++ b/core/java/com/android/internal/os/PkgUsageStats.java
@@ -19,6 +19,9 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* implementation of PkgUsageStats associated with an
* application package.
@@ -28,6 +31,7 @@
public String packageName;
public int launchCount;
public long usageTime;
+ public Map<String, Long> componentResumeTimes;
public static final Parcelable.Creator<PkgUsageStats> CREATOR
= new Parcelable.Creator<PkgUsageStats>() {
@@ -46,31 +50,45 @@
+ " " + packageName + "}";
}
- public PkgUsageStats(String pkgName, int count, long time) {
+ public PkgUsageStats(String pkgName, int count, long time, Map<String, Long> lastResumeTimes) {
packageName = pkgName;
launchCount = count;
usageTime = time;
+ componentResumeTimes = new HashMap<String, Long>(lastResumeTimes);
}
public PkgUsageStats(Parcel source) {
packageName = source.readString();
launchCount = source.readInt();
usageTime = source.readLong();
+ final int N = source.readInt();
+ componentResumeTimes = new HashMap<String, Long>(N);
+ for (int i = 0; i < N; i++) {
+ String component = source.readString();
+ long lastResumeTime = source.readLong();
+ componentResumeTimes.put(component, lastResumeTime);
+ }
}
public PkgUsageStats(PkgUsageStats pStats) {
packageName = pStats.packageName;
launchCount = pStats.launchCount;
usageTime = pStats.usageTime;
+ componentResumeTimes = new HashMap<String, Long>(pStats.componentResumeTimes);
}
public int describeContents() {
return 0;
}
- public void writeToParcel(Parcel dest, int parcelableFlags){
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(packageName);
dest.writeInt(launchCount);
dest.writeLong(usageTime);
+ dest.writeInt(componentResumeTimes.size());
+ for (Map.Entry<String, Long> ent : componentResumeTimes.entrySet()) {
+ dest.writeString(ent.getKey());
+ dest.writeLong(ent.getValue());
+ }
}
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index a3b7795..9af7e96 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -900,6 +900,7 @@
}
}
+ boolean usingWrapper = false;
if (pipeFd != null && pid > 0) {
DataInputStream is = new DataInputStream(new FileInputStream(pipeFd));
int innerPid = -1;
@@ -924,6 +925,7 @@
if (parentPid > 0) {
Log.i(TAG, "Wrapped process has pid " + innerPid);
pid = innerPid;
+ usingWrapper = true;
} else {
Log.w(TAG, "Wrapped process reported a pid that is not a child of "
+ "the process that we forked: childPid=" + pid
@@ -934,6 +936,7 @@
try {
mSocketOutStream.writeInt(pid);
+ mSocketOutStream.writeBoolean(usingWrapper);
} catch (IOException ex) {
Log.e(TAG, "Error reading from command socket", ex);
return true;
diff --git a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
index b86eb13..9c06d69 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuPresenter.java
@@ -20,7 +20,8 @@
import android.content.Context;
import android.content.res.Resources;
-import android.util.Log;
+import android.os.Parcel;
+import android.os.Parcelable;
import android.util.SparseBooleanArray;
import android.view.MenuItem;
import android.view.SoundEffectConstants;
@@ -60,6 +61,9 @@
private OpenOverflowRunnable mPostedOpenRunnable;
+ final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
+ int mOpenSubMenuId;
+
public ActionMenuPresenter() {
super(com.android.internal.R.layout.action_menu_layout,
com.android.internal.R.layout.action_menu_item_layout);
@@ -196,8 +200,12 @@
topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
}
View anchor = findViewForItem(topSubMenu.getItem());
- if (anchor == null) return false;
+ if (anchor == null) {
+ if (mOverflowButton == null) return false;
+ anchor = mOverflowButton;
+ }
+ mOpenSubMenuId = subMenu.getItem().getItemId();
mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
mActionButtonPopup.setAnchorView(anchor);
mActionButtonPopup.show();
@@ -426,6 +434,57 @@
super.onCloseMenu(menu, allMenusAreClosing);
}
+ @Override
+ public Parcelable onSaveInstanceState() {
+ SavedState state = new SavedState();
+ state.openSubMenuId = mOpenSubMenuId;
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ SavedState saved = (SavedState) state;
+ if (saved.openSubMenuId > 0) {
+ MenuItem item = mMenu.findItem(saved.openSubMenuId);
+ if (item != null) {
+ SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
+ onSubMenuSelected(subMenu);
+ }
+ }
+ }
+
+ private static class SavedState implements Parcelable {
+ public int openSubMenuId;
+
+ SavedState() {
+ }
+
+ SavedState(Parcel in) {
+ openSubMenuId = in.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(openSubMenuId);
+ }
+
+ public static final Parcelable.Creator<SavedState> CREATOR
+ = new Parcelable.Creator<SavedState>() {
+ public SavedState createFromParcel(Parcel in) {
+ return new SavedState(in);
+ }
+
+ public SavedState[] newArray(int size) {
+ return new SavedState[size];
+ }
+ };
+ }
+
private class OverflowMenuButton extends ImageButton implements ActionMenuChildView {
public OverflowMenuButton(Context context) {
super(context, null, com.android.internal.R.attr.actionOverflowButtonStyle);
@@ -460,6 +519,7 @@
public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
boolean overflowOnly) {
super(context, menu, anchorView, overflowOnly);
+ setCallback(mPopupPresenterCallback);
}
@Override
@@ -482,6 +542,8 @@
// Give a reasonable anchor to nested submenus.
setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
}
+
+ setCallback(mPopupPresenterCallback);
}
@Override
@@ -489,6 +551,20 @@
super.onDismiss();
mSubMenu.close();
mActionButtonPopup = null;
+ mOpenSubMenuId = 0;
+ }
+ }
+
+ private class PopupPresenterCallback implements MenuPresenter.Callback {
+
+ @Override
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
+ mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
+ return false;
+ }
+
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
}
}
diff --git a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
index ddbb08c..ed9d34a 100644
--- a/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/BaseMenuPresenter.java
@@ -39,6 +39,8 @@
protected MenuView mMenuView;
+ private int mId;
+
/**
* Construct a new BaseMenuPresenter.
*
@@ -200,4 +202,12 @@
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
return false;
}
+
+ public int getId() {
+ return mId;
+ }
+
+ public void setId(int id) {
+ mId = id;
+ }
}
diff --git a/core/java/com/android/internal/view/menu/IconMenuPresenter.java b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
index f717904..56128d4 100644
--- a/core/java/com/android/internal/view/menu/IconMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/IconMenuPresenter.java
@@ -23,6 +23,7 @@
import android.util.SparseArray;
import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
+import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -35,7 +36,12 @@
private IconMenuItemView mMoreView;
private int mMaxItems = -1;
+ int mOpenSubMenuId;
+ SubMenuPresenterCallback mSubMenuPresenterCallback = new SubMenuPresenterCallback();
+ MenuDialogHelper mOpenSubMenu;
+
private static final String VIEWS_TAG = "android:menu:icon";
+ private static final String OPEN_SUBMENU_KEY = "android:menu:icon:submenu";
public IconMenuPresenter() {
super(com.android.internal.R.layout.icon_menu_layout,
@@ -86,7 +92,11 @@
if (!subMenu.hasVisibleItems()) return false;
// The window manager will give us a token.
- new MenuDialogHelper(subMenu).show(null);
+ MenuDialogHelper helper = new MenuDialogHelper(subMenu);
+ helper.setPresenterCallback(mSubMenuPresenterCallback);
+ helper.show(null);
+ mOpenSubMenu = helper;
+ mOpenSubMenuId = subMenu.getItem().getItemId();
super.onSubMenuSelected(subMenu);
return true;
}
@@ -137,5 +147,47 @@
if (viewStates != null) {
((View) mMenuView).restoreHierarchyState(viewStates);
}
+ int subMenuId = inState.getInt(OPEN_SUBMENU_KEY, 0);
+ if (subMenuId > 0 && mMenu != null) {
+ MenuItem item = mMenu.findItem(subMenuId);
+ if (item != null) {
+ onSubMenuSelected((SubMenuBuilder) item.getSubMenu());
+ }
+ }
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ if (mMenuView == null) {
+ return null;
+ }
+
+ Bundle state = new Bundle();
+ saveHierarchyState(state);
+ if (mOpenSubMenuId > 0) {
+ state.putInt(OPEN_SUBMENU_KEY, mOpenSubMenuId);
+ }
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ restoreHierarchyState((Bundle) state);
+ }
+
+ class SubMenuPresenterCallback implements MenuPresenter.Callback {
+ @Override
+ public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
+ mOpenSubMenuId = 0;
+ mOpenSubMenu.dismiss();
+ mOpenSubMenu = null;
+ }
+
+ @Override
+ public boolean onOpenSubMenu(MenuBuilder subMenu) {
+ mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
+ return false;
+ }
+
}
}
diff --git a/core/java/com/android/internal/view/menu/ListMenuPresenter.java b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
index 27e4191..146c7ac 100644
--- a/core/java/com/android/internal/view/menu/ListMenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/ListMenuPresenter.java
@@ -47,6 +47,8 @@
private Callback mCallback;
private MenuAdapter mAdapter;
+ private int mId;
+
public static final String VIEWS_TAG = "android:menu:list";
/**
@@ -182,6 +184,31 @@
}
}
+ public void setId(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ if (mMenuView == null) {
+ return null;
+ }
+
+ Bundle state = new Bundle();
+ saveHierarchyState(state);
+ return state;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ restoreHierarchyState((Bundle) state);
+ }
+
private class MenuAdapter extends BaseAdapter {
public int getCount() {
ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index fdfa954..a4edbc5 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -25,8 +25,8 @@
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.os.Bundle;
import android.os.Parcelable;
-import android.util.Log;
import android.util.SparseArray;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyCharacterMap;
@@ -38,7 +38,6 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
-import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
@@ -49,6 +48,8 @@
public class MenuBuilder implements Menu {
private static final String LOGTAG = "MenuBuilder";
+ private static final String PRESENTER_KEY = "android:menu:presenters";
+
private static final int[] sCategoryToOrder = new int[] {
1, /* No category */
4, /* CONTAINER */
@@ -254,6 +255,58 @@
return result;
}
+ private void dispatchSaveInstanceState(Bundle outState) {
+ if (mPresenters.isEmpty()) return;
+
+ SparseArray<Parcelable> presenterStates = new SparseArray<Parcelable>();
+
+ for (WeakReference<MenuPresenter> ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ final int id = presenter.getId();
+ if (id > 0) {
+ final Parcelable state = presenter.onSaveInstanceState();
+ if (state != null) {
+ presenterStates.put(id, state);
+ }
+ }
+ }
+ }
+
+ outState.putSparseParcelableArray(PRESENTER_KEY, presenterStates);
+ }
+
+ private void dispatchRestoreInstanceState(Bundle state) {
+ SparseArray<Parcelable> presenterStates = state.getSparseParcelableArray(PRESENTER_KEY);
+
+ if (presenterStates == null || mPresenters.isEmpty()) return;
+
+ for (WeakReference<MenuPresenter> ref : mPresenters) {
+ final MenuPresenter presenter = ref.get();
+ if (presenter == null) {
+ mPresenters.remove(ref);
+ } else {
+ final int id = presenter.getId();
+ if (id > 0) {
+ Parcelable parcel = presenterStates.get(id);
+ if (parcel != null) {
+ presenter.onRestoreInstanceState(parcel);
+ }
+ }
+ }
+ }
+ }
+
+ public void savePresenterStates(Bundle outState) {
+ dispatchSaveInstanceState(outState);
+ }
+
+ public void restorePresenterStates(Bundle state) {
+ dispatchRestoreInstanceState(state);
+ }
+
public void setCallback(Callback cb) {
mCallback = cb;
}
diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
index cffbb4e..4ecc828 100644
--- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java
+++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java
@@ -18,6 +18,7 @@
import android.content.Context;
import android.content.res.Resources;
+import android.os.Parcelable;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -296,4 +297,18 @@
return convertView;
}
}
+
+ @Override
+ public int getId() {
+ return 0;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
}
diff --git a/core/java/com/android/internal/view/menu/MenuPresenter.java b/core/java/com/android/internal/view/menu/MenuPresenter.java
index bd66448..d913a39 100644
--- a/core/java/com/android/internal/view/menu/MenuPresenter.java
+++ b/core/java/com/android/internal/view/menu/MenuPresenter.java
@@ -17,6 +17,7 @@
package com.android.internal.view.menu;
import android.content.Context;
+import android.os.Parcelable;
import android.view.Menu;
import android.view.ViewGroup;
@@ -125,4 +126,24 @@
* @return true if this presenter collapsed the action view, false otherwise.
*/
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item);
+
+ /**
+ * Returns an ID for determining how to save/restore instance state.
+ * @return a valid ID value.
+ */
+ public int getId();
+
+ /**
+ * Returns a Parcelable describing the current state of the presenter.
+ * It will be passed to the {@link #onRestoreInstanceState(Parcelable)}
+ * method of the presenter sharing the same ID later.
+ * @return The saved instance state
+ */
+ public Parcelable onSaveInstanceState();
+
+ /**
+ * Supplies the previously saved instance state to be restored.
+ * @param state The previously saved instance state
+ */
+ public void onRestoreInstanceState(Parcelable state);
}
diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java
index 09bc1fc..595753a 100644
--- a/core/java/com/android/internal/widget/ActionBarView.java
+++ b/core/java/com/android/internal/widget/ActionBarView.java
@@ -337,6 +337,7 @@
if (mActionMenuPresenter == null) {
mActionMenuPresenter = new ActionMenuPresenter();
mActionMenuPresenter.setCallback(cb);
+ mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter);
mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter();
}
@@ -1301,5 +1302,19 @@
item.setActionViewExpanded(false);
return true;
}
+
+ @Override
+ public int getId() {
+ return 0;
+ }
+
+ @Override
+ public Parcelable onSaveInstanceState() {
+ return null;
+ }
+
+ @Override
+ public void onRestoreInstanceState(Parcelable state) {
+ }
}
}
diff --git a/core/res/res/values/ids.xml b/core/res/res/values/ids.xml
index d05685c..547a192 100644
--- a/core/res/res/values/ids.xml
+++ b/core/res/res/values/ids.xml
@@ -74,4 +74,9 @@
<item type="id" name="rowTypeId" />
<item type="id" name="up" />
<item type="id" name="action_menu_divider" />
+ <item type="id" name="icon_menu_presenter" />
+ <item type="id" name="list_menu_presenter" />
+ <item type="id" name="action_menu_presenter" />
+ <item type="id" name="overflow_menu_presenter" />
+ <item type="id" name="popup_submenu_presenter" />
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 97a8c0b..70c204e 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2038,13 +2038,13 @@
<string name="autofill_phone_re">phone<!-- de-DE -->|telefonnummer<!-- es -->|telefono|teléfono<!-- fr-FR -->|telfixe<!-- ja-JP -->|電話<!-- pt-BR, pt-PT -->|telefone|telemovel<!-- ru -->|телефон<!-- zh-CN -->|电话</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_area_code_re">area code</string>
+ <string name="autofill_area_code_re">area.*code|acode|area</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_prefix_re">^-$|\\)$|prefix<!-- fr-FR -->|preselection<!-- pt-BR, pt-PT -->|ddd</string>
+ <string name="autofill_phone_prefix_re">prefix<!-- fr-FR -->|preselection<!-- pt-BR, pt-PT -->|ddd</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_suffix_re">^-$|suffix</string>
+ <string name="autofill_phone_suffix_re">suffix</string>
<!-- Do not translate. Regex used by AutoFill. -->
<string name="autofill_phone_extension_re">ext<!-- pt-BR, pt-PT -->|ramal</string>
@@ -2077,17 +2077,49 @@
<string name="autofill_country_code_re">country.*code|ccode|_cc</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_area_code_notext_re">^\($</string>
+ <string name="autofill_area_code_notext_re">^\\($</string>
<!-- Do not translate. Regex used by AutoFill. -->
- <string name="autofill_phone_prefix_separator_re">^-$|^\)$</string>
+ <string name="autofill_phone_prefix_separator_re">^-$|^\\)$</string>
<!-- Do not translate. Regex used by AutoFill. -->
<string name="autofill_phone_suffix_separator_re">^-$</string>
- <!-- Do not translate. Regex used by AutoFill. -->
- <!-- Ex: ************1234 -->
- <string name="credit_card_number_preview_format">$1</string>
+ <!-- Label in a web form for "Province" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_province">Province</string>
+
+ <!-- Label in a web form for "Postal code" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_postal_code">Postal code</string>
+
+ <!-- Label in a web form for "State" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_state">State</string>
+
+ <!-- Label in a web form for "ZIP code" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_zip_code">ZIP code</string>
+
+ <!-- Label in a web form for "County" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_county">County</string>
+
+ <!-- Label in a web form for "Island" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_island">Island</string>
+
+ <!-- Label in a web form for "District" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_district">District</string>
+
+ <!-- Label in a web form for "Department" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_department">Department</string>
+
+ <!-- Label in a web form for "Prefecture" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_prefecture">Prefecture</string>
+
+ <!-- Label in a web form for "Parish" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_parish">Parish</string>
+
+ <!-- Label in a web form for "Area" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_area">Area</string>
+
+ <!-- Label in a web form for "Emirate" [CHAR-LIMIT=NONE] -->
+ <string name="autofill_emirate">Emirate</string>
<!-- Title of an application permission, listed so the user can choose whether
they want to allow the application to do this. -->
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 00f47fa..b2ebb08 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -12,7 +12,7 @@
$(call all-java-files-under, EnabledTestApp/src)
LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver guava
LOCAL_JAVA_LIBRARIES := android.test.runner
LOCAL_PACKAGE_NAME := FrameworksCoreTests
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 40fa552..146466f 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -1242,6 +1242,13 @@
</intent-filter>
</activity>
+ <activity android:name="android.animation.BasicAnimatorActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/core/tests/coretests/res/layout/animator_basic.xml b/core/tests/coretests/res/layout/animator_basic.xml
new file mode 100644
index 0000000..7b8ef11
--- /dev/null
+++ b/core/tests/coretests/res/layout/animator_basic.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+ <Button
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/animatingButton"/>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
new file mode 100644
index 0000000..65f2b8e
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/AnimatorSetEventsTest.java
@@ -0,0 +1,39 @@
+/*
+* 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 android.animation;
+
+import android.widget.Button;
+import com.android.frameworks.coretests.R;
+
+/**
+ * Listener tests for AnimatorSet.
+ */
+public class AnimatorSetEventsTest extends EventsTest {
+
+ @Override
+ public void setUp() throws Exception {
+ final BasicAnimatorActivity activity = getActivity();
+ Button button = (Button) activity.findViewById(R.id.animatingButton);
+
+ ObjectAnimator xAnim = ObjectAnimator.ofFloat(button, "translationX", 0, 100);
+ ObjectAnimator yAnim = ObjectAnimator.ofFloat(button, "translationX", 0, 100);
+ mAnimator = new AnimatorSet();
+ ((AnimatorSet)mAnimator).playSequentially(xAnim, yAnim);
+
+ super.setUp();
+ }
+
+}
diff --git a/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java
new file mode 100644
index 0000000..93808d9
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/BasicAnimatorActivity.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.animation;
+
+import com.android.frameworks.coretests.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class BasicAnimatorActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.animator_basic);
+ }
+}
diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java
new file mode 100644
index 0000000..f970ffc
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/EventsTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.animation;
+
+import android.os.Handler;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.UiThreadTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Tests for the various lifecycle events of Animators. This abstract class is subclassed by
+ * concrete implementations that provide the actual Animator objects being tested. All of the
+ * testing mechanisms are in this class; the subclasses are only responsible for providing
+ * the mAnimator object.
+ *
+ * This test is more complicated than a typical synchronous test because much of the functionality
+ * must happen on the UI thread. Some tests do this by using the UiThreadTest annotation to
+ * automatically run the whole test on that thread. Other tests must run on the UI thread and also
+ * wait for some later event to occur before ending. These tests use a combination of an
+ * AbstractFuture mechanism and a delayed action to release that Future later.
+ */
+public abstract class EventsTest
+ extends ActivityInstrumentationTestCase2<BasicAnimatorActivity> {
+
+ private static final int ANIM_DURATION = 400;
+ private static final int ANIM_DELAY = 100;
+ private static final int ANIM_MID_DURATION = ANIM_DURATION / 2;
+ private static final int ANIM_MID_DELAY = ANIM_DELAY / 2;
+
+ private boolean mRunning; // tracks whether we've started the animator
+ private boolean mCanceled; // trackes whether we've canceled the animator
+ private Animator.AnimatorListener mFutureListener; // mechanism for delaying the end of the test
+ private FutureWaiter mFuture; // Mechanism for waiting for the UI test to complete
+ private Animator.AnimatorListener mListener; // Listener that handles/tests the events
+
+ protected Animator mAnimator; // The animator used in the tests. Must be set in subclass
+ // setup() method prior to calling the superclass setup()
+
+ /**
+ * Cancels the given animator. Used to delay cancelation until some later time (after the
+ * animator has started playing).
+ */
+ static class Canceler implements Runnable {
+ Animator mAnim;
+ public Canceler(Animator anim) {
+ mAnim = anim;
+ }
+ @Override
+ public void run() {
+ mAnim.cancel();
+ }
+ };
+
+ /**
+ * Releases the given Future object when the listener's end() event is called. Specifically,
+ * it releases it after some further delay, to give the test time to do other things right
+ * after an animation ends.
+ */
+ static class FutureReleaseListener extends AnimatorListenerAdapter {
+ FutureWaiter mFuture;
+
+ public FutureReleaseListener(FutureWaiter future) {
+ mFuture = future;
+ }
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ Handler handler = new Handler();
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mFuture.release();
+ }
+ }, ANIM_MID_DURATION);
+ }
+ };
+
+ public EventsTest() {
+ super(BasicAnimatorActivity.class);
+ }
+
+
+ /**
+ * Sets up the fields used by each test. Subclasses must override this method to create
+ * the protected mAnimator object used in all tests. Overrides must create that animator
+ * and then call super.setup(), where further properties are set on that animator.
+ * @throws Exception
+ */
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ // mListener is the main testing mechanism of this file. The asserts of each test
+ // are embedded in the listener callbacks that it implements.
+ mListener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationCancel(Animator animation) {
+ // This should only be called on an animation that has been started and not
+ // yet canceled or ended
+ assertFalse(mCanceled);
+ assertTrue(mRunning);
+ mCanceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ // This should only be called on an animation that has been started and not
+ // yet ended
+ assertTrue(mRunning);
+ mRunning = false;
+ super.onAnimationEnd(animation);
+ }
+ };
+
+ mAnimator.addListener(mListener);
+ mAnimator.setDuration(ANIM_DURATION);
+
+ mFuture = new FutureWaiter();
+
+ mRunning = false;
+ mCanceled = false;
+ }
+
+ /**
+ * Verify that calling cancel on an unstarted animator does nothing.
+ */
+ @UiThreadTest
+ @SmallTest
+ public void testCancel() throws Exception {
+ mAnimator.cancel();
+ }
+
+ /**
+ * Verify that calling cancel on a started animator does the right thing.
+ */
+ @UiThreadTest
+ @SmallTest
+ public void testStartCancel() throws Exception {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ }
+
+ /**
+ * Same as testStartCancel, but with a startDelayed animator
+ */
+ @UiThreadTest
+ @SmallTest
+ public void testStartDelayedCancel() throws Exception {
+ mAnimator.setStartDelay(ANIM_DELAY);
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ }
+
+ /**
+ * Verify that canceling an animator that is playing does the right thing.
+ */
+ @MediumTest
+ public void testPlayingCancel() throws Exception {
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Canceler(mAnimator), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get();
+ }
+
+ /**
+ * Same as testPlayingCancel, but with a startDelayed animator
+ */
+ @MediumTest
+ public void testPlayingDelayedCancel() throws Exception {
+ mAnimator.setStartDelay(ANIM_DELAY);
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Handler handler = new Handler();
+ mAnimator.addListener(mFutureListener);
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Canceler(mAnimator), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get();
+ }
+
+ /**
+ * Verifies that canceling a started animation after it has already been canceled
+ * does nothing.
+ */
+ @MediumTest
+ public void testStartDoubleCancel() throws Exception {
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get();
+ }
+
+ /**
+ * Same as testStartDoubleCancel, but with a startDelayed animator
+ */
+ @MediumTest
+ public void testStartDelayedDoubleCancel() throws Exception {
+ mAnimator.setStartDelay(ANIM_DELAY);
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get();
+ }
+
+
+}
diff --git a/core/tests/coretests/src/android/animation/FutureWaiter.java b/core/tests/coretests/src/android/animation/FutureWaiter.java
new file mode 100644
index 0000000..320a1c2
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/FutureWaiter.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.animation;
+
+import com.google.common.util.concurrent.AbstractFuture;
+
+/**
+ * Simple extension of {@link com.google.common.util.concurrent.AbstractFuture} which exposes a new
+ * release() method which calls the protected
+ * {@link com.google.common.util.concurrent.AbstractFuture#set(Object)} method internally. It
+ * also exposes the protected {@link AbstractFuture#setException(Throwable)} method.
+ */
+public class FutureWaiter extends AbstractFuture<Void> {
+
+ /**
+ * Release the Future currently waiting on
+ * {@link com.google.common.util.concurrent.AbstractFuture#get()}.
+ */
+ public void release() {
+ super.set(null);
+ }
+
+ @Override
+ public boolean setException(Throwable throwable) {
+ return super.setException(throwable);
+ }
+}
diff --git a/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
new file mode 100644
index 0000000..606a939
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/ObjectAnimatorEventsTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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 android.animation;
+
+import android.widget.Button;
+import com.android.frameworks.coretests.R;
+
+/**
+ * Listener tests for ObjectAnimator.
+ */
+public class ObjectAnimatorEventsTest extends EventsTest {
+
+ @Override
+ public void setUp() throws Exception {
+ final BasicAnimatorActivity activity = getActivity();
+ Button button = (Button) activity.findViewById(R.id.animatingButton);
+
+ mAnimator = ObjectAnimator.ofFloat(button, "translationX", 0, 100);
+ super.setUp();
+ }
+
+}
diff --git a/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
new file mode 100644
index 0000000..c25d050
--- /dev/null
+++ b/core/tests/coretests/src/android/animation/ValueAnimatorEventsTest.java
@@ -0,0 +1,29 @@
+/*
+ * 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 android.animation;
+
+/**
+ * Listener tests for ValueAnimator.
+ */
+public class ValueAnimatorEventsTest extends EventsTest {
+
+ @Override
+ public void setUp() throws Exception {
+ mAnimator = ValueAnimator.ofFloat(0, 1);
+ super.setUp();
+ }
+
+}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 3cb64c7..82345e2 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -44,20 +44,20 @@
public void testAddEntryGrow() throws Exception {
final NetworkStats stats = new NetworkStats(TEST_START, 2);
- assertEquals(0, stats.size);
+ assertEquals(0, stats.size());
assertEquals(2, stats.iface.length);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 1L, 2L);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 2L, 2L);
- assertEquals(2, stats.size);
+ assertEquals(2, stats.size());
assertEquals(2, stats.iface.length);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 3L, 4L);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 4L, 4L);
stats.addEntry(TEST_IFACE, TEST_UID, TAG_NONE, 5L, 5L);
- assertEquals(5, stats.size);
+ assertEquals(5, stats.size());
assertTrue(stats.iface.length >= 5);
assertEquals(1L, stats.rx[0]);
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 6254192..12e5ada 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -155,6 +155,14 @@
}
}
+
+ private int getIDSafe() {
+ if (mAdaptedAllocation != null) {
+ return mAdaptedAllocation.getID();
+ }
+ return getID();
+ }
+
private void updateCacheInfo(Type t) {
mCurrentDimX = t.getX();
mCurrentDimY = t.getY();
@@ -262,7 +270,7 @@
throw new RSIllegalArgumentException("Source must be exactly one usage type.");
}
mRS.validate();
- mRS.nAllocationSyncAll(getID(), srcLocation);
+ mRS.nAllocationSyncAll(getIDSafe(), srcLocation);
}
public void copyFrom(BaseObj[] d) {
@@ -480,7 +488,7 @@
" does not match component size " + eSize + ".");
}
- mRS.nAllocationElementData1D(getID(), xoff, mSelectedLOD,
+ mRS.nAllocationElementData1D(getIDSafe(), xoff, mSelectedLOD,
component_number, data, data.length);
}
@@ -527,7 +535,7 @@
public void copy1DRangeFromUnchecked(int off, int count, int[] d) {
int dataSize = mType.mElement.getSizeBytes() * count;
data1DChecks(off, count, d.length * 4, dataSize);
- mRS.nAllocationData1D(getID(), off, mSelectedLOD, count, d, dataSize);
+ mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
}
/**
* Copy part of an allocation from an array. This variant is
@@ -541,7 +549,7 @@
public void copy1DRangeFromUnchecked(int off, int count, short[] d) {
int dataSize = mType.mElement.getSizeBytes() * count;
data1DChecks(off, count, d.length * 2, dataSize);
- mRS.nAllocationData1D(getID(), off, mSelectedLOD, count, d, dataSize);
+ mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
}
/**
* Copy part of an allocation from an array. This variant is
@@ -555,7 +563,7 @@
public void copy1DRangeFromUnchecked(int off, int count, byte[] d) {
int dataSize = mType.mElement.getSizeBytes() * count;
data1DChecks(off, count, d.length, dataSize);
- mRS.nAllocationData1D(getID(), off, mSelectedLOD, count, d, dataSize);
+ mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
}
/**
* Copy part of an allocation from an array. This variant is
@@ -569,7 +577,7 @@
public void copy1DRangeFromUnchecked(int off, int count, float[] d) {
int dataSize = mType.mElement.getSizeBytes() * count;
data1DChecks(off, count, d.length * 4, dataSize);
- mRS.nAllocationData1D(getID(), off, mSelectedLOD, count, d, dataSize);
+ mRS.nAllocationData1D(getIDSafe(), off, mSelectedLOD, count, d, dataSize);
}
/**
@@ -638,7 +646,7 @@
* be copied.
*/
public void copy1DRangeFrom(int off, int count, Allocation data, int dataOff) {
- mRS.nAllocationData2D(getID(), off, 0,
+ mRS.nAllocationData2D(getIDSafe(), off, 0,
mSelectedLOD, mSelectedFace.mID,
count, 1, data.getID(), dataOff, 0,
data.mSelectedLOD, data.mSelectedFace.mID);
@@ -674,28 +682,28 @@
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, byte[] data) {
mRS.validate();
validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
+ mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length);
}
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, short[] data) {
mRS.validate();
validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
+ mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length * 2);
}
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, int[] data) {
mRS.validate();
validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
+ mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length * 4);
}
public void copy2DRangeFrom(int xoff, int yoff, int w, int h, float[] data) {
mRS.validate();
validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
+ mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID,
w, h, data, data.length * 4);
}
@@ -715,7 +723,7 @@
Allocation data, int dataXoff, int dataYoff) {
mRS.validate();
validate2DRange(xoff, yoff, w, h);
- mRS.nAllocationData2D(getID(), xoff, yoff,
+ mRS.nAllocationData2D(getIDSafe(), xoff, yoff,
mSelectedLOD, mSelectedFace.mID,
w, h, data.getID(), dataXoff, dataYoff,
data.mSelectedLOD, data.mSelectedFace.mID);
@@ -734,10 +742,16 @@
mRS.validate();
validateBitmapFormat(data);
validate2DRange(xoff, yoff, data.getWidth(), data.getHeight());
- mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data);
+ mRS.nAllocationData2D(getIDSafe(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, data);
}
+ /**
+ * Copy from the Allocation into a Bitmap. The bitmap must
+ * match the dimensions of the Allocation.
+ *
+ * @param b The bitmap to be set from the Allocation.
+ */
public void copyTo(Bitmap b) {
mRS.validate();
validateBitmapFormat(b);
@@ -745,24 +759,52 @@
mRS.nAllocationCopyToBitmap(getID(), b);
}
+ /**
+ * Copy from the Allocation into a byte array. The array must
+ * be at least as large as the Allocation. The allocation must
+ * be of an 8 bit elemental type.
+ *
+ * @param d The array to be set from the Allocation.
+ */
public void copyTo(byte[] d) {
validateIsInt8();
mRS.validate();
mRS.nAllocationRead(getID(), d);
}
+ /**
+ * Copy from the Allocation into a short array. The array must
+ * be at least as large as the Allocation. The allocation must
+ * be of an 16 bit elemental type.
+ *
+ * @param d The array to be set from the Allocation.
+ */
public void copyTo(short[] d) {
validateIsInt16();
mRS.validate();
mRS.nAllocationRead(getID(), d);
}
+ /**
+ * Copy from the Allocation into a int array. The array must be
+ * at least as large as the Allocation. The allocation must be
+ * of an 32 bit elemental type.
+ *
+ * @param d The array to be set from the Allocation.
+ */
public void copyTo(int[] d) {
validateIsInt32();
mRS.validate();
mRS.nAllocationRead(getID(), d);
}
+ /**
+ * Copy from the Allocation into a float array. The array must
+ * be at least as large as the Allocation. The allocation must
+ * be of an 32 bit float elemental type.
+ *
+ * @param d The array to be set from the Allocation.
+ */
public void copyTo(float[] d) {
validateIsFloat32();
mRS.validate();
diff --git a/graphics/java/android/renderscript/AllocationAdapter.java b/graphics/java/android/renderscript/AllocationAdapter.java
index ca5246a..d38f2df 100644
--- a/graphics/java/android/renderscript/AllocationAdapter.java
+++ b/graphics/java/android/renderscript/AllocationAdapter.java
@@ -31,7 +31,8 @@
}
int getID() {
- return mAdaptedAllocation.getID();
+ throw new RSInvalidStateException(
+ "This operation is not supported with adapters at this time.");
}
/**
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index 6ce44fc..9db7364 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -65,6 +65,8 @@
int dispatchDisconnect(va_list args);
int dispatchSetBufferCount(va_list args);
int dispatchSetBuffersGeometry(va_list args);
+ int dispatchSetBuffersDimensions(va_list args);
+ int dispatchSetBuffersFormat(va_list args);
int dispatchSetBuffersTransform(va_list args);
int dispatchSetBuffersTimestamp(va_list args);
int dispatchSetCrop(va_list args);
@@ -73,7 +75,8 @@
int connect(int api);
int disconnect(int api);
int setBufferCount(int bufferCount);
- int setBuffersGeometry(int w, int h, int format);
+ int setBuffersDimensions(int w, int h);
+ int setBuffersFormat(int format);
int setBuffersTransform(int transform);
int setBuffersTimestamp(int64_t timestamp);
int setCrop(Rect const* rect);
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index b9b2310..e203035 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -254,6 +254,12 @@
case NATIVE_WINDOW_SET_BUFFERS_TIMESTAMP:
res = dispatchSetBuffersTimestamp(args);
break;
+ case NATIVE_WINDOW_SET_BUFFERS_DIMENSIONS:
+ res = dispatchSetBuffersDimensions(args);
+ break;
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
+ res = dispatchSetBuffersFormat(args);
+ break;
default:
res = NAME_NOT_FOUND;
break;
@@ -290,7 +296,22 @@
int w = va_arg(args, int);
int h = va_arg(args, int);
int f = va_arg(args, int);
- return setBuffersGeometry(w, h, f);
+ int err = setBuffersDimensions(w, h);
+ if (err != 0) {
+ return err;
+ }
+ return setBuffersFormat(f);
+}
+
+int SurfaceTextureClient::dispatchSetBuffersDimensions(va_list args) {
+ int w = va_arg(args, int);
+ int h = va_arg(args, int);
+ return setBuffersDimensions(w, h);
+}
+
+int SurfaceTextureClient::dispatchSetBuffersFormat(va_list args) {
+ int f = va_arg(args, int);
+ return setBuffersFormat(f);
}
int SurfaceTextureClient::dispatchSetBuffersTransform(va_list args) {
@@ -390,12 +411,12 @@
return err;
}
-int SurfaceTextureClient::setBuffersGeometry(int w, int h, int format)
+int SurfaceTextureClient::setBuffersDimensions(int w, int h)
{
- LOGV("SurfaceTextureClient::setBuffersGeometry");
+ LOGV("SurfaceTextureClient::setBuffersDimensions");
Mutex::Autolock lock(mMutex);
- if (w<0 || h<0 || format<0)
+ if (w<0 || h<0)
return BAD_VALUE;
if ((w && !h) || (!w && h))
@@ -403,7 +424,6 @@
mReqWidth = w;
mReqHeight = h;
- mReqFormat = format;
status_t err = mSurfaceTexture->setCrop(Rect(0, 0));
LOGE_IF(err, "ISurfaceTexture::setCrop(...) returned %s", strerror(-err));
@@ -411,6 +431,19 @@
return err;
}
+int SurfaceTextureClient::setBuffersFormat(int format)
+{
+ LOGV("SurfaceTextureClient::setBuffersFormat");
+ Mutex::Autolock lock(mMutex);
+
+ if (format<0)
+ return BAD_VALUE;
+
+ mReqFormat = format;
+
+ return NO_ERROR;
+}
+
int SurfaceTextureClient::setBuffersTransform(int transform)
{
LOGV("SurfaceTextureClient::setBuffersTransform");
diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp
index 4393504..9c10c75 100644
--- a/libs/ui/FramebufferNativeWindow.cpp
+++ b/libs/ui/FramebufferNativeWindow.cpp
@@ -299,6 +299,7 @@
{
switch (operation) {
case NATIVE_WINDOW_SET_USAGE:
+ case NATIVE_WINDOW_SET_BUFFERS_FORMAT:
case NATIVE_WINDOW_CONNECT:
case NATIVE_WINDOW_DISCONNECT:
break;
diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp
index 289c826..87ae3d5 100644
--- a/libs/utils/VectorImpl.cpp
+++ b/libs/utils/VectorImpl.cpp
@@ -290,7 +290,7 @@
void* VectorImpl::editItemLocation(size_t index)
{
LOG_ASSERT(index<capacity(),
- "[%p] itemLocation: index=%d, capacity=%d, count=%d",
+ "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
this, (int)index, (int)capacity(), (int)mCount);
void* buffer = editArrayImpl();
@@ -302,7 +302,7 @@
const void* VectorImpl::itemLocation(size_t index) const
{
LOG_ASSERT(index<capacity(),
- "[%p] editItemLocation: index=%d, capacity=%d, count=%d",
+ "[%p] itemLocation: index=%d, capacity=%d, count=%d",
this, (int)index, (int)capacity(), (int)mCount);
const void* buffer = arrayImpl();
diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c
index a3e76d9..a9689bc 100644
--- a/media/libeffects/factory/EffectsFactory.c
+++ b/media/libeffects/factory/EffectsFactory.c
@@ -130,10 +130,43 @@
return ret;
}
+int Effect_ProcessReverse(effect_handle_t self, audio_buffer_t *inBuffer, audio_buffer_t *outBuffer)
+{
+ int ret = init();
+ if (ret < 0) {
+ return ret;
+ }
+ effect_entry_t *fx = (effect_entry_t *)self;
+ pthread_mutex_lock(&gLibLock);
+ if (fx->lib == NULL) {
+ pthread_mutex_unlock(&gLibLock);
+ return -EPIPE;
+ }
+ pthread_mutex_lock(&fx->lib->lock);
+ pthread_mutex_unlock(&gLibLock);
+
+ if ((*fx->subItfe)->process_reverse != NULL) {
+ ret = (*fx->subItfe)->process_reverse(fx->subItfe, inBuffer, outBuffer);
+ } else {
+ ret = -ENOSYS;
+ }
+ pthread_mutex_unlock(&fx->lib->lock);
+ return ret;
+}
+
+
const struct effect_interface_s gInterface = {
Effect_Process,
Effect_Command,
- Effect_GetDescriptor
+ Effect_GetDescriptor,
+ NULL
+};
+
+const struct effect_interface_s gInterfaceWithReverse = {
+ Effect_Process,
+ Effect_Command,
+ Effect_GetDescriptor,
+ Effect_ProcessReverse
};
/////////////////////////////////////////////////
@@ -266,7 +299,13 @@
// add entry to effect list
fx = (effect_entry_t *)malloc(sizeof(effect_entry_t));
fx->subItfe = itfe;
- fx->itfe = (struct effect_interface_s *)&gInterface;
+ if ((*itfe)->process_reverse != NULL) {
+ fx->itfe = (struct effect_interface_s *)&gInterfaceWithReverse;
+ LOGV("EffectCreate() gInterfaceWithReverse");
+ } else {
+ fx->itfe = (struct effect_interface_s *)&gInterface;
+ LOGV("EffectCreate() gInterface");
+ }
fx->lib = l;
e = (list_elem_t *)malloc(sizeof(list_elem_t));
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 21c451f..efa1c45 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -3231,7 +3231,8 @@
const struct effect_interface_s gLvmEffectInterface = {
Effect_process,
Effect_command,
- Effect_getDescriptor
+ Effect_getDescriptor,
+ NULL,
}; /* end gLvmEffectInterface */
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
diff --git a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
index 2727375..663f8ff 100755
--- a/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
+++ b/media/libeffects/lvm/wrapper/Reverb/EffectReverb.cpp
@@ -2134,7 +2134,8 @@
const struct effect_interface_s gReverbInterface = {
Reverb_process,
Reverb_command,
- Reverb_getDescriptor
+ Reverb_getDescriptor,
+ NULL,
}; /* end gReverbInterface */
audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
diff --git a/media/libeffects/testlibs/EffectEqualizer.cpp b/media/libeffects/testlibs/EffectEqualizer.cpp
index c2ffce5..b22ebec 100644
--- a/media/libeffects/testlibs/EffectEqualizer.cpp
+++ b/media/libeffects/testlibs/EffectEqualizer.cpp
@@ -736,7 +736,8 @@
const struct effect_interface_s gEqualizerInterface = {
Equalizer_process,
Equalizer_command,
- Equalizer_getDescriptor
+ Equalizer_getDescriptor,
+ NULL
};
diff --git a/media/libeffects/testlibs/EffectReverb.c b/media/libeffects/testlibs/EffectReverb.c
index 02762c9..405f908 100644
--- a/media/libeffects/testlibs/EffectReverb.c
+++ b/media/libeffects/testlibs/EffectReverb.c
@@ -27,7 +27,8 @@
const struct effect_interface_s gReverbInterface = {
Reverb_Process,
Reverb_Command,
- Reverb_GetDescriptor
+ Reverb_GetDescriptor,
+ NULL
};
// Google auxiliary environmental reverb UUID: 1f0ae2e0-4ef7-11df-bc09-0002a5d5c51b
diff --git a/media/libeffects/visualizer/EffectVisualizer.cpp b/media/libeffects/visualizer/EffectVisualizer.cpp
index aeebd4d..3c3af8f 100644
--- a/media/libeffects/visualizer/EffectVisualizer.cpp
+++ b/media/libeffects/visualizer/EffectVisualizer.cpp
@@ -450,7 +450,8 @@
const struct effect_interface_s gVisualizerInterface = {
Visualizer_process,
Visualizer_command,
- Visualizer_getDescriptor
+ Visualizer_getDescriptor,
+ NULL,
};
diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp
index b11db32..ddad2d3 100644
--- a/opengl/libs/EGL/egl.cpp
+++ b/opengl/libs/EGL/egl.cpp
@@ -31,6 +31,7 @@
#include <cutils/properties.h>
#include <cutils/memory.h>
+#include <utils/CallStack.h>
#include <utils/String8.h>
#include "egldefs.h"
@@ -147,6 +148,10 @@
if (egl_tls_t::logNoContextCall()) {
LOGE("call to OpenGL ES API with no current context "
"(logged once per thread)");
+ LOGE("call stack before error:");
+ CallStack stack;
+ stack.update();
+ stack.dump();
}
return 0;
}
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 7d5d010..ba5d29a 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -367,7 +367,12 @@
if (cnx->egl.eglGetConfigAttrib(iDpy,
iConfig, EGL_NATIVE_VISUAL_ID, &format)) {
if (format != 0) {
- native_window_set_buffers_geometry(window, 0, 0, format);
+ int err = native_window_set_buffers_format(window, format);
+ if (err != 0) {
+ LOGE("error setting native window pixel format: %s (%d)",
+ strerror(-err), err);
+ return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
+ }
}
}
diff --git a/opengl/specs/EGL_ANDROID_blob_cache.txt b/opengl/specs/EGL_ANDROID_blob_cache.txt
new file mode 100644
index 0000000..55dc900
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_blob_cache.txt
@@ -0,0 +1,208 @@
+Name
+
+ ANDROID_blob_cache
+
+Name Strings
+
+ EGL_ANDROID_blob_cache
+
+Contributors
+
+ Jamie Gennis
+
+Contact
+
+ Jamie Gennis, Google Inc. (jgennis 'at' google.com)
+
+Status
+
+ Draft.
+
+Version
+
+ Version 1, April 22, 2011
+
+Number
+
+ EGL Extension #XXX
+
+Dependencies
+
+ Requires EGL 1.0
+
+ This extension is written against the wording of the EGL 1.4 Specification
+
+Overview
+
+ Shader compilation and optimization has been a troublesome aspect of OpenGL
+ programming for a long time. It can consume seconds of CPU cycles during
+ application start-up. Additionally, state-based re-compiles done
+ internally by the drivers add an unpredictable element to application
+ performance tuning, often leading to occasional pauses in otherwise smooth
+ animations.
+
+ This extension provides a mechanism through which client API
+ implementations may cache shader binaries after they are compiled. It may
+ then retrieve those cached shaders during subsequent executions of the same
+ program. The management of the cache is handled by the application (or
+ middleware), allowing it to be tuned to a particular platform or
+ environment.
+
+ While the focus of this extension is on providing a persistent cache for
+ shader binaries, it may also be useful for caching other data. This is
+ perfectly acceptable, but the guarantees provided (or lack thereof) were
+ designed around the shader use case.
+
+ Note that although this extension is written as if the application
+ implements the caching functionality, on the Android OS it is implemented
+ as part of the Android EGL module. This extension is not exposed to
+ applications on Android, but will be used automatically in every
+ application that uses EGL if it is supported by the underlying
+ device-specific EGL implementation.
+
+New Types
+
+ /*
+ * EGLsizei is a signed integer type for representing the size of a memory
+ * buffer.
+ */
+ #include <khrplatform.h>
+ typedef khronos_ssize_t EGLsizei;
+
+ /*
+ * EGLSetBlobFunc is a pointer to an application-provided function that a
+ * client API implementation may use to insert a key/value pair into the
+ * cache.
+ */
+ typedef void (*EGLSetBlobFunc) (const void* key, EGLsizei keySize,
+ const void* value, EGLsizei valueSize)
+
+ /*
+ * EGLGetBlobFunc is a pointer to an application-provided function that a
+ * client API implementation may use to retrieve a cached value from the
+ * cache.
+ */
+ typedef EGLsizei (*EGLGetBlobFunc) (const void* key, EGLsizei keySize,
+ void* value, EGLsizei valueSize)
+
+New Procedures and Functions
+
+ void eglSetBlobCacheFuncs(EGLDisplay dpy,
+ EGLSetBlobFunc set,
+ EGLGetBlobFunc get);
+
+New Tokens
+
+ None.
+
+Changes to Chapter 3 of the EGL 1.4 Specification (EGL Functions and Errors)
+
+ Add a new subsection after Section 3.8, page 50
+ (Synchronization Primitives)
+
+ "3.9 Persistent Caching
+
+ In order to facilitate persistent caching of internal client API state that
+ is slow to compute or collect, the application may specify callback
+ function pointers through which the client APIs can request data be cached
+ and retrieved. The command
+
+ void eglSetBlobCacheFuncs(EGLDisplay dpy,
+ EGLSetBlobFunc set, EGLGetBlobFunc get);
+
+ sets the callback function pointers that client APIs associated with
+ display <dpy> can use to interact with caching functionality provided by
+ the application. <set> points to a function that inserts a new value into
+ the cache and associates it with the given key. <get> points to a function
+ that retrieves from the cache the value associated with a given key. The
+ semantics of these callback functions are described in Section 3.9.1 (Cache
+ Operations).
+
+ Cache functions may only be specified once during the lifetime of an
+ EGLDisplay. The <set> and <get> functions may be called at any time and
+ from any thread from the time at which eglSetBlobCacheFuncs is called until
+ the time that the last resource associated with <dpy> is deleted and <dpy>
+ itself is terminated. Concurrent calls to these functions from different
+ threads is also allowed.
+
+ If eglSetBlobCacheFuncs generates an error then all client APIs must behave
+ as though eglSetBlobCacheFuncs was not called for the display <dpy>. If
+ <set> or <get> is NULL then an EGL_BAD_PARAMETER error is generated. If a
+ successful eglSetBlobCacheFuncs call was already made for <dpy> and the
+ display has not since been terminated then an EGL_BAD_PARAMETER error is
+ generated.
+
+ 3.9.1 Cache Operations
+
+ To insert a new binary value into the cache and associate it with a given
+ key, a client API implementation can call the application-provided callback
+ function
+
+ void (*set) (const void* key, EGLsizei keySize, const void* value,
+ EGLsizei valueSize)
+
+ <key> and <value> are pointers to the beginning of the key and value,
+ respectively, that are to be inserted. <keySize> and <valueSize> specify
+ the size in bytes of the data pointed to by <key> and <value>,
+ respectively.
+
+ No guarantees are made as to whether a given key/value pair is present in
+ the cache after the set call. If a different value has been associated
+ with the given key in the past then it is undefined which value, if any, is
+ associated with the key after the set call. Note that while there are no
+ guarantees, the cache implementation should attempt to cache the most
+ recently set value for a given key.
+
+ To retrieve the binary value associated with a given key from the cache, a
+ client API implementation can call the application-provided callback
+ function
+
+ EGLsizei (*get) (const void* key, EGLsizei keySize, void* value,
+ EGLsizei valueSize)
+
+ <key> is a pointer to the beginning of the key. <keySize> specifies the
+ size in bytes of the binary key pointed to by <key>. If the cache contains
+ a value associated with the given key then the size of that binary value in
+ bytes is returned. Otherwise 0 is returned.
+
+ If the cache contains a value for the given key and its size in bytes is
+ less than or equal to <valueSize> then the value is written to the memory
+ pointed to by <value>. Otherwise nothing is written to the memory pointed
+ to by <value>.
+
+Issues
+
+ 1. How should errors be handled in the callback functions?
+
+ RESOLVED: No guarantees are made about the presence of values in the cache,
+ so there should not be a need to return error information to the client API
+ implementation. The cache implementation can simply drop a value if it
+ encounters an error during the 'set' callback. Similarly, it can simply
+ return 0 if it encouters an error in a 'get' callback.
+
+ 2. When a client API driver gets updated, that may need to invalidate
+ previously cached entries. How can the driver handle this situation?
+
+ RESPONSE: There are a number of ways the driver can handle this situation.
+ The recommended way is to include the driver version in all cache keys.
+ That way each driver version will use a set of cache keys that are unique
+ to that version, and conflicts should never occur. Updating the driver
+ could then leave a number of values in the cache that will never be
+ requested again. If needed, the cache implementation can handle those
+ values in some way, but the driver does not need to take any special
+ action.
+
+ 3. How much data can be stored in the cache?
+
+ RESPONSE: This is entirely dependent upon the cache implementation.
+ Presumably it will be tuned to store enough data to be useful, but not
+ enough to become problematic. :)
+
+Revision History
+
+#2 (Jamie Gennis, April 25, 2011)
+ - Swapped the order of the size and pointer arguments to the get and set
+ functions.
+
+#1 (Jamie Gennis, April 22, 2011)
+ - Initial draft.
diff --git a/opengl/specs/EGL_ANDROID_recordable.txt b/opengl/specs/EGL_ANDROID_recordable.txt
new file mode 100644
index 0000000..cf44465
--- /dev/null
+++ b/opengl/specs/EGL_ANDROID_recordable.txt
@@ -0,0 +1,113 @@
+Name
+
+ ANDROID_recordable
+
+Name Strings
+
+ EGL_ANDROID_recordable
+
+Contributors
+
+ Jamie Gennis
+
+Contact
+
+ Jamie Gennis, Google Inc. (jgennis 'at' google.com)
+
+Status
+
+ Draft.
+
+Version
+
+ Version 1, July 8, 2011
+
+Number
+
+ EGL Extension #XXX
+
+Dependencies
+
+ Requires EGL 1.0
+
+ This extension is written against the wording of the EGL 1.4 Specification
+
+Overview
+
+ Android supports a number of different ANativeWindow implementations that
+ can be used to create an EGLSurface. One implementation, which records the
+ rendered image as a video each time eglSwapBuffers gets called, may have
+ some device-specific restrictions. Because of this, some EGLConfigs may be
+ incompatible with these ANativeWindows. This extension introduces a new
+ boolean EGLConfig attribute that indicates whether the EGLConfig supports
+ rendering to an ANativeWindow that records images to a video.
+
+New Types
+
+ None.
+
+New Procedures and Functions
+
+ None.
+
+New Tokens
+
+ Accepted by the <attribute> parameter of eglGetConfigAttrib and
+ the <attrib_list> parameter of eglChooseConfig:
+
+ EGL_RECORDABLE_ANDROID 0xXXXX
+
+Changes to Chapter 3 of the EGL 1.4 Specification (EGL Functions and Errors)
+
+ Section 3.4, Configuration Management, add a row to Table 3.1.
+
+ Attribute Type Notes
+ ---------------------- ------- --------------------------
+ EGL_RECORDABLE_ANDROID boolean whether video recording is
+ supported
+
+ Section 3.4, Configuration Management, add a row to Table 3.4.
+
+ Attribute Default Selection Sort Sort
+ Criteria Order Priority
+ ---------------------- ------------- --------- ----- --------
+ EGL_RECORDABLE_ANDROID EGL_DONT_CARE Exact None
+
+ Section 3.4, Configuration Management, add a paragraph at the end of the
+ subsection titled Other EGLConfig Attribute Descriptions.
+
+ EGL_RECORDABLE_ANDROID is a boolean indicating whether the config may
+ be used to create an EGLSurface from an ANativeWindow that is a video
+ recorder as indicated by the NATIVE_WINDOW_IS_VIDEO_RECORDER query on
+ the ANativeWindow.
+
+ Section 3.4.1, Querying Configurations, change the last paragraph as follow
+
+ EGLConfigs are not sorted with respect to the parameters
+ EGL_BIND_TO_TEXTURE_RGB, EGL_BIND_TO_TEXTURE_RGBA, EGL_CONFORMANT,
+ EGL_LEVEL, EGL_NATIVE_RENDERABLE, EGL_MAX_SWAP_INTERVAL,
+ EGL_MIN_SWAP_INTERVAL, EGL_RENDERABLE_TYPE, EGL_SURFACE_TYPE,
+ EGL_TRANSPARENT_TYPE, EGL_TRANSPARENT_RED_VALUE,
+ EGL_TRANSPARENT_GREEN_VALUE, EGL_TRANSPARENT_BLUE_VALUE, and
+ EGL_RECORDABLE_ANDROID.
+
+Issues
+
+ 1. Should this functionality be exposed as a new attribute or as a bit in
+ the EGL_SURFACE_TYPE bitfield?
+
+ RESOLVED: It should be a new attribute. It does not make sense to use up a
+ bit in the limit-size bitfield for a platform-specific extension.
+
+ 2. How should the new attribute affect the sorting of EGLConfigs?
+
+ RESOLVED: It should not affect sorting. Some implementations may not have
+ any drawback associated with using a recordable EGLConfig. Such
+ implementations should not have to double-up some of their configs to one sort earlier than .
+ Implementations that do have drawbacks can use the existing caveat
+ mechanism to report this drawback to the client.
+
+Revision History
+
+#1 (Jamie Gennis, July 8, 2011)
+ - Initial draft.
diff --git a/opengl/specs/README b/opengl/specs/README
new file mode 100644
index 0000000..2fa2587
--- /dev/null
+++ b/opengl/specs/README
@@ -0,0 +1,12 @@
+This directory contains OpenGL ES and EGL extension specifications that have
+been or are being defined for Android.
+
+The table below tracks usage of EGL enumerant values that have been reserved
+for use by Android extensions.
+
+ Value Extension
+---------------- ----------------------------------
+0x3140 EGL_ANDROID_image_native_buffer
+0x3141 (unused)
+0x3142 EGL_ANDROID_recordable
+0x3143 - 0x314F (unused)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
index 826ac92..dedbe5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarPolicy.java
@@ -785,7 +785,7 @@
int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
mBluetoothEnabled = state == BluetoothAdapter.STATE_ON;
} else if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
- int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ int state = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE,
BluetoothAdapter.STATE_DISCONNECTED);
if (state == BluetoothAdapter.STATE_CONNECTED) {
iconId = R.drawable.stat_sys_data_bluetooth_connected;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
index 7f6327d..dff0556 100644
--- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java
@@ -1555,9 +1555,12 @@
// We restore the panel if it was last open; we skip it if it
// now is open, to avoid a race condition if the user immediately
// opens it when we are resuming.
- if ((st != null) && !st.isOpen && st.wasLastOpen) {
- st.isInExpandedMode = st.wasLastExpanded;
- openPanel(st, null);
+ if (st != null) {
+ st.applyFrozenState();
+ if (!st.isOpen && st.wasLastOpen) {
+ st.isInExpandedMode = st.wasLastExpanded;
+ openPanel(st, null);
+ }
}
}
}
@@ -2235,6 +2238,11 @@
}
mActionModePopup = null;
}
+
+ PanelFeatureState st = getPanelState(FEATURE_OPTIONS_PANEL, false);
+ if (st != null && st.menu != null) {
+ st.menu.close();
+ }
}
@Override
@@ -3046,46 +3054,33 @@
getIconMenuView(cb); // Need this initialized to know where our offset goes
- boolean init = false;
if (expandedMenuPresenter == null) {
expandedMenuPresenter = new ListMenuPresenter(
com.android.internal.R.layout.list_menu_item_layout,
com.android.internal.R.style.Theme_ExpandedMenu);
expandedMenuPresenter.setCallback(cb);
+ expandedMenuPresenter.setId(com.android.internal.R.id.list_menu_presenter);
menu.addMenuPresenter(expandedMenuPresenter);
- init = true;
}
expandedMenuPresenter.setItemIndexOffset(iconMenuPresenter.getNumActualItemsShown());
MenuView result = expandedMenuPresenter.getMenuView(decorView);
- if (init && frozenMenuState != null) {
- expandedMenuPresenter.restoreHierarchyState(frozenMenuState);
- // Once we initialize the expanded menu we're done with the frozen state
- // since we will have also restored any icon menu state.
- frozenMenuState = null;
- }
-
return result;
}
MenuView getIconMenuView(MenuPresenter.Callback cb) {
if (menu == null) return null;
- boolean init = false;
if (iconMenuPresenter == null) {
iconMenuPresenter = new IconMenuPresenter();
iconMenuPresenter.setCallback(cb);
+ iconMenuPresenter.setId(com.android.internal.R.id.icon_menu_presenter);
menu.addMenuPresenter(iconMenuPresenter);
- init = true;
}
MenuView result = iconMenuPresenter.getMenuView(decorView);
- if (init && frozenMenuState != null) {
- iconMenuPresenter.restoreHierarchyState(frozenMenuState);
- }
-
return result;
}
@@ -3097,12 +3092,7 @@
if (menu != null) {
savedState.menuState = new Bundle();
- if (iconMenuPresenter != null) {
- iconMenuPresenter.saveHierarchyState(savedState.menuState);
- }
- if (expandedMenuPresenter != null) {
- expandedMenuPresenter.saveHierarchyState(savedState.menuState);
- }
+ menu.savePresenterStates(savedState.menuState);
}
return savedState;
@@ -3127,6 +3117,13 @@
decorView = null;
}
+ void applyFrozenState() {
+ if (menu != null && frozenMenuState != null) {
+ menu.restorePresenterStates(frozenMenuState);
+ frozenMenuState = null;
+ }
+ }
+
private static class SavedState implements Parcelable {
int featureId;
boolean isOpen;
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 10b9083..da9b55c 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -3154,11 +3154,9 @@
mLastHoverWindow = NULL;
}
- mWindows.clear();
-
// Loop over new windows and rebuild the necessary window pointers for
// tracking focus and touch.
- mWindows.appendVector(inputWindows);
+ mWindows = inputWindows;
size_t numWindows = mWindows.size();
for (size_t i = 0; i < numWindows; i++) {
@@ -4560,8 +4558,7 @@
split = other.split;
deviceId = other.deviceId;
source = other.source;
- windows.clear();
- windows.appendVector(other.windows);
+ windows = other.windows;
}
void InputDispatcher::TouchState::addOrUpdateWindow(const InputWindow* window,
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 2d55433..18d393f 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -531,10 +531,7 @@
mImeSwitcherNotification.sound = null;
mImeSwitcherNotification.vibrate = null;
Intent intent = new Intent(Settings.ACTION_SHOW_INPUT_METHOD_PICKER);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
- | Intent.FLAG_ACTIVITY_CLEAR_TOP);
- mImeSwitchPendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+ mImeSwitchPendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
mShowOngoingImeSwitcherForPhones = mRes.getBoolean(
com.android.internal.R.bool.show_ongoing_ime_switcher);
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index da1bf83..829df39 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -77,11 +77,15 @@
/** Path to {@code /proc/uid_stat}. */
@Deprecated
- private final File mProcStatsUidstat;
+ private final File mStatsUid;
+ /** Path to {@code /proc/net/dev}. */
+ private final File mStatsIface;
/** Path to {@code /proc/net/xt_qtaguid/stats}. */
- private final File mProcStatsNetfilter;
+ private final File mStatsXtUid;
+ /** Path to {@code /proc/net/xt_qtaguid/iface_stat}. */
+ private final File mStatsXtIface;
- /** {@link #mProcStatsNetfilter} headers. */
+ /** {@link #mStatsXtUid} headers. */
private static final String KEY_IFACE = "iface";
private static final String KEY_TAG_HEX = "acct_tag_hex";
private static final String KEY_UID = "uid_tag_int";
@@ -137,8 +141,10 @@
mContext = context;
mObservers = new ArrayList<INetworkManagementEventObserver>();
- mProcStatsUidstat = new File(procRoot, "uid_stat");
- mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats");
+ mStatsUid = new File(procRoot, "uid_stat");
+ mStatsIface = new File(procRoot, "net/dev");
+ mStatsXtUid = new File(procRoot, "net/xt_qtaguid/stats");
+ mStatsXtIface = new File(procRoot, "net/xt_qtaguid/iface_stat");
if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
return;
@@ -161,9 +167,12 @@
}
// @VisibleForTesting
- public static NetworkManagementService createForTest(Context context, File procRoot) {
+ public static NetworkManagementService createForTest(
+ Context context, File procRoot, boolean bandwidthControlEnabled) {
// TODO: eventually connect with mock netd
- return new NetworkManagementService(context, procRoot);
+ final NetworkManagementService service = new NetworkManagementService(context, procRoot);
+ service.mBandwidthControlEnabled = bandwidthControlEnabled;
+ return service;
}
public void systemReady() {
@@ -930,13 +939,68 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
- final String[] ifaces = listInterfaces();
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), ifaces.length);
+ final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 6);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
- for (String iface : ifaces) {
- final long rx = getInterfaceCounter(iface, true);
- final long tx = getInterfaceCounter(iface, false);
- stats.addEntry(iface, UID_ALL, TAG_NONE, rx, tx);
+ final HashSet<String> activeIfaces = Sets.newHashSet();
+ final ArrayList<String> values = Lists.newArrayList();
+
+ BufferedReader reader = null;
+ try {
+ reader = new BufferedReader(new FileReader(mStatsIface));
+
+ // skip first two header lines
+ reader.readLine();
+ reader.readLine();
+
+ // parse remaining lines
+ String line;
+ while ((line = reader.readLine()) != null) {
+ splitLine(line, values);
+
+ try {
+ entry.iface = values.get(0);
+ entry.uid = UID_ALL;
+ entry.tag = TAG_NONE;
+ entry.rxBytes = Long.parseLong(values.get(1));
+ entry.rxPackets = Long.parseLong(values.get(2));
+ entry.txBytes = Long.parseLong(values.get(9));
+ entry.txPackets = Long.parseLong(values.get(10));
+
+ activeIfaces.add(entry.iface);
+ stats.addValues(entry);
+ } catch (NumberFormatException e) {
+ Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
+ }
+ }
+ } catch (IOException e) {
+ Slog.w(TAG, "problem parsing stats: " + e);
+ } finally {
+ IoUtils.closeQuietly(reader);
+ }
+
+ if (DBG) Slog.d(TAG, "recorded active stats from " + activeIfaces);
+
+ // splice in stats from any disabled ifaces
+ if (mBandwidthControlEnabled) {
+ final HashSet<String> xtIfaces = Sets.newHashSet(fileListWithoutNull(mStatsXtIface));
+ xtIfaces.removeAll(activeIfaces);
+
+ for (String iface : xtIfaces) {
+ final File ifacePath = new File(mStatsXtIface, iface);
+
+ entry.iface = iface;
+ entry.uid = UID_ALL;
+ entry.tag = TAG_NONE;
+ entry.rxBytes = readSingleLongFromFile(new File(ifacePath, "rx_bytes"));
+ entry.rxPackets = readSingleLongFromFile(new File(ifacePath, "rx_packets"));
+ entry.txBytes = readSingleLongFromFile(new File(ifacePath, "tx_bytes"));
+ entry.txPackets = readSingleLongFromFile(new File(ifacePath, "tx_packets"));
+
+ stats.addValues(entry);
+ }
+
+ if (DBG) Slog.d(TAG, "recorded stale stats from " + xtIfaces);
}
return stats;
@@ -1063,13 +1127,15 @@
*/
private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) {
final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
+
final ArrayList<String> keys = Lists.newArrayList();
final ArrayList<String> values = Lists.newArrayList();
final HashMap<String, String> parsed = Maps.newHashMap();
BufferedReader reader = null;
try {
- reader = new BufferedReader(new FileReader(mProcStatsNetfilter));
+ reader = new BufferedReader(new FileReader(mStatsXtUid));
// parse first line as header
String line = reader.readLine();
@@ -1081,15 +1147,16 @@
parseLine(keys, values, parsed);
try {
- final String iface = parsed.get(KEY_IFACE);
- final int tag = NetworkManagementSocketTagger.kernelToTag(
+ // TODO: add rxPackets/txPackets once kernel exports
+ entry.iface = parsed.get(KEY_IFACE);
+ entry.tag = NetworkManagementSocketTagger.kernelToTag(
parsed.get(KEY_TAG_HEX));
- final int uid = Integer.parseInt(parsed.get(KEY_UID));
- final long rx = Long.parseLong(parsed.get(KEY_RX));
- final long tx = Long.parseLong(parsed.get(KEY_TX));
+ entry.uid = Integer.parseInt(parsed.get(KEY_UID));
+ entry.rxBytes = Long.parseLong(parsed.get(KEY_RX));
+ entry.txBytes = Long.parseLong(parsed.get(KEY_TX));
- if (limitUid == UID_ALL || limitUid == uid) {
- stats.addEntry(iface, uid, tag, rx, tx);
+ if (limitUid == UID_ALL || limitUid == entry.uid) {
+ stats.addValues(entry);
}
} catch (NumberFormatException e) {
Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
@@ -1114,19 +1181,27 @@
private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
final String[] knownUids;
if (limitUid == UID_ALL) {
- knownUids = mProcStatsUidstat.list();
+ knownUids = fileListWithoutNull(mStatsUid);
} else {
knownUids = new String[] { String.valueOf(limitUid) };
}
final NetworkStats stats = new NetworkStats(
SystemClock.elapsedRealtime(), knownUids.length);
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
for (String uid : knownUids) {
final int uidInt = Integer.parseInt(uid);
- final File uidPath = new File(mProcStatsUidstat, uid);
- final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
- final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
- stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx);
+ final File uidPath = new File(mStatsUid, uid);
+
+ entry.iface = IFACE_ALL;
+ entry.uid = uidInt;
+ entry.tag = TAG_NONE;
+ entry.rxBytes = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
+ entry.rxPackets = readSingleLongFromFile(new File(uidPath, "tcp_rcv_pkt"));
+ entry.txBytes = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
+ entry.txPackets = readSingleLongFromFile(new File(uidPath, "tcp_snd_pkt"));
+
+ stats.addValues(entry);
}
return stats;
@@ -1197,7 +1272,7 @@
private static void splitLine(String line, ArrayList<String> outSplit) {
outSplit.clear();
- final StringTokenizer t = new StringTokenizer(line);
+ final StringTokenizer t = new StringTokenizer(line, " \t\n\r\f:");
while (t.hasMoreTokens()) {
outSplit.add(t.nextToken());
}
@@ -1232,6 +1307,15 @@
}
}
+ /**
+ * Wrapper for {@link File#list()} that returns empty array instead of
+ * {@code null}.
+ */
+ private static String[] fileListWithoutNull(File file) {
+ final String[] list = file.list();
+ return list != null ? list : new String[0];
+ }
+
public void setDefaultInterfaceForDns(String iface) throws IllegalStateException {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index d5e8730..8501163 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -208,6 +208,12 @@
// before we decide it's never going to come up for real.
static final int PROC_START_TIMEOUT = 10*1000;
+ // How long we wait for a launched process to attach to the activity manager
+ // before we decide it's never going to come up for real, when the process was
+ // started with a wrapper for instrumentation (such as Valgrind) because it
+ // could take much longer than usual.
+ static final int PROC_START_TIMEOUT_WITH_WRAPPER = 300*1000;
+
// How long to wait after going idle before forcing apps to GC.
static final int GC_TIMEOUT = 5*1000;
@@ -1950,9 +1956,13 @@
if ("1".equals(SystemProperties.get("debug.assert"))) {
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
}
- int pid = Process.start("android.app.ActivityThread",
+
+ // Start the process. It will either succeed and return a result containing
+ // the PID of the new process, or else throw a RuntimeException.
+ Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread",
app.processName, uid, uid, gids, debugFlags,
app.info.targetSdkVersion, null);
+
BatteryStatsImpl bs = app.batteryStats.getBatteryStats();
synchronized (bs) {
if (bs.isOnBattery()) {
@@ -1960,12 +1970,12 @@
}
}
- EventLog.writeEvent(EventLogTags.AM_PROC_START, pid, uid,
+ EventLog.writeEvent(EventLogTags.AM_PROC_START, startResult.pid, uid,
app.processName, hostingType,
hostingNameStr != null ? hostingNameStr : "");
if (app.persistent) {
- Watchdog.getInstance().processStarted(app.processName, pid);
+ Watchdog.getInstance().processStarted(app.processName, startResult.pid);
}
StringBuilder buf = mStringBuilder;
@@ -1979,7 +1989,7 @@
buf.append(hostingNameStr);
}
buf.append(": pid=");
- buf.append(pid);
+ buf.append(startResult.pid);
buf.append(" uid=");
buf.append(uid);
buf.append(" gids={");
@@ -1992,21 +2002,15 @@
}
buf.append("}");
Slog.i(TAG, buf.toString());
- if (pid > 0) {
- app.pid = pid;
- app.removed = false;
- synchronized (mPidsSelfLocked) {
- this.mPidsSelfLocked.put(pid, app);
- Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
- msg.obj = app;
- mHandler.sendMessageDelayed(msg, PROC_START_TIMEOUT);
- }
- } else {
- app.pid = 0;
- RuntimeException e = new RuntimeException(
- "Failure starting process " + app.processName
- + ": returned pid=" + pid);
- Slog.e(TAG, e.getMessage(), e);
+ app.pid = startResult.pid;
+ app.usingWrapper = startResult.usingWrapper;
+ app.removed = false;
+ synchronized (mPidsSelfLocked) {
+ this.mPidsSelfLocked.put(startResult.pid, app);
+ Message msg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);
+ msg.obj = app;
+ mHandler.sendMessageDelayed(msg, startResult.usingWrapper
+ ? PROC_START_TIMEOUT_WITH_WRAPPER : PROC_START_TIMEOUT);
}
} catch (RuntimeException e) {
// XXX do better error recovery.
diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java
index 090e26b..73ffafb 100644
--- a/services/java/com/android/server/am/ActivityRecord.java
+++ b/services/java/com/android/server/am/ActivityRecord.java
@@ -658,12 +658,12 @@
public long getKeyDispatchingTimeout() {
synchronized(service) {
ActivityRecord r = getWaitingHistoryRecordLocked();
- if (r == null || r.app == null
- || r.app.instrumentationClass == null) {
- return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
+ if (r != null && r.app != null
+ && (r.app.instrumentationClass != null || r.app.usingWrapper)) {
+ return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
}
-
- return ActivityManagerService.INSTRUMENTATION_KEY_DISPATCHING_TIMEOUT;
+
+ return ActivityManagerService.KEY_DISPATCHING_TIMEOUT;
}
}
diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java
index 3968f66..da83e7d 100644
--- a/services/java/com/android/server/am/ProcessRecord.java
+++ b/services/java/com/android/server/am/ProcessRecord.java
@@ -78,6 +78,7 @@
IInstrumentationWatcher instrumentationWatcher; // who is waiting
Bundle instrumentationArguments;// as given to us
ComponentName instrumentationResultClass;// copy of instrumentationClass
+ boolean usingWrapper; // Set to true when process was launched with a wrapper attached
BroadcastRecord curReceiver;// receiver currently running in the app
long lastWakeTime; // How long proc held wake lock at last check
long lastCpuTime; // How long proc has run CPU at last check
diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java
index 6e8f248..12c8ccf 100644
--- a/services/java/com/android/server/am/UsageStatsService.java
+++ b/services/java/com/android/server/am/UsageStatsService.java
@@ -63,7 +63,7 @@
private static final String TAG = "UsageStats";
// Current on-disk Parcel version
- private static final int VERSION = 1005;
+ private static final int VERSION = 1006;
private static final int CHECKIN_VERSION = 4;
@@ -145,6 +145,8 @@
final HashMap<String, TimeStats> mLaunchTimes
= new HashMap<String, TimeStats>();
int mLaunchCount;
+ final HashMap<String, Long> mLastResumeTimes
+ = new HashMap<String, Long>();
long mUsageTime;
long mPausedTime;
long mResumedTime;
@@ -160,20 +162,28 @@
if (localLOGV) Slog.v(TAG, "Launch count: " + mLaunchCount
+ ", Usage time:" + mUsageTime);
- final int N = in.readInt();
- if (localLOGV) Slog.v(TAG, "Reading comps: " + N);
- for (int i=0; i<N; i++) {
+ final int numTimeStats = in.readInt();
+ if (localLOGV) Slog.v(TAG, "Reading comps: " + numTimeStats);
+ for (int i=0; i<numTimeStats; i++) {
String comp = in.readString();
if (localLOGV) Slog.v(TAG, "Component: " + comp);
TimeStats times = new TimeStats(in);
mLaunchTimes.put(comp, times);
}
+ final int numResumeTimes = in.readInt();
+ if (localLOGV) Slog.v(TAG, "Reading last resume times: " + numResumeTimes);
+ for (int i=0; i<numResumeTimes; i++) {
+ String comp = in.readString();
+ if (localLOGV) Slog.v(TAG, "Component: " + comp);
+ mLastResumeTimes.put(comp, in.readLong());
+ }
}
-
- void updateResume(boolean launched) {
+
+ void updateResume(String comp, boolean launched) {
if (launched) {
mLaunchCount ++;
}
+ mLastResumeTimes.put(comp, System.currentTimeMillis());
mResumedTime = SystemClock.elapsedRealtime();
}
@@ -203,20 +213,29 @@
void writeToParcel(Parcel out) {
out.writeInt(mLaunchCount);
out.writeLong(mUsageTime);
- final int N = mLaunchTimes.size();
- out.writeInt(N);
- if (N > 0) {
+ final int numTimeStats = mLaunchTimes.size();
+ out.writeInt(numTimeStats);
+ if (numTimeStats > 0) {
for (Map.Entry<String, TimeStats> ent : mLaunchTimes.entrySet()) {
out.writeString(ent.getKey());
TimeStats times = ent.getValue();
times.writeToParcel(out);
}
}
+ final int numResumeTimes = mLastResumeTimes.size();
+ out.writeInt(numResumeTimes);
+ if (numResumeTimes > 0) {
+ for (Map.Entry<String, Long> ent : mLastResumeTimes.entrySet()) {
+ out.writeString(ent.getKey());
+ out.writeLong(ent.getValue());
+ }
+ }
}
void clear() {
mLaunchTimes.clear();
mLaunchCount = 0;
+ mLastResumeTimes.clear();
mUsageTime = 0;
}
}
@@ -546,7 +565,7 @@
pus = new PkgUsageStatsExtended();
mStats.put(pkgName, pus);
}
- pus.updateResume(!samePackage);
+ pus.updateResume(mLastResumedComp, !samePackage);
if (!sameComp) {
pus.addLaunchCount(mLastResumedComp);
}
@@ -624,7 +643,8 @@
if (pus == null) {
return null;
}
- return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime);
+ return new PkgUsageStats(pkgName, pus.mLaunchCount, pus.mUsageTime,
+ pus.mLastResumeTimes);
}
}
@@ -641,7 +661,8 @@
int i = 0;
for (String key: keys) {
PkgUsageStatsExtended pus = mStats.get(key);
- retArr[i] = new PkgUsageStats(key, pus.mLaunchCount, pus.mUsageTime);
+ retArr[i] = new PkgUsageStats(key, pus.mLaunchCount, pus.mUsageTime,
+ pus.mLastResumeTimes);
i++;
}
return retArr;
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index 6bb7949..d7d4b03 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -221,8 +221,6 @@
}
public void interfaceLinkStateChanged(String iface, boolean up) {
- if (DEBUG) Log.d(TAG, "interfaceLinkStateChanged " + iface + ", " + up);
- interfaceStatusChanged(iface, up);
}
private boolean isUsb(String iface) {
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 1ab570a..86b3d36 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -251,19 +251,18 @@
public UsbHandler() {
try {
+ // persist.sys.usb.config should never be unset. But if it is, set it to "adb"
+ // so we have a chance of debugging what happened.
+ mDefaultFunctions = SystemProperties.get("persist.sys.usb.config", "adb");
// sanity check the sys.usb.config system property
// this may be necessary if we crashed while switching USB configurations
String config = SystemProperties.get("sys.usb.config", "none");
- if (config.equals("none")) {
- String persistConfig = SystemProperties.get("persist.sys.usb.config", "none");
- Slog.w(TAG, "resetting config to persistent property: " + persistConfig);
- SystemProperties.set("sys.usb.config", persistConfig);
+ if (!config.equals(mDefaultFunctions)) {
+ Slog.w(TAG, "resetting config to persistent property: " + mDefaultFunctions);
+ SystemProperties.set("sys.usb.config", mDefaultFunctions);
}
- // Read initial USB state
- mCurrentFunctions = FileUtils.readTextFile(
- new File(FUNCTIONS_PATH), 0, null).trim();
- mDefaultFunctions = mCurrentFunctions;
+ mCurrentFunctions = mDefaultFunctions;
String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
updateState(state);
mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
@@ -621,6 +620,16 @@
pw.println(" mConnected: " + mConnected);
pw.println(" mConfigured: " + mConfigured);
pw.println(" mCurrentAccessory: " + mCurrentAccessory);
+ try {
+ pw.println(" Kernel state: "
+ + FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim());
+ pw.println(" Kernel function list: "
+ + FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim());
+ pw.println(" Mass storage backing file: "
+ + FileUtils.readTextFile(new File(MASS_STORAGE_FILE_PATH), 0, null).trim());
+ } catch (IOException e) {
+ pw.println("IOException: " + e);
+ }
}
}
diff --git a/services/tests/servicestests/res/raw/net_dev_typical b/services/tests/servicestests/res/raw/net_dev_typical
new file mode 100644
index 0000000..290bf03
--- /dev/null
+++ b/services/tests/servicestests/res/raw/net_dev_typical
@@ -0,0 +1,8 @@
+Inter-| Receive | Transmit
+ face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
+ lo: 8308 116 0 0 0 0 0 0 8308 116 0 0 0 0 0 0
+rmnet0: 1507570 2205 0 0 0 0 0 0 489339 2237 0 0 0 0 0 0
+ ifb0: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0
+ ifb1: 52454 151 0 151 0 0 0 0 0 0 0 0 0 0 0 0
+ sit0: 0 0 0 0 0 0 0 0 0 0 148 0 0 0 0 0
+ip6tnl0: 0 0 0 0 0 0 0 0 0 0 151 151 0 0 0 0
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
index ac7cb5a..56ef995a 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -16,6 +16,8 @@
package com.android.server;
+import static android.net.NetworkStats.TAG_NONE;
+import static android.net.NetworkStats.UID_ALL;
import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
import static com.android.server.NetworkManagementSocketTagger.tagToKernel;
@@ -25,9 +27,11 @@
import android.test.suitebuilder.annotation.LargeTest;
import com.android.frameworks.servicestests.R;
+import com.google.common.io.Files;
import java.io.File;
import java.io.FileOutputStream;
+import java.io.FileWriter;
import java.io.InputStream;
import java.io.OutputStream;
@@ -46,14 +50,23 @@
public void setUp() throws Exception {
super.setUp();
- mTestProc = getContext().getFilesDir();
- mService = NetworkManagementService.createForTest(mContext, mTestProc);
+ final File canonicalFilesDir = getContext().getFilesDir().getCanonicalFile();
+ mTestProc = new File(canonicalFilesDir, "proc");
+ if (mTestProc.exists()) {
+ Files.deleteRecursively(mTestProc);
+ }
+
+ mService = NetworkManagementService.createForTest(mContext, mTestProc, true);
}
@Override
public void tearDown() throws Exception {
mService = null;
+ if (mTestProc.exists()) {
+ Files.deleteRecursively(mTestProc);
+ }
+
super.tearDown();
}
@@ -61,7 +74,7 @@
stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats"));
final NetworkStats stats = mService.getNetworkStatsDetail();
- assertEquals(31, stats.size);
+ assertEquals(31, stats.size());
assertStatsEntry(stats, "wlan0", 0, 0, 14615L, 4270L);
assertStatsEntry(stats, "wlan0", 10004, 0, 333821L, 53558L);
assertStatsEntry(stats, "wlan0", 10004, 1947740890, 18725L, 1066L);
@@ -73,11 +86,37 @@
stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats"));
final NetworkStats stats = mService.getNetworkStatsDetail();
- assertEquals(2, stats.size);
+ assertEquals(2, stats.size());
assertStatsEntry(stats, "test0", 1000, 0, 1024L, 2048L);
assertStatsEntry(stats, "test0", 1000, 0xF00D, 512L, 512L);
}
+ public void testNetworkStatsSummary() throws Exception {
+ stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev"));
+
+ final NetworkStats stats = mService.getNetworkStatsSummary();
+ assertEquals(6, stats.size());
+ assertStatsEntry(stats, "lo", UID_ALL, TAG_NONE, 8308L, 8308L);
+ assertStatsEntry(stats, "rmnet0", UID_ALL, TAG_NONE, 1507570L, 489339L);
+ assertStatsEntry(stats, "ifb0", UID_ALL, TAG_NONE, 52454L, 0L);
+ assertStatsEntry(stats, "ifb1", UID_ALL, TAG_NONE, 52454L, 0L);
+ assertStatsEntry(stats, "sit0", UID_ALL, TAG_NONE, 0L, 0L);
+ assertStatsEntry(stats, "ip6tnl0", UID_ALL, TAG_NONE, 0L, 0L);
+ }
+
+ public void testNetworkStatsSummaryDown() throws Exception {
+ stageFile(R.raw.net_dev_typical, new File(mTestProc, "net/dev"));
+ stageLong(1024L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_bytes"));
+ stageLong(128L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/rx_packets"));
+ stageLong(2048L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_bytes"));
+ stageLong(256L, new File(mTestProc, "net/xt_qtaguid/iface_stat/wlan0/tx_packets"));
+
+ final NetworkStats stats = mService.getNetworkStatsSummary();
+ assertEquals(7, stats.size());
+ assertStatsEntry(stats, "rmnet0", UID_ALL, TAG_NONE, 1507570L, 489339L);
+ assertStatsEntry(stats, "wlan0", UID_ALL, TAG_NONE, 1024L, 2048L);
+ }
+
public void testKernelTags() throws Exception {
assertEquals("0", tagToKernel(0x0));
assertEquals("214748364800", tagToKernel(0x32));
@@ -90,7 +129,6 @@
assertEquals(2147483647, kernelToTag("0x7fffffff00000000"));
assertEquals(0, kernelToTag("0x0000000000000000"));
assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000"));
-
}
/**
@@ -111,11 +149,23 @@
}
}
+ private void stageLong(long value, File file) throws Exception {
+ new File(file.getParent()).mkdirs();
+ FileWriter out = null;
+ try {
+ out = new FileWriter(file);
+ out.write(Long.toString(value));
+ } finally {
+ IoUtils.closeQuietly(out);
+ }
+ }
+
private static void assertStatsEntry(
- NetworkStats stats, String iface, int uid, int tag, long rx, long tx) {
+ NetworkStats stats, String iface, int uid, int tag, long rxBytes, long txBytes) {
final int i = stats.findIndex(iface, uid, tag);
- assertEquals(rx, stats.rx[i]);
- assertEquals(tx, stats.tx[i]);
+ final NetworkStats.Entry entry = stats.getValues(i, null);
+ assertEquals(rxBytes, entry.rxBytes);
+ assertEquals(txBytes, entry.txBytes);
}
}