Merge "Handle rotation gracefully in the backup/restore confirmation UI"
diff --git a/api/current.txt b/api/current.txt
index bdc695f..035da89 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18185,13 +18185,21 @@
public abstract class SpellCheckerService extends android.app.Service {
ctor public SpellCheckerService();
- method public void cancel();
- method public abstract android.view.textservice.SuggestionsInfo getSuggestions(android.view.textservice.TextInfo, int, java.lang.String);
- method public android.view.textservice.SuggestionsInfo[] getSuggestionsMultiple(android.view.textservice.TextInfo[], java.lang.String, int, boolean);
+ method public abstract android.service.textservice.SpellCheckerService.Session createSession();
method public final android.os.IBinder onBind(android.content.Intent);
field public static final java.lang.String SERVICE_INTERFACE = "android.service.textservice.SpellCheckerService";
}
+ public abstract class SpellCheckerService.Session {
+ ctor public SpellCheckerService.Session();
+ method public android.os.Bundle getBundle();
+ method public java.lang.String getLocale();
+ method public void onCancel();
+ method public abstract void onCreate();
+ method public abstract android.view.textservice.SuggestionsInfo onGetSuggestions(android.view.textservice.TextInfo, int);
+ method public android.view.textservice.SuggestionsInfo[] onGetSuggestionsMultiple(android.view.textservice.TextInfo[], int, boolean);
+ }
+
}
package android.service.wallpaper {
@@ -24308,7 +24316,7 @@
}
public final class TextServicesManager {
- method public android.view.textservice.SpellCheckerSession newSpellCheckerSession(java.util.Locale, android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
+ method public android.view.textservice.SpellCheckerSession newSpellCheckerSession(android.os.Bundle, java.util.Locale, android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
}
}
diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java
index 5fe3644..57e0583 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -32,10 +32,9 @@
/**
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
- * running after that delay elapses. Note that the animation does not start synchronously with
- * this call, because all animation events are posted to a central timing loop so that animation
- * times are all synchronized on a single timing pulse on the UI thread. So the animation will
- * start the next time that event handler processes events.
+ * running after that delay elapses. A non-delayed animation will have its initial
+ * value(s) set immediately, followed by calls to
+ * {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator.
*
* <p>The animation started by calling this method will be run on the thread that called
* this method. This thread should have a Looper on it (a runtime exception will be thrown if
diff --git a/core/java/android/animation/AnimatorSet.java b/core/java/android/animation/AnimatorSet.java
index 61a12ee..ce3dd13 100644
--- a/core/java/android/animation/AnimatorSet.java
+++ b/core/java/android/animation/AnimatorSet.java
@@ -349,7 +349,8 @@
return true;
}
}
- return false;
+ // Also return true if we're currently running the startDelay animator
+ return (mDelayAnim != null && mDelayAnim.isRunning());
}
/**
@@ -487,7 +488,6 @@
mPlayingSet.add(node.animation);
}
} else {
- // TODO: Need to cancel out of the delay appropriately
mDelayAnim = ValueAnimator.ofFloat(0f, 1f);
mDelayAnim.setDuration(mStartDelay);
mDelayAnim.addListener(new AnimatorListenerAdapter() {
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 90d676e..c22306a 100755
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -186,6 +186,16 @@
int mPlayingState = STOPPED;
/**
+ * Additional playing state to indicate whether an animator has been start()'d. There is
+ * some lag between a call to start() and the first animation frame. We should still note
+ * that the animation has been started, even if it's first animation frame has not yet
+ * happened, and reflect that state in isRunning().
+ * Note that delayed animations are different: they are not started until their first
+ * animation frame, which occurs after their delay elapses.
+ */
+ private boolean mStarted = false;
+
+ /**
* Flag that denotes whether the animation is set up and ready to go. Used to
* set up animation that has not yet been started.
*/
@@ -618,6 +628,7 @@
for (int i = 0; i < numReadyAnims; ++i) {
ValueAnimator anim = readyAnims.get(i);
anim.startAnimation();
+ anim.mStarted = true;
delayedAnims.remove(anim);
}
readyAnims.clear();
@@ -908,6 +919,7 @@
// This sets the initial value of the animation, prior to actually starting it running
setCurrentPlayTime(getCurrentPlayTime());
mPlayingState = STOPPED;
+ mStarted = true;
if (mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
@@ -937,7 +949,8 @@
// to run
if (mPlayingState != STOPPED || sPendingAnimations.get().contains(this) ||
sDelayedAnims.get().contains(this)) {
- if (mListeners != null) {
+ // Only notify listeners if the animator has actually started
+ if (mStarted && mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
for (AnimatorListener listener : tmpListeners) {
@@ -969,7 +982,7 @@
@Override
public boolean isRunning() {
- return (mPlayingState == RUNNING);
+ return (mPlayingState == RUNNING || mStarted);
}
/**
@@ -1000,7 +1013,7 @@
sPendingAnimations.get().remove(this);
sDelayedAnims.get().remove(this);
mPlayingState = STOPPED;
- if (mListeners != null) {
+ if (mStarted && mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
@@ -1008,6 +1021,7 @@
tmpListeners.get(i).onAnimationEnd(this);
}
}
+ mStarted = false;
}
/**
diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java
index f2d84eb..81738f3 100644
--- a/core/java/android/net/DnsPinger.java
+++ b/core/java/android/net/DnsPinger.java
@@ -17,20 +17,27 @@
package android.net;
import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.NetworkUtils;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Slog;
+import com.android.internal.util.Protocol;
+
+import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketTimeoutException;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
/**
* Performs a simple DNS "ping" by sending a "server status" query packet to the
@@ -40,42 +47,174 @@
* API may not differentiate between a time out and a failure lookup (which we
* really care about).
* <p>
- * TODO : More general API. Socket does not bind to specified connection type
- * TODO : Choice of DNS query location - current looks up www.android.com
*
* @hide
*/
-public final class DnsPinger {
+public final class DnsPinger extends Handler {
private static final boolean V = true;
- /** Number of bytes for the query */
- private static final int DNS_QUERY_BASE_SIZE = 32;
-
- /** The DNS port */
+ private static final int RECEIVE_POLL_INTERVAL_MS = 30;
private static final int DNS_PORT = 53;
+ /** Short socket timeout so we don't block one any 'receive' call */
+ private static final int SOCKET_TIMEOUT_MS = 1;
+
/** Used to generate IDs */
- private static Random sRandom = new Random();
+ private static final Random sRandom = new Random();
+ private static final AtomicInteger sCounter = new AtomicInteger();
private ConnectivityManager mConnectivityManager = null;
- private Context mContext;
- private int mConnectionType;
- private InetAddress mDefaultDns;
-
+ private final Context mContext;
+ private final int mConnectionType;
+ private final Handler mTarget;
+ private final InetAddress mDefaultDns;
private String TAG;
+ private static final int BASE = Protocol.BASE_DNS_PINGER;
+
/**
- * @param connectionType The connection type from {@link ConnectivityManager}
+ * Async response packet for dns pings.
+ * arg1 is the ID of the ping, also returned by {@link #pingDnsAsync(InetAddress, int, int)}
+ * arg2 is the delay, or is negative on error.
*/
- public DnsPinger(String TAG, Context context, int connectionType) {
+ public static final int DNS_PING_RESULT = BASE;
+ /** An error code for a {@link #DNS_PING_RESULT} packet */
+ public static final int TIMEOUT = -1;
+ /** An error code for a {@link #DNS_PING_RESULT} packet */
+ public static final int SOCKET_EXCEPTION = -2;
+
+ /**
+ * Send a new ping via a socket. arg1 is ID, arg2 is timeout, obj is InetAddress to ping
+ */
+ private static final int ACTION_PING_DNS = BASE + 1;
+ private static final int ACTION_LISTEN_FOR_RESPONSE = BASE + 2;
+ private static final int ACTION_CANCEL_ALL_PINGS = BASE + 3;
+
+ private List<ActivePing> mActivePings = new ArrayList<ActivePing>();
+ private int mEventCounter;
+
+ private class ActivePing {
+ DatagramSocket socket;
+ int internalId;
+ short packetId;
+ int timeout;
+ Integer result;
+ long start = SystemClock.elapsedRealtime();
+ }
+
+ public DnsPinger(Context context, String TAG, Looper looper,
+ Handler target, int connectionType) {
+ super(looper);
+ this.TAG = TAG;
mContext = context;
+ mTarget = target;
mConnectionType = connectionType;
if (!ConnectivityManager.isNetworkTypeValid(connectionType)) {
- Slog.e(TAG, "Invalid connectionType in constructor: " + connectionType);
+ throw new IllegalArgumentException("Invalid connectionType in constructor: "
+ + connectionType);
}
- this.TAG = TAG;
-
mDefaultDns = getDefaultDns();
+ mEventCounter = 0;
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ACTION_PING_DNS:
+ try {
+ ActivePing newActivePing = new ActivePing();
+ InetAddress dnsAddress = (InetAddress) msg.obj;
+ newActivePing.internalId = msg.arg1;
+ newActivePing.timeout = msg.arg2;
+ newActivePing.socket = new DatagramSocket();
+ // Set some socket properties
+ newActivePing.socket.setSoTimeout(SOCKET_TIMEOUT_MS);
+
+ // Try to bind but continue ping if bind fails
+ try {
+ newActivePing.socket.setNetworkInterface(NetworkInterface.getByName(
+ getCurrentLinkProperties().getInterfaceName()));
+ } catch (Exception e) {
+ Slog.w(TAG,"sendDnsPing::Error binding to socket", e);
+ }
+
+ newActivePing.packetId = (short) sRandom.nextInt();
+ byte[] buf = mDnsQuery.clone();
+ buf[0] = (byte) (newActivePing.packetId >> 8);
+ buf[1] = (byte) newActivePing.packetId;
+
+ // Send the DNS query
+ DatagramPacket packet = new DatagramPacket(buf,
+ buf.length, dnsAddress, DNS_PORT);
+ if (V) {
+ Slog.v(TAG, "Sending a ping to " + dnsAddress.getHostAddress()
+ + " with ID " + newActivePing.packetId + ".");
+ }
+
+ newActivePing.socket.send(packet);
+ mActivePings.add(newActivePing);
+ mEventCounter++;
+ sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
+ RECEIVE_POLL_INTERVAL_MS);
+ } catch (IOException e) {
+ sendResponse((short) msg.arg1, SOCKET_EXCEPTION);
+ }
+ break;
+ case ACTION_LISTEN_FOR_RESPONSE:
+ if (msg.arg1 != mEventCounter) {
+ break;
+ }
+ for (ActivePing curPing : mActivePings) {
+ try {
+ /** Each socket will block for {@link #SOCKET_TIMEOUT_MS} in receive() */
+ byte[] responseBuf = new byte[2];
+ DatagramPacket replyPacket = new DatagramPacket(responseBuf, 2);
+ curPing.socket.receive(replyPacket);
+ // Check that ID field matches (we're throwing out the rest of the packet)
+ if (responseBuf[0] == (byte) (curPing.packetId >> 8) &&
+ responseBuf[1] == (byte) curPing.packetId) {
+ curPing.result =
+ (int) (SystemClock.elapsedRealtime() - curPing.start);
+ } else {
+ if (V) {
+ Slog.v(TAG, "response ID didn't match, ignoring packet");
+ }
+ }
+ } catch (SocketTimeoutException e) {
+ // A timeout here doesn't mean anything - squelsh this exception
+ } catch (Exception e) {
+ if (V) {
+ Slog.v(TAG, "DnsPinger.pingDns got socket exception: ", e);
+ }
+ curPing.result = SOCKET_EXCEPTION;
+ }
+ }
+ Iterator<ActivePing> iter = mActivePings.iterator();
+ while (iter.hasNext()) {
+ ActivePing curPing = iter.next();
+ if (curPing.result != null) {
+ sendResponse(curPing.internalId, curPing.result);
+ curPing.socket.close();
+ iter.remove();
+ } else if (SystemClock.elapsedRealtime() >
+ curPing.start + curPing.timeout) {
+ sendResponse(curPing.internalId, TIMEOUT);
+ curPing.socket.close();
+ iter.remove();
+ }
+ }
+ if (!mActivePings.isEmpty()) {
+ sendMessageDelayed(obtainMessage(ACTION_LISTEN_FOR_RESPONSE, mEventCounter, 0),
+ RECEIVE_POLL_INTERVAL_MS);
+ }
+ break;
+ case ACTION_CANCEL_ALL_PINGS:
+ for (ActivePing activePing : mActivePings)
+ activePing.socket.close();
+ mActivePings.clear();
+ removeMessages(ACTION_PING_DNS);
+ break;
+ }
}
/**
@@ -99,6 +238,30 @@
return dnses.iterator().next();
}
+ /**
+ * Send a ping. The response will come via a {@link #DNS_PING_RESULT} to the handler
+ * specified at creation.
+ * @param dns address of dns server to ping
+ * @param timeout timeout for ping
+ * @return an ID field, which will also be included in the {@link #DNS_PING_RESULT} message.
+ */
+ public int pingDnsAsync(InetAddress dns, int timeout, int delay) {
+ int id = sCounter.incrementAndGet();
+ sendMessageDelayed(obtainMessage(ACTION_PING_DNS, id, timeout, dns), delay);
+ return id;
+ }
+
+ public void cancelPings() {
+ obtainMessage(ACTION_CANCEL_ALL_PINGS).sendToTarget();
+ }
+
+ private void sendResponse(int internalId, int responseVal) {
+ if(V) {
+ Slog.v(TAG, "Responding with id " + internalId + " and val " + responseVal);
+ }
+ mTarget.sendMessage(obtainMessage(DNS_PING_RESULT, internalId, responseVal));
+ }
+
private LinkProperties getCurrentLinkProperties() {
if (mConnectivityManager == null) {
mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
@@ -123,106 +286,18 @@
}
}
- /**
- * @return time to response. Negative value on error.
- */
- public long pingDns(InetAddress dnsAddress, int timeout) {
- DatagramSocket socket = null;
- try {
- socket = new DatagramSocket();
-
- // Set some socket properties
- socket.setSoTimeout(timeout);
-
- // Try to bind but continue ping if bind fails
- try {
- socket.setNetworkInterface(NetworkInterface.getByName(
- getCurrentLinkProperties().getInterfaceName()));
- } catch (Exception e) {
- Slog.d(TAG,"pingDns::Error binding to socket", e);
- }
-
- byte[] buf = constructQuery();
-
- // Send the DNS query
-
- DatagramPacket packet = new DatagramPacket(buf,
- buf.length, dnsAddress, DNS_PORT);
- long start = SystemClock.elapsedRealtime();
- socket.send(packet);
-
- // Wait for reply (blocks for the above timeout)
- DatagramPacket replyPacket = new DatagramPacket(buf, buf.length);
- socket.receive(replyPacket);
-
- // If a timeout occurred, an exception would have been thrown. We
- // got a reply!
- return SystemClock.elapsedRealtime() - start;
-
- } catch (SocketTimeoutException e) {
- // Squelch this exception.
- return -1;
- } catch (Exception e) {
- if (V) {
- Slog.v(TAG, "DnsPinger.pingDns got socket exception: ", e);
- }
- return -2;
- } finally {
- if (socket != null) {
- socket.close();
- }
- }
-
- }
-
- /**
- * @return google.com DNS query packet
- */
- private static byte[] constructQuery() {
- byte[] buf = new byte[DNS_QUERY_BASE_SIZE];
-
- // [0-1] bytes are an ID, generate random ID for this query
- buf[0] = (byte) sRandom.nextInt(256);
- buf[1] = (byte) sRandom.nextInt(256);
-
- // [2-3] bytes are for flags.
- buf[2] = 0x01; // Recursion desired
-
- // [4-5] bytes are for number of queries (QCOUNT)
- buf[5] = 0x01;
-
- // [6-7] [8-9] [10-11] are all counts of other fields we don't use
-
- // [12-15] for www
- writeString(buf, 12, "www");
-
- // [16-22] for google
- writeString(buf, 16, "google");
-
- // [23-26] for com
- writeString(buf, 23, "com");
-
- // [27] is a null byte terminator byte for the url
-
- // [28-29] bytes are for QTYPE, set to 1 = A (host address)
- buf[29] = 0x01;
-
- // [30-31] bytes are for QCLASS, set to 1 = IN (internet)
- buf[31] = 0x01;
-
- return buf;
- }
-
- /**
- * Writes the string's length and its contents to the buffer
- */
- private static void writeString(byte[] buf, int startPos, String string) {
- int pos = startPos;
-
- // Write the length first
- buf[pos++] = (byte) string.length();
- for (int i = 0; i < string.length(); i++) {
- buf[pos++] = (byte) string.charAt(i);
- }
- }
+ private static final byte[] mDnsQuery = new byte[] {
+ 0, 0, // [0-1] is for ID (will set each time)
+ 0, 0, // [2-3] are flags. Set byte[2] = 1 for recursion desired (RD) on. Currently off.
+ 0, 1, // [4-5] bytes are for number of queries (QCOUNT)
+ 0, 0, // [6-7] unused count field for dns response packets
+ 0, 0, // [8-9] unused count field for dns response packets
+ 0, 0, // [10-11] unused count field for dns response packets
+ 3, 'w', 'w', 'w',
+ 6, 'g', 'o', 'o', 'g', 'l', 'e',
+ 3, 'c', 'o', 'm',
+ 0, // null terminator of address (also called empty TLD)
+ 0, 1, // QTYPE, set to 1 = A (host address)
+ 0, 1 // QCLASS, set to 1 = IN (internet)
+ };
}
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index c41d182..b65506c 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -24,9 +24,9 @@
interface INetworkStatsService {
/** Return historical network layer stats for traffic that matches template. */
- NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template);
+ NetworkStatsHistory getHistoryForNetwork(in NetworkTemplate template, int fields);
/** Return historical network layer stats for specific UID traffic that matches template. */
- NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int tag);
+ NetworkStatsHistory getHistoryForUid(in NetworkTemplate template, int uid, int tag, int fields);
/** Return network layer usage summary for traffic that matches template. */
NetworkStats getSummaryForNetwork(in NetworkTemplate template, long start, long end);
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 1e9d813..9d253c7 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -189,9 +189,10 @@
*/
public static void snapToCycleDay(Time time, int cycleDay) {
if (cycleDay > time.getActualMaximum(MONTH_DAY)) {
- // cycle day isn't valid this month; snap to 1st of next month
+ // cycle day isn't valid this month; snap to last second of month
time.month += 1;
time.monthDay = 1;
+ time.second = -1;
} else {
time.monthDay = cycleDay;
}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 0e8e7fc..f2fcb8f 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -58,7 +58,7 @@
private long[] rxPackets;
private long[] txBytes;
private long[] txPackets;
- private int[] operations;
+ private long[] operations;
public static class Entry {
public String iface;
@@ -68,13 +68,18 @@
public long rxPackets;
public long txBytes;
public long txPackets;
- public int operations;
+ public long operations;
public Entry() {
+ this(IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
+ }
+
+ public Entry(long rxBytes, long rxPackets, long txBytes, long txPackets, long operations) {
+ this(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, rxPackets, txBytes, txPackets, operations);
}
public Entry(String iface, int uid, int tag, long rxBytes, long rxPackets, long txBytes,
- long txPackets, int operations) {
+ long txPackets, long operations) {
this.iface = iface;
this.uid = uid;
this.tag = tag;
@@ -96,7 +101,7 @@
this.rxPackets = new long[initialSize];
this.txBytes = new long[initialSize];
this.txPackets = new long[initialSize];
- this.operations = new int[initialSize];
+ this.operations = new long[initialSize];
}
public NetworkStats(Parcel parcel) {
@@ -109,7 +114,7 @@
rxPackets = parcel.createLongArray();
txBytes = parcel.createLongArray();
txPackets = parcel.createLongArray();
- operations = parcel.createIntArray();
+ operations = parcel.createLongArray();
}
/** {@inheritDoc} */
@@ -123,16 +128,16 @@
dest.writeLongArray(rxPackets);
dest.writeLongArray(txBytes);
dest.writeLongArray(txPackets);
- dest.writeIntArray(operations);
+ dest.writeLongArray(operations);
}
public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
long txBytes, long txPackets) {
- return addValues(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, 0);
+ return addValues(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, 0L);
}
public NetworkStats addValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
- long txBytes, long txPackets, int operations) {
+ long txBytes, long txPackets, long operations) {
return addValues(
new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
}
@@ -197,7 +202,7 @@
}
public NetworkStats combineValues(String iface, int uid, int tag, long rxBytes, long rxPackets,
- long txBytes, long txPackets, int operations) {
+ long txBytes, long txPackets, long operations) {
return combineValues(
new Entry(iface, uid, tag, rxBytes, rxPackets, txBytes, txPackets, operations));
}
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index 7a4b811..c917af9 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -19,11 +19,11 @@
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
-import static android.net.NetworkStatsHistory.DataStreamUtils.readLongArray;
-import static android.net.NetworkStatsHistory.DataStreamUtils.writeLongArray;
-import static android.net.NetworkStatsHistory.ParcelUtils.readIntArray;
+import static android.net.NetworkStatsHistory.DataStreamUtils.readFullLongArray;
+import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLongArray;
+import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLongArray;
+import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
import static android.net.NetworkStatsHistory.ParcelUtils.readLongArray;
-import static android.net.NetworkStatsHistory.ParcelUtils.writeIntArray;
import static android.net.NetworkStatsHistory.ParcelUtils.writeLongArray;
import android.os.Parcel;
@@ -51,42 +51,53 @@
*/
public class NetworkStatsHistory implements Parcelable {
private static final int VERSION_INIT = 1;
+ private static final int VERSION_ADD_PACKETS = 2;
- // TODO: teach about varint encoding to use less disk space
- // TODO: teach about omitting entire fields to reduce parcel pressure
- // TODO: persist/restore packet and operation counts
+ public static final int FIELD_RX_BYTES = 0x01;
+ public static final int FIELD_RX_PACKETS = 0x02;
+ public static final int FIELD_TX_BYTES = 0x04;
+ public static final int FIELD_TX_PACKETS = 0x08;
+ public static final int FIELD_OPERATIONS = 0x10;
- private final long bucketDuration;
+ public static final int FIELD_ALL = 0xFFFFFFFF;
+
+ private long bucketDuration;
private int bucketCount;
private long[] bucketStart;
private long[] rxBytes;
private long[] rxPackets;
private long[] txBytes;
private long[] txPackets;
- private int[] operations;
+ private long[] operations;
public static class Entry {
+ public static final long UNKNOWN = -1;
+
public long bucketStart;
public long bucketDuration;
public long rxBytes;
public long rxPackets;
public long txBytes;
public long txPackets;
- public int operations;
+ public long operations;
}
public NetworkStatsHistory(long bucketDuration) {
- this(bucketDuration, 10);
+ this(bucketDuration, 10, FIELD_ALL);
}
public NetworkStatsHistory(long bucketDuration, int initialSize) {
+ this(bucketDuration, initialSize, FIELD_ALL);
+ }
+
+ public NetworkStatsHistory(long bucketDuration, int initialSize, int fields) {
this.bucketDuration = bucketDuration;
bucketStart = new long[initialSize];
- rxBytes = new long[initialSize];
- rxPackets = new long[initialSize];
- txBytes = new long[initialSize];
- txPackets = new long[initialSize];
- operations = new int[initialSize];
+ if ((fields & FIELD_RX_BYTES) != 0) rxBytes = new long[initialSize];
+ if ((fields & FIELD_RX_PACKETS) != 0) rxPackets = new long[initialSize];
+ if ((fields & FIELD_TX_BYTES) != 0) txBytes = new long[initialSize];
+ if ((fields & FIELD_TX_PACKETS) != 0) txPackets = new long[initialSize];
+ if ((fields & FIELD_OPERATIONS) != 0) operations = new long[initialSize];
bucketCount = 0;
}
@@ -97,7 +108,7 @@
rxPackets = readLongArray(in);
txBytes = readLongArray(in);
txPackets = readLongArray(in);
- operations = readIntArray(in);
+ operations = readLongArray(in);
bucketCount = bucketStart.length;
}
@@ -109,21 +120,31 @@
writeLongArray(out, rxPackets, bucketCount);
writeLongArray(out, txBytes, bucketCount);
writeLongArray(out, txPackets, bucketCount);
- writeIntArray(out, operations, bucketCount);
+ writeLongArray(out, operations, bucketCount);
}
public NetworkStatsHistory(DataInputStream in) throws IOException {
- // TODO: read packet and operation counts
final int version = in.readInt();
switch (version) {
case VERSION_INIT: {
bucketDuration = in.readLong();
- bucketStart = readLongArray(in);
- rxBytes = readLongArray(in);
+ bucketStart = readFullLongArray(in);
+ rxBytes = readFullLongArray(in);
rxPackets = new long[bucketStart.length];
- txBytes = readLongArray(in);
+ txBytes = readFullLongArray(in);
txPackets = new long[bucketStart.length];
- operations = new int[bucketStart.length];
+ operations = new long[bucketStart.length];
+ bucketCount = bucketStart.length;
+ break;
+ }
+ case VERSION_ADD_PACKETS: {
+ bucketDuration = in.readLong();
+ bucketStart = readVarLongArray(in);
+ rxBytes = readVarLongArray(in);
+ rxPackets = readVarLongArray(in);
+ txBytes = readVarLongArray(in);
+ txPackets = readVarLongArray(in);
+ operations = readVarLongArray(in);
bucketCount = bucketStart.length;
break;
}
@@ -134,12 +155,14 @@
}
public void writeToStream(DataOutputStream out) throws IOException {
- // TODO: write packet and operation counts
- out.writeInt(VERSION_INIT);
+ out.writeInt(VERSION_ADD_PACKETS);
out.writeLong(bucketDuration);
- writeLongArray(out, bucketStart, bucketCount);
- writeLongArray(out, rxBytes, bucketCount);
- writeLongArray(out, txBytes, bucketCount);
+ writeVarLongArray(out, bucketStart, bucketCount);
+ writeVarLongArray(out, rxBytes, bucketCount);
+ writeVarLongArray(out, rxPackets, bucketCount);
+ writeVarLongArray(out, txBytes, bucketCount);
+ writeVarLongArray(out, txPackets, bucketCount);
+ writeVarLongArray(out, operations, bucketCount);
}
/** {@inheritDoc} */
@@ -178,11 +201,11 @@
final Entry entry = recycle != null ? recycle : new Entry();
entry.bucketStart = bucketStart[i];
entry.bucketDuration = bucketDuration;
- entry.rxBytes = rxBytes[i];
- entry.rxPackets = rxPackets[i];
- entry.txBytes = txBytes[i];
- entry.txPackets = txPackets[i];
- entry.operations = operations[i];
+ entry.rxBytes = getLong(rxBytes, i, UNKNOWN);
+ entry.rxPackets = getLong(rxPackets, i, UNKNOWN);
+ entry.txBytes = getLong(txBytes, i, UNKNOWN);
+ entry.txPackets = getLong(txPackets, i, UNKNOWN);
+ entry.operations = getLong(operations, i, UNKNOWN);
return entry;
}
@@ -193,7 +216,7 @@
@Deprecated
public void recordData(long start, long end, long rxBytes, long txBytes) {
recordData(start, end,
- new NetworkStats.Entry(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0));
+ new NetworkStats.Entry(IFACE_ALL, UID_ALL, TAG_NONE, rxBytes, 0L, txBytes, 0L, 0L));
}
/**
@@ -230,11 +253,11 @@
final long fracTxPackets = entry.txPackets * overlap / duration;
final int fracOperations = (int) (entry.operations * overlap / duration);
- rxBytes[i] += fracRxBytes; entry.rxBytes -= fracRxBytes;
- rxPackets[i] += fracRxPackets; entry.rxPackets -= fracRxPackets;
- txBytes[i] += fracTxBytes; entry.txBytes -= fracTxBytes;
- txPackets[i] += fracTxPackets; entry.txPackets -= fracTxPackets;
- operations[i] += fracOperations; entry.operations -= fracOperations;
+ addLong(rxBytes, i, fracRxBytes); entry.rxBytes -= fracRxBytes;
+ addLong(rxPackets, i, fracRxPackets); entry.rxPackets -= fracRxPackets;
+ addLong(txBytes, i, fracTxBytes); entry.txBytes -= fracTxBytes;
+ addLong(txPackets, i, fracTxPackets); entry.txPackets -= fracTxPackets;
+ addLong(operations, i, fracOperations); entry.operations -= fracOperations;
duration -= overlap;
}
@@ -246,16 +269,16 @@
*/
public void recordEntireHistory(NetworkStatsHistory input) {
final NetworkStats.Entry entry = new NetworkStats.Entry(
- IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0);
+ IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
for (int i = 0; i < input.bucketCount; i++) {
final long start = input.bucketStart[i];
final long end = start + input.bucketDuration;
- entry.rxBytes = input.rxBytes[i];
- entry.rxPackets = input.rxPackets[i];
- entry.txBytes = input.txBytes[i];
- entry.txPackets = input.txPackets[i];
- entry.operations = input.operations[i];
+ entry.rxBytes = getLong(input.rxBytes, i, 0L);
+ entry.rxPackets = getLong(input.rxPackets, i, 0L);
+ entry.txBytes = getLong(input.txBytes, i, 0L);
+ entry.txPackets = getLong(input.txPackets, i, 0L);
+ entry.operations = getLong(input.operations, i, 0L);
recordData(start, end, entry);
}
@@ -287,11 +310,11 @@
if (bucketCount >= bucketStart.length) {
final int newLength = Math.max(bucketStart.length, 10) * 3 / 2;
bucketStart = Arrays.copyOf(bucketStart, newLength);
- rxBytes = Arrays.copyOf(rxBytes, newLength);
- rxPackets = Arrays.copyOf(rxPackets, newLength);
- txBytes = Arrays.copyOf(txBytes, newLength);
- txPackets = Arrays.copyOf(txPackets, newLength);
- operations = Arrays.copyOf(operations, newLength);
+ if (rxBytes != null) rxBytes = Arrays.copyOf(rxBytes, newLength);
+ if (rxPackets != null) rxPackets = Arrays.copyOf(rxPackets, newLength);
+ if (txBytes != null) txBytes = Arrays.copyOf(txBytes, newLength);
+ if (txPackets != null) txPackets = Arrays.copyOf(txPackets, newLength);
+ if (operations != null) operations = Arrays.copyOf(operations, newLength);
}
// create gap when inserting bucket in middle
@@ -300,19 +323,19 @@
final int length = bucketCount - index;
System.arraycopy(bucketStart, index, bucketStart, dstPos, length);
- System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
- System.arraycopy(rxPackets, index, rxPackets, dstPos, length);
- System.arraycopy(txBytes, index, txBytes, dstPos, length);
- System.arraycopy(txPackets, index, txPackets, dstPos, length);
- System.arraycopy(operations, index, operations, dstPos, length);
+ if (rxBytes != null) System.arraycopy(rxBytes, index, rxBytes, dstPos, length);
+ if (rxPackets != null) System.arraycopy(rxPackets, index, rxPackets, dstPos, length);
+ if (txBytes != null) System.arraycopy(txBytes, index, txBytes, dstPos, length);
+ if (txPackets != null) System.arraycopy(txPackets, index, txPackets, dstPos, length);
+ if (operations != null) System.arraycopy(operations, index, operations, dstPos, length);
}
bucketStart[index] = start;
- rxBytes[index] = 0;
- rxPackets[index] = 0;
- txBytes[index] = 0;
- txPackets[index] = 0;
- operations[index] = 0;
+ setLong(rxBytes, index, 0L);
+ setLong(rxPackets, index, 0L);
+ setLong(txBytes, index, 0L);
+ setLong(txPackets, index, 0L);
+ setLong(operations, index, 0L);
bucketCount++;
}
@@ -333,11 +356,11 @@
if (i > 0) {
final int length = bucketStart.length;
bucketStart = Arrays.copyOfRange(bucketStart, i, length);
- rxBytes = Arrays.copyOfRange(rxBytes, i, length);
- rxPackets = Arrays.copyOfRange(rxPackets, i, length);
- txBytes = Arrays.copyOfRange(txBytes, i, length);
- txPackets = Arrays.copyOfRange(txPackets, i, length);
- operations = Arrays.copyOfRange(operations, i, length);
+ if (rxBytes != null) rxBytes = Arrays.copyOfRange(rxBytes, i, length);
+ if (rxPackets != null) rxPackets = Arrays.copyOfRange(rxPackets, i, length);
+ if (txBytes != null) txBytes = Arrays.copyOfRange(txBytes, i, length);
+ if (txPackets != null) txPackets = Arrays.copyOfRange(txPackets, i, length);
+ if (operations != null) operations = Arrays.copyOfRange(operations, i, length);
bucketCount -= i;
}
}
@@ -358,11 +381,11 @@
final Entry entry = recycle != null ? recycle : new Entry();
entry.bucketStart = start;
entry.bucketDuration = end - start;
- entry.rxBytes = 0;
- entry.rxPackets = 0;
- entry.txBytes = 0;
- entry.txPackets = 0;
- entry.operations = 0;
+ entry.rxBytes = rxBytes != null ? 0 : UNKNOWN;
+ entry.rxPackets = rxPackets != null ? 0 : UNKNOWN;
+ entry.txBytes = txBytes != null ? 0 : UNKNOWN;
+ entry.txPackets = txPackets != null ? 0 : UNKNOWN;
+ entry.operations = operations != null ? 0 : UNKNOWN;
for (int i = bucketCount - 1; i >= 0; i--) {
final long curStart = bucketStart[i];
@@ -380,11 +403,11 @@
if (overlap <= 0) continue;
// integer math each time is faster than floating point
- entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
- entry.rxPackets += rxPackets[i] * overlap / bucketDuration;
- entry.txBytes += txBytes[i] * overlap / bucketDuration;
- entry.txPackets += txPackets[i] * overlap / bucketDuration;
- entry.operations += operations[i] * overlap / bucketDuration;
+ if (rxBytes != null) entry.rxBytes += rxBytes[i] * overlap / bucketDuration;
+ if (rxPackets != null) entry.rxPackets += rxPackets[i] * overlap / bucketDuration;
+ if (txBytes != null) entry.txBytes += txBytes[i] * overlap / bucketDuration;
+ if (txPackets != null) entry.txPackets += txPackets[i] * overlap / bucketDuration;
+ if (operations != null) entry.operations += operations[i] * overlap / bucketDuration;
}
return entry;
@@ -394,19 +417,29 @@
* @deprecated only for temporary testing
*/
@Deprecated
- public void generateRandom(long start, long end, long rx, long tx) {
+ public void generateRandom(long start, long end, long rxBytes, long rxPackets, long txBytes,
+ long txPackets, long operations) {
ensureBuckets(start, end);
final NetworkStats.Entry entry = new NetworkStats.Entry(
- IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0);
+ IFACE_ALL, UID_ALL, TAG_NONE, 0L, 0L, 0L, 0L, 0L);
final Random r = new Random();
- while (rx > 1024 && tx > 1024) {
+ while (rxBytes > 1024 && rxPackets > 128 && txBytes > 1024 && txPackets > 128
+ && operations > 32) {
final long curStart = randomLong(r, start, end);
final long curEnd = randomLong(r, curStart, end);
- entry.rxBytes = randomLong(r, 0, rx);
- entry.txBytes = randomLong(r, 0, tx);
- rx -= entry.rxBytes;
- tx -= entry.txBytes;
+
+ entry.rxBytes = randomLong(r, 0, rxBytes);
+ entry.rxPackets = randomLong(r, 0, rxPackets);
+ entry.txBytes = randomLong(r, 0, txBytes);
+ entry.txPackets = randomLong(r, 0, txPackets);
+ entry.operations = randomLong(r, 0, operations);
+
+ rxBytes -= entry.rxBytes;
+ rxPackets -= entry.rxPackets;
+ txBytes -= entry.txBytes;
+ txPackets -= entry.txPackets;
+ operations -= entry.operations;
recordData(curStart, curEnd, entry);
}
@@ -429,11 +462,12 @@
for (int i = start; i < bucketCount; i++) {
pw.print(prefix);
pw.print(" bucketStart="); pw.print(bucketStart[i]);
- pw.print(" rxBytes="); pw.print(rxBytes[i]);
- pw.print(" rxPackets="); pw.print(rxPackets[i]);
- pw.print(" txBytes="); pw.print(txBytes[i]);
- pw.print(" txPackets="); pw.print(txPackets[i]);
- pw.print(" operations="); pw.println(operations[i]);
+ if (rxBytes != null) pw.print(" rxBytes="); pw.print(rxBytes[i]);
+ if (rxPackets != null) pw.print(" rxPackets="); pw.print(rxPackets[i]);
+ if (txBytes != null) pw.print(" txBytes="); pw.print(txBytes[i]);
+ if (txPackets != null) pw.print(" txPackets="); pw.print(txPackets[i]);
+ if (operations != null) pw.print(" operations="); pw.print(operations[i]);
+ pw.println();
}
}
@@ -454,12 +488,25 @@
}
};
+ private static long getLong(long[] array, int i, long value) {
+ return array != null ? array[i] : value;
+ }
+
+ private static void setLong(long[] array, int i, long value) {
+ if (array != null) array[i] = value;
+ }
+
+ private static void addLong(long[] array, int i, long value) {
+ if (array != null) array[i] += value;
+ }
+
/**
* Utility methods for interacting with {@link DataInputStream} and
* {@link DataOutputStream}, mostly dealing with writing partial arrays.
*/
public static class DataStreamUtils {
- public static long[] readLongArray(DataInputStream in) throws IOException {
+ @Deprecated
+ public static long[] readFullLongArray(DataInputStream in) throws IOException {
final int size = in.readInt();
final long[] values = new long[size];
for (int i = 0; i < values.length; i++) {
@@ -468,14 +515,59 @@
return values;
}
- public static void writeLongArray(DataOutputStream out, long[] values, int size)
+ /**
+ * Read variable-length {@link Long} using protobuf-style approach.
+ */
+ public static long readVarLong(DataInputStream in) throws IOException {
+ int shift = 0;
+ long result = 0;
+ while (shift < 64) {
+ byte b = in.readByte();
+ result |= (long) (b & 0x7F) << shift;
+ if ((b & 0x80) == 0)
+ return result;
+ shift += 7;
+ }
+ throw new ProtocolException("malformed long");
+ }
+
+ /**
+ * Write variable-length {@link Long} using protobuf-style approach.
+ */
+ public static void writeVarLong(DataOutputStream out, long value) throws IOException {
+ while (true) {
+ if ((value & ~0x7FL) == 0) {
+ out.writeByte((int) value);
+ return;
+ } else {
+ out.writeByte(((int) value & 0x7F) | 0x80);
+ value >>>= 7;
+ }
+ }
+ }
+
+ public static long[] readVarLongArray(DataInputStream in) throws IOException {
+ final int size = in.readInt();
+ if (size == -1) return null;
+ final long[] values = new long[size];
+ for (int i = 0; i < values.length; i++) {
+ values[i] = readVarLong(in);
+ }
+ return values;
+ }
+
+ public static void writeVarLongArray(DataOutputStream out, long[] values, int size)
throws IOException {
+ if (values == null) {
+ out.writeInt(-1);
+ return;
+ }
if (size > values.length) {
throw new IllegalArgumentException("size larger than length");
}
out.writeInt(size);
for (int i = 0; i < size; i++) {
- out.writeLong(values[i]);
+ writeVarLong(out, values[i]);
}
}
}
@@ -487,6 +579,7 @@
public static class ParcelUtils {
public static long[] readLongArray(Parcel in) {
final int size = in.readInt();
+ if (size == -1) return null;
final long[] values = new long[size];
for (int i = 0; i < values.length; i++) {
values[i] = in.readLong();
@@ -495,6 +588,10 @@
}
public static void writeLongArray(Parcel out, long[] values, int size) {
+ if (values == null) {
+ out.writeInt(-1);
+ return;
+ }
if (size > values.length) {
throw new IllegalArgumentException("size larger than length");
}
@@ -503,25 +600,6 @@
out.writeLong(values[i]);
}
}
-
- public static int[] readIntArray(Parcel in) {
- final int size = in.readInt();
- final int[] values = new int[size];
- for (int i = 0; i < values.length; i++) {
- values[i] = in.readInt();
- }
- return values;
- }
-
- public static void writeIntArray(Parcel out, int[] values, int size) {
- if (size > values.length) {
- throw new IllegalArgumentException("size larger than length");
- }
- out.writeInt(size);
- for (int i = 0; i < size; i++) {
- out.writeInt(values[i]);
- }
- }
}
}
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index b668f30..6ba3451 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -21,6 +21,7 @@
import android.os.Parcelable;
import java.lang.UnsupportedOperationException;
+import java.nio.charset.Charset;
import java.nio.charset.Charsets;
import java.util.Arrays;
@@ -139,6 +140,22 @@
*/
public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs"
+ /**
+ * RTD Android app type. For use with TNF_EXTERNAL.
+ * <p>
+ * The payload of a record with type RTD_ANDROID_APP
+ * should be the package name identifying an application.
+ * Multiple RTD_ANDROID_APP records may be included
+ * in a single {@link NdefMessage}.
+ * <p>
+ * Use {@link #createApplicationRecord(String)} to create
+ * RTD_ANDROID_APP records.
+ * @hide
+ */
+ // TODO unhide for ICS
+ // TODO recheck docs
+ public static final byte[] RTD_ANDROID_APP = "android.com:pkg".getBytes();
+
private static final byte FLAG_MB = (byte) 0x80;
private static final byte FLAG_ME = (byte) 0x40;
private static final byte FLAG_CF = (byte) 0x20;
@@ -333,6 +350,29 @@
}
/**
+ * Creates an Android application NDEF record.
+ * <p>
+ * When an Android device dispatches an {@link NdefMessage}
+ * containing one or more Android application records,
+ * the applications contained in those records will be the
+ * preferred target for the NDEF_DISCOVERED intent, in
+ * the order in which they appear in the {@link NdefMessage}.
+ * <p>
+ * If none of the applications are installed on the device,
+ * a Market link will be opened to the first application.
+ * <p>
+ * Note that Android application records do not overrule
+ * applications that have called {@link NfcAdapter#enableForegroundDispatch}.
+ * @hide
+ */
+ // TODO unhide for ICS
+ // TODO recheck javadoc - should mention this works from ICS only
+ public static NdefRecord createApplicationRecord(String packageName) {
+ return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, new byte[] {},
+ packageName.getBytes(Charsets.US_ASCII));
+ }
+
+ /**
* Creates an NDEF record of well known type URI.
*/
public static NdefRecord createUri(Uri uri) {
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 3704248..bc4e00c 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -240,6 +240,11 @@
void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces);
/**
+ * Return status of bandwidth control module.
+ */
+ boolean isBandwidthControlEnabled();
+
+ /**
* Configures bandwidth throttling on an interface.
*/
void setInterfaceThrottle(String iface, int rxKbps, int txKbps);
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index a6c3387..9a4e0fc 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3166,6 +3166,18 @@
public static final String TTY_MODE_ENABLED = "tty_mode_enabled";
/**
+ * The number of milliseconds to delay before sending out Connectivyt Change broadcasts
+ * @hide
+ */
+ public static final String CONNECTIVITY_CHANGE_DELAY = "connectivity_change_delay";
+
+ /**
+ * Default value for CONNECTIVITY_CHANGE_DELAY in milliseconds.
+ * @hide
+ */
+ public static final int CONNECTIVITY_CHANGE_DELAY_DEFAULT = 3000;
+
+ /**
* Controls whether settings backup is enabled.
* Type: int ( 0 = disabled, 1 = enabled )
* @hide
diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java
index f498bb2..8c04853 100644
--- a/core/java/android/server/BluetoothA2dpService.java
+++ b/core/java/android/server/BluetoothA2dpService.java
@@ -194,7 +194,7 @@
}
private synchronized void onBluetoothEnable() {
- String devices = mBluetoothService.getProperty("Devices");
+ String devices = mBluetoothService.getProperty("Devices", true);
if (devices != null) {
String [] paths = devices.split(",");
for (String path: paths) {
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 3029c9d..0357958 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -798,8 +798,15 @@
return true;
}
- /*package*/ synchronized String getProperty(String name) {
- if (!isEnabledInternal()) return null;
+ /*package*/ synchronized String getProperty(String name, boolean checkState) {
+ // If checkState is false, check if the event loop is running.
+ // before making the call to Bluez
+ if (checkState) {
+ if (!isEnabledInternal()) return null;
+ } else if (!mEventLoop.isEventLoopRunning()) {
+ return null;
+ }
+
return mAdapterProperties.getProperty(name);
}
@@ -825,17 +832,19 @@
public synchronized String getAddress() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return getProperty("Address");
+ // Don't check state since we want to provide address, even if BT is off
+ return getProperty("Address", false);
}
public synchronized String getName() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- return getProperty("Name");
+ // Don't check state since we want to provide name, even if BT is off
+ return getProperty("Name", false);
}
public synchronized ParcelUuid[] getUuids() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- String value = getProperty("UUIDs");
+ String value = getProperty("UUIDs", true);
if (value == null) return null;
return convertStringToParcelUuid(value);
}
@@ -915,7 +924,7 @@
*/
public synchronized int getDiscoverableTimeout() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- String timeout = getProperty("DiscoverableTimeout");
+ String timeout = getProperty("DiscoverableTimeout", true);
if (timeout != null)
return Integer.valueOf(timeout);
else
@@ -927,8 +936,8 @@
if (!isEnabledInternal())
return BluetoothAdapter.SCAN_MODE_NONE;
- boolean pairable = getProperty("Pairable").equals("true");
- boolean discoverable = getProperty("Discoverable").equals("true");
+ boolean pairable = getProperty("Pairable", true).equals("true");
+ boolean discoverable = getProperty("Discoverable", true).equals("true");
return bluezStringToScanMode (pairable, discoverable);
}
@@ -951,7 +960,7 @@
public synchronized boolean isDiscovering() {
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
- String discoveringProperty = mAdapterProperties.getProperty("Discovering");
+ String discoveringProperty = getProperty("Discovering", false);
if (discoveringProperty == null) {
return false;
}
@@ -2341,7 +2350,7 @@
synchronized String[] getKnownDevices() {
String[] bonds = null;
- String val = getProperty("Devices");
+ String val = getProperty("Devices", true);
if (val != null) {
bonds = val.split(",");
}
@@ -2350,7 +2359,7 @@
private void initProfileState() {
String[] bonds = null;
- String val = mAdapterProperties.getProperty("Devices");
+ String val = getProperty("Devices", false);
if (val != null) {
bonds = val.split(",");
}
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 6f70ab8..3e2e38e 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -22,6 +22,7 @@
import android.app.Service;
import android.content.Intent;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
@@ -43,45 +44,6 @@
private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this);
- /**
- * Get suggestions for specified text in TextInfo.
- * This function will run on the incoming IPC thread. So, this is not called on the main thread,
- * but will be called in series on another thread.
- * @param textInfo the text metadata
- * @param suggestionsLimit the number of limit of suggestions returned
- * @param locale the locale for getting suggestions
- * @return SuggestionInfo which contains suggestions for textInfo
- */
- public abstract SuggestionsInfo getSuggestions(
- TextInfo textInfo, int suggestionsLimit, String locale);
-
- /**
- * A batch process of onGetSuggestions.
- * This function will run on the incoming IPC thread. So, this is not called on the main thread,
- * but will be called in series on another thread.
- * @param textInfos an array of the text metadata
- * @param locale the locale for getting suggestions
- * @param suggestionsLimit the number of limit of suggestions returned
- * @param sequentialWords true if textInfos can be treated as sequential words.
- * @return an array of SuggestionInfo of onGetSuggestions
- */
- public SuggestionsInfo[] getSuggestionsMultiple(
- TextInfo[] textInfos, String locale, int suggestionsLimit, boolean sequentialWords) {
- final int length = textInfos.length;
- final SuggestionsInfo[] retval = new SuggestionsInfo[length];
- for (int i = 0; i < length; ++i) {
- retval[i] = getSuggestions(textInfos[i], suggestionsLimit, locale);
- retval[i].setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence());
- }
- return retval;
- }
-
- /**
- * Request to abort all tasks executed in SpellChecker.
- * This function will run on the incoming IPC thread. So, this is not called on the main thread,
- * but will be called in series on another thread.
- */
- public void cancel() {}
/**
* Implement to return the implementation of the internal spell checker
@@ -95,36 +57,125 @@
return mBinder;
}
- private static class SpellCheckerSessionImpl extends ISpellCheckerSession.Stub {
- private final WeakReference<SpellCheckerService> mInternalServiceRef;
- private final String mLocale;
- private final ISpellCheckerSessionListener mListener;
+ /**
+ * Factory method to create a spell checker session impl
+ * @return SpellCheckerSessionImpl which should be overridden by a concrete implementation.
+ */
+ public abstract Session createSession();
- public SpellCheckerSessionImpl(
- SpellCheckerService service, String locale, ISpellCheckerSessionListener listener) {
- mInternalServiceRef = new WeakReference<SpellCheckerService>(service);
- mLocale = locale;
+ /**
+ * This abstract class should be overridden by a concrete implementation of a spell checker.
+ */
+ public abstract class Session {
+ private InternalISpellCheckerSession mInternalSession;
+
+ /**
+ * @hide
+ */
+ public final void setInternalISpellCheckerSession(InternalISpellCheckerSession session) {
+ mInternalSession = session;
+ }
+
+ /**
+ * This is called after the class is initialized, at which point it knows it can call
+ * getLocale() etc...
+ */
+ public abstract void onCreate();
+
+ /**
+ * Get suggestions for specified text in TextInfo.
+ * This function will run on the incoming IPC thread.
+ * So, this is not called on the main thread,
+ * but will be called in series on another thread.
+ * @param textInfo the text metadata
+ * @param suggestionsLimit the number of limit of suggestions returned
+ * @return SuggestionInfo which contains suggestions for textInfo
+ */
+ public abstract SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit);
+
+ /**
+ * A batch process of onGetSuggestions.
+ * This function will run on the incoming IPC thread.
+ * So, this is not called on the main thread,
+ * but will be called in series on another thread.
+ * @param textInfos an array of the text metadata
+ * @param suggestionsLimit the number of limit of suggestions returned
+ * @param sequentialWords true if textInfos can be treated as sequential words.
+ * @return an array of SuggestionInfo of onGetSuggestions
+ */
+ public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
+ int suggestionsLimit, boolean sequentialWords) {
+ final int length = textInfos.length;
+ final SuggestionsInfo[] retval = new SuggestionsInfo[length];
+ for (int i = 0; i < length; ++i) {
+ retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
+ retval[i].setCookieAndSequence(
+ textInfos[i].getCookie(), textInfos[i].getSequence());
+ }
+ return retval;
+ }
+
+ /**
+ * Request to abort all tasks executed in SpellChecker.
+ * This function will run on the incoming IPC thread.
+ * So, this is not called on the main thread,
+ * but will be called in series on another thread.
+ */
+ public void onCancel() {}
+
+ /**
+ * @return Locale for this session
+ */
+ public String getLocale() {
+ return mInternalSession.getLocale();
+ }
+
+ /**
+ * @return Bundle for this session
+ */
+ public Bundle getBundle() {
+ return mInternalSession.getBundle();
+ }
+ }
+
+ // Preventing from exposing ISpellCheckerSession.aidl, create an internal class.
+ private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub {
+ private final ISpellCheckerSessionListener mListener;
+ private final Session mSession;
+ private final String mLocale;
+ private final Bundle mBundle;
+
+ public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener,
+ Bundle bundle, Session session) {
mListener = listener;
+ mSession = session;
+ mLocale = locale;
+ mBundle = bundle;
+ session.setInternalISpellCheckerSession(this);
}
@Override
- public void getSuggestionsMultiple(
+ public void onGetSuggestionsMultiple(
TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
- final SpellCheckerService service = mInternalServiceRef.get();
- if (service == null) return;
try {
mListener.onGetSuggestions(
- service.getSuggestionsMultiple(textInfos, mLocale,
- suggestionsLimit, sequentialWords));
+ mSession.onGetSuggestionsMultiple(
+ textInfos, suggestionsLimit, sequentialWords));
} catch (RemoteException e) {
}
}
@Override
- public void cancel() {
- final SpellCheckerService service = mInternalServiceRef.get();
- if (service == null) return;
- service.cancel();
+ public void onCancel() {
+ mSession.onCancel();
+ }
+
+ public String getLocale() {
+ return mLocale;
+ }
+
+ public Bundle getBundle() {
+ return mBundle;
}
}
@@ -137,10 +188,14 @@
@Override
public ISpellCheckerSession getISpellCheckerSession(
- String locale, ISpellCheckerSessionListener listener) {
+ String locale, ISpellCheckerSessionListener listener, Bundle bundle) {
final SpellCheckerService service = mInternalServiceRef.get();
if (service == null) return null;
- return new SpellCheckerSessionImpl(service, locale, listener);
+ final Session session = service.createSession();
+ final InternalISpellCheckerSession internalSession =
+ new InternalISpellCheckerSession(locale, listener, bundle, session);
+ session.onCreate();
+ return internalSession;
}
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 9be2a67..c93b564 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -784,7 +784,7 @@
builder.append("\n");
}
} else {
- builder.append("; recordCount: ").append(getAddedCount());
+ builder.append("; recordCount: ").append(getRecordCount());
}
return builder.toString();
}
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index bf07e71..b940b80 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -233,7 +233,7 @@
Log.w(TAG, "Cancel spell checker tasks.");
}
try {
- mISpellCheckerSession.cancel();
+ mISpellCheckerSession.onCancel();
} catch (RemoteException e) {
Log.e(TAG, "Failed to cancel " + e);
}
@@ -247,7 +247,7 @@
Log.w(TAG, "Get suggestions from the spell checker.");
}
try {
- mISpellCheckerSession.getSuggestionsMultiple(
+ mISpellCheckerSession.onGetSuggestionsMultiple(
scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords);
} catch (RemoteException e) {
Log.e(TAG, "Failed to get suggestions " + e);
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index a60eb24..d60ce4f 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -19,11 +19,11 @@
import com.android.internal.textservice.ITextServicesManager;
import android.content.Context;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
-import android.view.textservice.SpellCheckerSession;
import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
import java.util.Locale;
@@ -74,7 +74,7 @@
*/
// TODO: Add a method to get enabled spell checkers.
// TODO: Handle referToSpellCheckerLanguageSettings
- public SpellCheckerSession newSpellCheckerSession(Locale locale,
+ public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale,
SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
if (listener == null) {
throw new NullPointerException();
@@ -94,7 +94,7 @@
try {
sService.getSpellCheckerService(sci.getId(), localeString,
session.getTextServicesSessionListener(),
- session.getSpellCheckerSessionListener());
+ session.getSpellCheckerSessionListener(), bundle);
} catch (RemoteException e) {
return null;
}
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index d977029..390002b 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -395,10 +395,6 @@
*/
public void setUseDefaultMargins(boolean useDefaultMargins) {
mUseDefaultMargins = useDefaultMargins;
- if (useDefaultMargins) {
- int padding = mDefaultGap;
- setPadding(padding, padding, padding, padding);
- }
requestLayout();
}
@@ -740,6 +736,10 @@
graphics.drawLine(dx + x1, dy + y1, dx + x2, dy + y2, paint);
}
+ private static void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint) {
+ canvas.drawRect(x1, y1, x2 - 1, y2 - 1, paint);
+ }
+
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
@@ -772,14 +772,14 @@
paint.setColor(Color.BLUE);
for (int i = 0; i < getChildCount(); i++) {
View c = getChildAt(i);
- canvas.drawRect(c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), paint);
+ drawRect(canvas, c.getLeft(), c.getTop(), c.getRight(), c.getBottom(), paint);
}
// Draw margins
paint.setColor(Color.MAGENTA);
for (int i = 0; i < getChildCount(); i++) {
View c = getChildAt(i);
- canvas.drawRect(
+ drawRect(canvas,
c.getLeft() - getMargin1(c, true, true),
c.getTop() - getMargin1(c, false, true),
c.getRight() + getMargin1(c, true, false),
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index 299e1ff..fc60949 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -83,6 +83,7 @@
// Avoid allocations...
private RectF mTempSrc = new RectF();
private RectF mTempDst = new RectF();
+ private float[] mTempPoints;
private boolean mCropToPadding;
@@ -337,7 +338,6 @@
}
}
-
/**
* Sets a drawable as the content of this ImageView.
*
@@ -347,8 +347,31 @@
if (mDrawable != drawable) {
mResource = 0;
mUri = null;
+ int oldWidth = mDrawableWidth;
+ int oldHeight = mDrawableHeight;
updateDrawable(drawable);
- requestLayout();
+
+ boolean needsLayout;
+ if (mScaleType == ScaleType.CENTER) {
+ needsLayout = mDrawableWidth != oldWidth || mDrawableHeight != oldHeight;
+ } else {
+ if (mTempPoints == null) {
+ mTempPoints = new float[4];
+ }
+ float[] points = mTempPoints;
+ points[0] = oldWidth;
+ points[1] = oldHeight;
+ points[2] = mDrawableWidth;
+ points[3] = mDrawableHeight;
+ if (!mMatrix.isIdentity()) {
+ mMatrix.mapPoints(points);
+ }
+ needsLayout = points[0] != points[2] || points[1] != points[3];
+ }
+
+ if (needsLayout) {
+ requestLayout();
+ }
invalidate();
}
}
@@ -643,6 +666,9 @@
// We are allowed to change the view's height
boolean resizeHeight = false;
+ final int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
+ final int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
+
if (mDrawable == null) {
// If no drawable, its intrinsic size is 0.
mDrawableWidth = -1;
@@ -657,10 +683,6 @@
// We are supposed to adjust view bounds to match the aspect
// ratio of our drawable. See if that is possible.
if (mAdjustViewBounds) {
-
- int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
- int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
-
resizeWidth = widthSpecMode != MeasureSpec.EXACTLY;
resizeHeight = heightSpecMode != MeasureSpec.EXACTLY;
diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java
index 3a22bfb..8c288d10 100644
--- a/core/java/android/widget/SearchView.java
+++ b/core/java/android/widget/SearchView.java
@@ -112,6 +112,7 @@
private boolean mClearingFocus;
private int mMaxWidth;
private boolean mVoiceButtonEnabled;
+ private CharSequence mOldQueryText;
private CharSequence mUserQuery;
private boolean mExpandedInActionView;
@@ -462,6 +463,7 @@
if (mIconifiedByDefault == iconified) return;
mIconifiedByDefault = iconified;
updateViewsVisibility(iconified);
+ updateQueryHint();
}
/**
@@ -970,9 +972,10 @@
updateVoiceButton(!hasText);
updateCloseButton();
updateSubmitArea();
- if (mOnQueryChangeListener != null) {
+ if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)) {
mOnQueryChangeListener.onQueryTextChange(newText.toString());
}
+ mOldQueryText = newText.toString();
}
private void onSubmitQuery() {
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
index ff00492..67d7b3e 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
@@ -19,11 +19,13 @@
import com.android.internal.textservice.ISpellCheckerSession;
import com.android.internal.textservice.ISpellCheckerSessionListener;
+import android.os.Bundle;
+
/**
* Public interface to the global spell checker.
* @hide
*/
interface ISpellCheckerService {
ISpellCheckerSession getISpellCheckerSession(
- String locale, ISpellCheckerSessionListener listener);
+ String locale, ISpellCheckerSessionListener listener, in Bundle bundle);
}
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
index 79e43510c0..5a00603 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
@@ -22,7 +22,7 @@
* @hide
*/
oneway interface ISpellCheckerSession {
- void getSuggestionsMultiple(
+ void onGetSuggestionsMultiple(
in TextInfo[] textInfos, int suggestionsLimit, boolean multipleWords);
- void cancel();
+ void onCancel();
}
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index 4d7dfbb..bb4b2a3 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -20,6 +20,7 @@
import com.android.internal.textservice.ITextServicesSessionListener;
import android.content.ComponentName;
+import android.os.Bundle;
import android.view.textservice.SpellCheckerInfo;
/**
@@ -30,7 +31,7 @@
SpellCheckerInfo getCurrentSpellChecker(String locale);
oneway void getSpellCheckerService(String sciId, in String locale,
in ITextServicesSessionListener tsListener,
- in ISpellCheckerSessionListener scListener);
+ in ISpellCheckerSessionListener scListener, in Bundle bundle);
oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener);
oneway void setCurrentSpellChecker(String sciId);
SpellCheckerInfo[] getEnabledSpellCheckers();
diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java
index 9ecd29f..0cadb16 100644
--- a/core/java/com/android/internal/util/Protocol.java
+++ b/core/java/com/android/internal/util/Protocol.java
@@ -49,5 +49,6 @@
public static final int BASE_DATA_CONNECTION_AC = 0x00041000;
public static final int BASE_DATA_CONNECTION_TRACKER = 0x00042000;
+ public static final int BASE_DNS_PINGER = 0x00050000;
//TODO: define all used protocols
}
diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java
index cbe72dd..36f0246 100644
--- a/core/java/com/android/internal/util/StateMachine.java
+++ b/core/java/com/android/internal/util/StateMachine.java
@@ -1226,6 +1226,12 @@
* be executed and upon the next message arriving
* destState.enter will be invoked.
*
+ * this function can also be called inside the enter function of the
+ * previous transition target, but the behavior is undefined when it is
+ * called mid-way through a previous transition (for example, calling this
+ * in the enter() routine of a intermediate node when the current transition
+ * target is one of the nodes descendants).
+ *
* @param destState will be the state that receives the next message.
*/
protected final void transitionTo(IState destState) {
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index 0510023..ec926e4 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -127,6 +127,7 @@
mAnimatingTargets = false;
}
};
+ private int mTargetResourceId;
public MultiWaveView(Context context) {
this(context, null);
@@ -474,6 +475,7 @@
Drawable drawable = array.getDrawable(i);
targetDrawables.add(new TargetDrawable(res, drawable));
}
+ mTargetResourceId = resourceId;
mTargetDrawables = targetDrawables;
updateTargetPositions();
}
@@ -492,6 +494,10 @@
}
}
+ public int getTargetResourceId() {
+ return mTargetResourceId;
+ }
+
/**
* Enable or disable vibrate on touch.
*
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_camera_activated.png b/core/res/res/drawable-hdpi/ic_lockscreen_camera_activated.png
new file mode 100644
index 0000000..d510e1d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_camera_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_camera_normal.png b/core/res/res/drawable-hdpi/ic_lockscreen_camera_normal.png
new file mode 100644
index 0000000..36d766d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_camera_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_silent_activated.png b/core/res/res/drawable-hdpi/ic_lockscreen_silent_activated.png
index fce4980..d1938b9 100644
--- a/core/res/res/drawable-hdpi/ic_lockscreen_silent_activated.png
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_silent_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_soundon_activated.png b/core/res/res/drawable-hdpi/ic_lockscreen_soundon_activated.png
index 73f01c9..f6ccbd2 100644
--- a/core/res/res/drawable-hdpi/ic_lockscreen_soundon_activated.png
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_soundon_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_text_activated.png b/core/res/res/drawable-hdpi/ic_lockscreen_text_activated.png
index d01bdb2..10b3268 100644
--- a/core/res/res/drawable-hdpi/ic_lockscreen_text_activated.png
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_text_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_unlock_activated.png b/core/res/res/drawable-hdpi/ic_lockscreen_unlock_activated.png
index d333946..d1f3015 100644
--- a/core/res/res/drawable-hdpi/ic_lockscreen_unlock_activated.png
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_unlock_activated.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_activated_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_ab_activated_holo_dark.9.png
new file mode 100644
index 0000000..d471c30
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_activated_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_ab_activated_holo_light.9.png
new file mode 100644
index 0000000..d471c30
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000..001cfbb
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000..5e278c8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..cf2e149
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000..63f212d
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000..85663e6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000..85663e6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..afddbe8
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000..0ad6476
--- /dev/null
+++ b/core/res/res/drawable-hdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_active_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_active_holo_dark.9.png
deleted file mode 100644
index 7cad0c9..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_active_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_active_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_active_holo_light.9.png
deleted file mode 100644
index 7cad0c9..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_active_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_default_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_default_holo_dark.9.png
deleted file mode 100644
index b3f3cf7..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_default_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_default_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_default_holo_light.9.png
deleted file mode 100644
index af46631..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_default_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_dark.9.png
deleted file mode 100644
index e672c95..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_light.9.png
deleted file mode 100644
index f499540..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_disabled_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_focused_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_focused_holo_dark.9.png
deleted file mode 100644
index f4900a5..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_focused_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_focused_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_focused_holo_light.9.png
deleted file mode 100644
index 1286542..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_focused_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_dark.9.png b/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_dark.9.png
deleted file mode 100644
index 8b6f9dd..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_light.9.png
deleted file mode 100644
index 4f0c476..0000000
--- a/core/res/res/drawable-hdpi/spinner_cab_pressed_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_camera_activated.png b/core/res/res/drawable-mdpi/ic_lockscreen_camera_activated.png
new file mode 100644
index 0000000..1437798
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_camera_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_camera_normal.png b/core/res/res/drawable-mdpi/ic_lockscreen_camera_normal.png
new file mode 100644
index 0000000..b718258
--- /dev/null
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_camera_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_silent_activated.png b/core/res/res/drawable-mdpi/ic_lockscreen_silent_activated.png
index 3b2f3fc..2bc3f52 100644
--- a/core/res/res/drawable-mdpi/ic_lockscreen_silent_activated.png
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_silent_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_soundon_activated.png b/core/res/res/drawable-mdpi/ic_lockscreen_soundon_activated.png
index 03f524d..637eec6 100644
--- a/core/res/res/drawable-mdpi/ic_lockscreen_soundon_activated.png
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_soundon_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_text_activated.png b/core/res/res/drawable-mdpi/ic_lockscreen_text_activated.png
index dbfc5ba..878ff1f 100644
--- a/core/res/res/drawable-mdpi/ic_lockscreen_text_activated.png
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_text_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_unlock_activated.png b/core/res/res/drawable-mdpi/ic_lockscreen_unlock_activated.png
index df47993..0d3f756 100644
--- a/core/res/res/drawable-mdpi/ic_lockscreen_unlock_activated.png
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_unlock_activated.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_activated_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_ab_activated_holo_dark.9.png
new file mode 100644
index 0000000..34c9188
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_activated_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_ab_activated_holo_light.9.png
new file mode 100644
index 0000000..34c9188
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000..b92abaf
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000..91f0e87
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..dab7eda0
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000..bf14605
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000..c733260
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000..c733260
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..6d290a6
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000..6dae484
--- /dev/null
+++ b/core/res/res/drawable-mdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_active_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_active_holo_dark.9.png
deleted file mode 100644
index 617c379..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_active_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_active_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_active_holo_light.9.png
deleted file mode 100644
index 617c379..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_active_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_default_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_default_holo_dark.9.png
deleted file mode 100644
index d76b123..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_default_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_default_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_default_holo_light.9.png
deleted file mode 100644
index ee91044..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_default_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_dark.9.png
deleted file mode 100644
index fc9c109..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_light.9.png
deleted file mode 100644
index 7fc7cc1..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_disabled_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_focused_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_focused_holo_dark.9.png
deleted file mode 100644
index 0f49fe9..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_focused_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_focused_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_focused_holo_light.9.png
deleted file mode 100644
index 032d5d3..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_focused_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_dark.9.png b/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_dark.9.png
deleted file mode 100644
index df0a935..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_light.9.png
deleted file mode 100644
index b43177b..0000000
--- a/core/res/res/drawable-mdpi/spinner_cab_pressed_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_camera_activated.png b/core/res/res/drawable-xhdpi/ic_lockscreen_camera_activated.png
new file mode 100644
index 0000000..d545883
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_camera_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_camera_normal.png b/core/res/res/drawable-xhdpi/ic_lockscreen_camera_normal.png
new file mode 100644
index 0000000..8de7b84
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_camera_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_silent_activated.png b/core/res/res/drawable-xhdpi/ic_lockscreen_silent_activated.png
index fd81211..2900045 100644
--- a/core/res/res/drawable-xhdpi/ic_lockscreen_silent_activated.png
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_silent_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_soundon_activated.png b/core/res/res/drawable-xhdpi/ic_lockscreen_soundon_activated.png
index 9edc70b..da2adc2 100644
--- a/core/res/res/drawable-xhdpi/ic_lockscreen_soundon_activated.png
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_soundon_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_text_activated.png b/core/res/res/drawable-xhdpi/ic_lockscreen_text_activated.png
index 29c4572..ddebe3e 100644
--- a/core/res/res/drawable-xhdpi/ic_lockscreen_text_activated.png
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_text_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_unlock_activated.png b/core/res/res/drawable-xhdpi/ic_lockscreen_unlock_activated.png
index fa0be96..73d7af3 100644
--- a/core/res/res/drawable-xhdpi/ic_lockscreen_unlock_activated.png
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_unlock_activated.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_activated_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_ab_activated_holo_dark.9.png
new file mode 100644
index 0000000..85d8540
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_activated_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_activated_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_ab_activated_holo_light.9.png
new file mode 100644
index 0000000..85d8540
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_activated_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_default_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_ab_default_holo_dark.9.png
new file mode 100644
index 0000000..31b39d7
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_default_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_default_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_ab_default_holo_light.9.png
new file mode 100644
index 0000000..1527c5c
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_default_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_dark.9.png
new file mode 100644
index 0000000..e4cef9a
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_light.9.png
new file mode 100644
index 0000000..1c37ece
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_disabled_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_dark.9.png
new file mode 100644
index 0000000..6aae46b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_light.9.png
new file mode 100644
index 0000000..6aae46b
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_focused_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_dark.9.png b/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_dark.9.png
new file mode 100644
index 0000000..db2e034
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_light.9.png b/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_light.9.png
new file mode 100644
index 0000000..77bb433
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/spinner_ab_pressed_holo_light.9.png
Binary files differ
diff --git a/core/res/res/drawable/ic_lockscreen_camera.xml b/core/res/res/drawable/ic_lockscreen_camera.xml
new file mode 100644
index 0000000..0e3ef37
--- /dev/null
+++ b/core/res/res/drawable/ic_lockscreen_camera.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item
+ android:state_enabled="true"
+ android:state_active="false"
+ android:state_focused="false"
+ android:drawable="@drawable/ic_lockscreen_camera_normal" />
+
+ <item
+ android:state_enabled="true"
+ android:state_active="true"
+ android:state_focused="false"
+ android:drawable="@drawable/ic_lockscreen_camera_activated" />
+
+</selector>
diff --git a/core/res/res/drawable/spinner_cab_background_holo_dark.xml b/core/res/res/drawable/spinner_ab_holo_dark.xml
similarity index 66%
rename from core/res/res/drawable/spinner_cab_background_holo_dark.xml
rename to core/res/res/drawable/spinner_ab_holo_dark.xml
index 5572450..708b6ab 100644
--- a/core/res/res/drawable/spinner_cab_background_holo_dark.xml
+++ b/core/res/res/drawable/spinner_ab_holo_dark.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2010 The Android Open Source Project
+<!-- Copyright (C) 2011 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -16,10 +16,12 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:drawable="@drawable/spinner_cab_disabled_holo_dark" />
+ android:drawable="@drawable/spinner_ab_disabled_holo_dark" />
<item android:state_pressed="true"
- android:drawable="@drawable/spinner_cab_pressed_holo_dark" />
+ android:drawable="@drawable/spinner_ab_pressed_holo_dark" />
<item android:state_pressed="false" android:state_focused="true"
- android:drawable="@drawable/spinner_cab_focused_holo_dark" />
- <item android:drawable="@drawable/spinner_cab_default_holo_dark" />
+ android:drawable="@drawable/spinner_ab_focused_holo_dark" />
+ <item android:state_activated="true"
+ android:drawable="@drawable/spinner_ab_activated_holo_dark" />
+ <item android:drawable="@drawable/spinner_ab_default_holo_dark" />
</selector>
diff --git a/core/res/res/drawable/spinner_cab_background_holo_light.xml b/core/res/res/drawable/spinner_ab_holo_light.xml
similarity index 70%
rename from core/res/res/drawable/spinner_cab_background_holo_light.xml
rename to core/res/res/drawable/spinner_ab_holo_light.xml
index 98ff48d..c4901ca 100644
--- a/core/res/res/drawable/spinner_cab_background_holo_light.xml
+++ b/core/res/res/drawable/spinner_ab_holo_light.xml
@@ -16,10 +16,12 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false"
- android:drawable="@drawable/spinner_cab_disabled_holo_light" />
+ android:drawable="@drawable/spinner_ab_disabled_holo_light" />
<item android:state_pressed="true"
- android:drawable="@drawable/spinner_cab_pressed_holo_light" />
+ android:drawable="@drawable/spinner_ab_pressed_holo_light" />
<item android:state_pressed="false" android:state_focused="true"
- android:drawable="@drawable/spinner_cab_focused_holo_light" />
- <item android:drawable="@drawable/spinner_cab_default_holo_light" />
+ android:drawable="@drawable/spinner_ab_focused_holo_light" />
+ <item android:state_activated="true"
+ android:drawable="@drawable/spinner_ab_activated_holo_light" />
+ <item android:drawable="@drawable/spinner_ab_default_holo_light" />
</selector>
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock.xml b/core/res/res/layout/keyguard_screen_tab_unlock.xml
index 05c768d..6016d4e 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock.xml
@@ -129,7 +129,7 @@
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
- android:targetDrawables="@array/lockscreen_targets_when_silent"
+ android:targetDrawables="@array/lockscreen_targets_with_camera"
android:handleDrawable="@drawable/ic_lockscreen_handle"
android:waveDrawable="@drawable/ic_lockscreen_outerring"
android:outerRadius="@dimen/multiwaveview_target_placement_radius"
diff --git a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
index 6440726..168bd1a 100644
--- a/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout/keyguard_screen_tab_unlock_land.xml
@@ -131,7 +131,7 @@
android:layout_height="match_parent"
android:layout_rowSpan="7"
- android:targetDrawables="@array/lockscreen_targets_when_silent"
+ android:targetDrawables="@array/lockscreen_targets_with_camera"
android:handleDrawable="@drawable/ic_lockscreen_handle"
android:waveDrawable="@drawable/ic_lockscreen_outerring"
android:outerRadius="@dimen/multiwaveview_target_placement_radius"
diff --git a/core/res/res/values-land/arrays.xml b/core/res/res/values-land/arrays.xml
index 92d5a87..fd492ec 100644
--- a/core/res/res/values-land/arrays.xml
+++ b/core/res/res/values-land/arrays.xml
@@ -34,4 +34,11 @@
<item>@drawable/ic_lockscreen_silent</item>
</array>
+ <array name="lockscreen_targets_with_camera">
+ <item>@null</item>
+ <item>@drawable/ic_lockscreen_unlock</item>
+ <item>@null</item>
+ <item>@drawable/ic_lockscreen_camera</item>
+ </array>
+
</resources>
diff --git a/core/res/res/values/arrays.xml b/core/res/res/values/arrays.xml
index 73103a6..753e4ac 100644
--- a/core/res/res/values/arrays.xml
+++ b/core/res/res/values/arrays.xml
@@ -217,14 +217,18 @@
<item>@drawable/scrollbar_handle_vertical</item>
<item>@drawable/spinner_background_holo_dark</item>
<item>@drawable/spinner_background_holo_light</item>
- <item>@drawable/spinner_cab_default_holo_dark</item>
- <item>@drawable/spinner_cab_default_holo_light</item>
- <item>@drawable/spinner_cab_disabled_holo_dark</item>
- <item>@drawable/spinner_cab_disabled_holo_light</item>
- <item>@drawable/spinner_cab_focused_holo_dark</item>
- <item>@drawable/spinner_cab_focused_holo_light</item>
- <item>@drawable/spinner_cab_pressed_holo_dark</item>
- <item>@drawable/spinner_cab_pressed_holo_light</item>
+ <item>@drawable/spinner_ab_default_holo_dark</item>
+ <item>@drawable/spinner_ab_default_holo_light</item>
+ <item>@drawable/spinner_ab_disabled_holo_dark</item>
+ <item>@drawable/spinner_ab_disabled_holo_light</item>
+ <item>@drawable/spinner_ab_focused_holo_dark</item>
+ <item>@drawable/spinner_ab_focused_holo_light</item>
+ <item>@drawable/spinner_ab_pressed_holo_dark</item>
+ <item>@drawable/spinner_ab_pressed_holo_light</item>
+ <item>@drawable/spinner_ab_activated_holo_dark</item>
+ <item>@drawable/spinner_ab_activated_holo_light</item>
+ <item>@drawable/spinner_ab_holo_dark</item>
+ <item>@drawable/spinner_ab_holo_light</item>
<item>@drawable/spinner_default_holo_dark</item>
<item>@drawable/spinner_default_holo_light</item>
<item>@drawable/spinner_disabled_holo_dark</item>
@@ -358,4 +362,11 @@
<item>@null</item>"
</array>
+ <array name="lockscreen_targets_with_camera">
+ <item>@drawable/ic_lockscreen_unlock</item>
+ <item>@null</item>
+ <item>@drawable/ic_lockscreen_camera</item>
+ <item>@null</item>"
+ </array>
+
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 6a3c2d5..afd8908 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -212,8 +212,8 @@
<!-- android.net.http Error strings --> <skip />
<!-- Displayed when a web request was successful. -->
<string name="httpErrorOk">OK</string>
- <!-- Displayed when a web request failed because we don't know the exact reason. -->
- <string name="httpError">The Web page contains an error.</string>
+ <!-- Displayed when a web request failed with a generic network error. -->
+ <string name="httpError">A network error occurred.</string>
<!-- Displayed when a web request failed because the URL could not be found. -->
<string name="httpErrorLookup">The URL could not be found.</string>
<!-- Displayed when a web request failed because the site's authentication scheme is not supported by us. -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 21f1cef..0c6e20f 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1744,6 +1744,7 @@
</style>
<style name="Widget.Holo.Spinner.DropDown.ActionBar">
+ <item name="android:background">@android:drawable/spinner_ab_holo_dark</item>
</style>
<style name="Widget.Holo.CompoundButton.Star" parent="Widget.CompoundButton.Star">
@@ -2131,6 +2132,7 @@
</style>
<style name="Widget.Holo.Light.Spinner.DropDown.ActionBar">
+ <item name="android:background">@android:drawable/spinner_ab_holo_light</item>
</style>
<style name="Widget.Holo.Light.CompoundButton.Star" parent="Widget.CompoundButton.Star">
diff --git a/core/tests/bandwidthtests/Android.mk b/core/tests/bandwidthtests/Android.mk
new file mode 100644
index 0000000..3d56141
--- /dev/null
+++ b/core/tests/bandwidthtests/Android.mk
@@ -0,0 +1,30 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# We only want this apk build for tests.
+LOCAL_MODULE_TAGS := tests
+
+# Include all test java files.
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+LOCAL_PACKAGE_NAME := BandwidthTests
+
+include $(BUILD_PACKAGE)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/core/tests/bandwidthtests/AndroidManifest.xml b/core/tests/bandwidthtests/AndroidManifest.xml
new file mode 100644
index 0000000..24221bc
--- /dev/null
+++ b/core/tests/bandwidthtests/AndroidManifest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.bandwidth.tests" >
+
+ <application >
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation
+ android:name="com.android.bandwidthtest.BandwidthTestRunner"
+ android:targetPackage="com.android.bandwidth.tests"
+ android:label="Bandwidth Tests" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+</manifest>
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
new file mode 100644
index 0000000..73c92b0
--- /dev/null
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTest.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bandwidthtest;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo.State;
+import android.net.NetworkStats;
+import android.net.TrafficStats;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Process;
+import android.os.SystemClock;
+import android.telephony.TelephonyManager;
+import android.test.InstrumentationTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+
+import com.android.bandwidthtest.util.BandwidthTestUtil;
+import com.android.bandwidthtest.util.ConnectionUtil;
+
+import java.io.File;
+
+/**
+ * Test that downloads files from a test server and reports the bandwidth metrics collected.
+ */
+public class BandwidthTest extends InstrumentationTestCase {
+
+ private static final String LOG_TAG = "BandwidthTest";
+ private final static String PROF_LABEL = "PROF_";
+ private final static String PROC_LABEL = "PROC_";
+ private final static int INSTRUMENTATION_IN_PROGRESS = 2;
+
+ private final static String BASE_DIR =
+ Environment.getExternalStorageDirectory().getAbsolutePath();
+ private final static String TMP_FILENAME = "tmp.dat";
+ // Download 10.486 * 106 bytes (+ headers) from app engine test server.
+ private final int FILE_SIZE = 10485613;
+ private Context mContext;
+ private ConnectionUtil mConnectionUtil;
+ private TelephonyManager mTManager;
+ private int mUid;
+ private String mSsid;
+ private String mTestServer;
+ private String mDeviceId;
+ private BandwidthTestRunner mRunner;
+
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mRunner = (BandwidthTestRunner) getInstrumentation();
+ mSsid = mRunner.mSsid;
+ mTestServer = mRunner.mTestServer;
+ mContext = mRunner.getTargetContext();
+ mConnectionUtil = new ConnectionUtil(mContext);
+ mConnectionUtil.initialize();
+ Log.v(LOG_TAG, "Initialized mConnectionUtil");
+ mUid = Process.myUid();
+ mTManager = (TelephonyManager)mContext.getSystemService(Context.TELEPHONY_SERVICE);
+ mDeviceId = mTManager.getDeviceId();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mConnectionUtil.cleanUp();
+ super.tearDown();
+ }
+
+ /**
+ * Ensure that downloading on wifi reports reasonable stats.
+ */
+ @LargeTest
+ public void testWifiDownload() {
+ assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
+ NetworkStats pre_test_stats = fetchDataFromProc(mUid);
+ String ts = Long.toString(System.currentTimeMillis());
+
+ String targetUrl = BandwidthTestUtil.buildDownloadUrl(
+ mTestServer, FILE_SIZE, mDeviceId, ts);
+ TrafficStats.startDataProfiling(mContext);
+ File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME);
+ assertTrue(BandwidthTestUtil.DownloadFromUrl(targetUrl, tmpSaveFile));
+ NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext);
+
+ NetworkStats post_test_stats = fetchDataFromProc(mUid);
+ NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats);
+
+ // Output measurements to instrumentation out, so that it can be compared to that of
+ // the server.
+ Bundle results = new Bundle();
+ results.putString("device_id", mDeviceId);
+ results.putString("timestamp", ts);
+ results.putInt("size", FILE_SIZE);
+ AddStatsToResults(PROF_LABEL, prof_stats, results);
+ AddStatsToResults(PROC_LABEL, proc_stats, results);
+ getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
+
+ // Clean up.
+ assertTrue(cleanUpFile(tmpSaveFile));
+ }
+
+ /**
+ * We want to make sure that if we use the Download Manager to download stuff,
+ * accounting still goes to the app making the call and that the numbers still make sense.
+ */
+ @LargeTest
+ public void testWifiDownloadWithDownloadManager() {
+ assertTrue(setDeviceWifiAndAirplaneMode(mSsid));
+ // If we are using the download manager, then the data that is written to /proc/uid_stat/
+ // is accounted against download manager's uid, since it uses pre-ICS API.
+ int downloadManagerUid = mConnectionUtil.downloadManagerUid();
+ assertTrue(downloadManagerUid >= 0);
+ NetworkStats pre_test_stats = fetchDataFromProc(downloadManagerUid);
+ // start profiling
+ TrafficStats.startDataProfiling(mContext);
+ String ts = Long.toString(System.currentTimeMillis());
+ String targetUrl = BandwidthTestUtil.buildDownloadUrl(
+ mTestServer, FILE_SIZE, mDeviceId, ts);
+ Log.v(LOG_TAG, "Download url: " + targetUrl);
+ File tmpSaveFile = new File(BASE_DIR + File.separator + TMP_FILENAME);
+ assertTrue(mConnectionUtil.startDownloadAndWait(targetUrl, 500000));
+ NetworkStats prof_stats = TrafficStats.stopDataProfiling(mContext);
+ NetworkStats post_test_stats = fetchDataFromProc(downloadManagerUid);
+ NetworkStats proc_stats = post_test_stats.subtract(pre_test_stats);
+
+ // Output measurements to instrumentation out, so that it can be compared to that of
+ // the server.
+ Bundle results = new Bundle();
+ results.putString("device_id", mDeviceId);
+ results.putString("timestamp", ts);
+ results.putInt("size", FILE_SIZE);
+ AddStatsToResults(PROF_LABEL, prof_stats, results);
+ AddStatsToResults(PROC_LABEL, proc_stats, results);
+ getInstrumentation().sendStatus(INSTRUMENTATION_IN_PROGRESS, results);
+
+ // Clean up.
+ assertTrue(cleanUpFile(tmpSaveFile));
+ }
+
+ /**
+ * Fetch network data from /proc/uid_stat/uid
+ * @return populated {@link NetworkStats}
+ */
+ public NetworkStats fetchDataFromProc(int uid) {
+ String root_filepath = "/proc/uid_stat/" + uid + "/";
+ File rcv_stat = new File (root_filepath + "tcp_rcv");
+ int rx = BandwidthTestUtil.parseIntValueFromFile(rcv_stat);
+ File snd_stat = new File (root_filepath + "tcp_snd");
+ int tx = BandwidthTestUtil.parseIntValueFromFile(snd_stat);
+ NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 1);
+ stats.addValues(NetworkStats.IFACE_ALL, uid, NetworkStats.TAG_NONE, rx, 0, tx, 0);
+ return stats;
+ }
+
+ /**
+ * Turn on Airplane mode and connect to the wifi
+ * @param ssid of the wifi to connect to
+ * @return true if we successfully connected to a given network.
+ */
+ public boolean setDeviceWifiAndAirplaneMode(String ssid) {
+ mConnectionUtil.setAirplaneMode(mContext, true);
+ assertTrue(mConnectionUtil.connectToWifi(ssid));
+ assertTrue(mConnectionUtil.waitForWifiState(WifiManager.WIFI_STATE_ENABLED,
+ ConnectionUtil.LONG_TIMEOUT));
+ return mConnectionUtil.waitForNetworkState(ConnectivityManager.TYPE_WIFI, State.CONNECTED,
+ ConnectionUtil.LONG_TIMEOUT);
+ }
+
+ /**
+ * Output the {@link NetworkStats} to Instrumentation out.
+ * @param label to attach to this given stats.
+ * @param stats {@link NetworkStats} to add.
+ * @param results {@link Bundle} to be added to.
+ */
+ public void AddStatsToResults(String label, NetworkStats stats, Bundle results){
+ if (results == null || results.isEmpty()) {
+ Log.e(LOG_TAG, "Empty bundle provided.");
+ return;
+ }
+ for (int i = 0; i < stats.size(); ++i) {
+ android.net.NetworkStats.Entry entry = stats.getValues(i, null);
+ results.putInt(label + "uid", entry.uid);
+ results.putString(label + "iface", entry.iface);
+ results.putInt(label + "tag", entry.tag);
+ results.putLong(label + "tx", entry.txBytes);
+ results.putLong(label + "rx", entry.rxBytes);
+ }
+ }
+
+ /**
+ * Remove file if it exists.
+ * @param file {@link File} to delete.
+ * @return true if successfully deleted the file.
+ */
+ private boolean cleanUpFile(File file) {
+ if (file.exists()) {
+ return file.delete();
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTestRunner.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTestRunner.java
new file mode 100644
index 0000000..290ccee
--- /dev/null
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/BandwidthTestRunner.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bandwidthtest;
+
+import android.os.Bundle;
+import android.test.InstrumentationTestRunner;
+
+public class BandwidthTestRunner extends InstrumentationTestRunner {
+ public String mSsid = "wifi-ssid";
+ public String mTestServer = "http://www.sometestserver.com";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ String ssidStr= (String) icicle.get("ssid");
+ if (ssidStr != null) {
+ mSsid = ssidStr;
+ }
+ String serverStr = (String) icicle.get("server");
+ if (serverStr != null) {
+ mTestServer = serverStr;
+ }
+ }
+}
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/NetworkState.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/NetworkState.java
new file mode 100644
index 0000000..fae0e76
--- /dev/null
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/NetworkState.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bandwidthtest;
+
+import android.net.NetworkInfo.State;
+import android.util.Log;
+
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * Data structure to keep track of the network state transitions.
+ */
+public class NetworkState {
+ /**
+ * Desired direction of state transition.
+ */
+ public enum StateTransitionDirection {
+ TO_DISCONNECTION, TO_CONNECTION, DO_NOTHING
+ }
+ private final String LOG_TAG = "NetworkState";
+ private List<State> mStateDepository;
+ private State mTransitionTarget;
+ private StateTransitionDirection mTransitionDirection;
+ private String mReason = null; // record mReason of state transition failure
+
+ public NetworkState() {
+ mStateDepository = new ArrayList<State>();
+ mTransitionDirection = StateTransitionDirection.DO_NOTHING;
+ mTransitionTarget = State.UNKNOWN;
+ }
+
+ public NetworkState(State currentState) {
+ mStateDepository = new ArrayList<State>();
+ mStateDepository.add(currentState);
+ mTransitionDirection = StateTransitionDirection.DO_NOTHING;
+ mTransitionTarget = State.UNKNOWN;
+ }
+
+ /**
+ * Reinitialize the network state
+ */
+ public void resetNetworkState() {
+ mStateDepository.clear();
+ mTransitionDirection = StateTransitionDirection.DO_NOTHING;
+ mTransitionTarget = State.UNKNOWN;
+ }
+
+ /**
+ * Set the transition criteria
+ * @param initState initial {@link State}
+ * @param transitionDir explicit {@link StateTransitionDirection}
+ * @param targetState desired {@link State}
+ */
+ public void setStateTransitionCriteria(State initState, StateTransitionDirection transitionDir,
+ State targetState) {
+ if (!mStateDepository.isEmpty()) {
+ mStateDepository.clear();
+ }
+ mStateDepository.add(initState);
+ mTransitionDirection = transitionDir;
+ mTransitionTarget = targetState;
+ Log.v(LOG_TAG, "setStateTransitionCriteria: " + printStates());
+ }
+
+ /**
+ * Record the current state of the network
+ * @param currentState the current {@link State}
+ */
+ public void recordState(State currentState) {
+ mStateDepository.add(currentState);
+ }
+
+ /**
+ * Verify the state transition
+ * @return true if the requested transition completed successfully.
+ */
+ public boolean validateStateTransition() {
+ Log.v(LOG_TAG, String.format("Print state depository: %s", printStates()));
+ switch (mTransitionDirection) {
+ case DO_NOTHING:
+ Log.v(LOG_TAG, "No direction requested, verifying network states");
+ return validateNetworkStates();
+ case TO_CONNECTION:
+ Log.v(LOG_TAG, "Transition to CONNECTED");
+ return validateNetworkConnection();
+ case TO_DISCONNECTION:
+ Log.v(LOG_TAG, "Transition to DISCONNECTED");
+ return validateNetworkDisconnection();
+ default:
+ Log.e(LOG_TAG, "Invalid transition direction.");
+ return false;
+ }
+ }
+
+ /**
+ * Verify that network states are valid
+ * @return false if any of the states are invalid
+ */
+ private boolean validateNetworkStates() {
+ if (mStateDepository.isEmpty()) {
+ Log.v(LOG_TAG, "no state is recorded");
+ mReason = "no state is recorded.";
+ return false;
+ } else if (mStateDepository.size() > 1) {
+ Log.v(LOG_TAG, "no broadcast is expected, instead broadcast is probably received");
+ mReason = "no broadcast is expected, instead broadcast is probably received";
+ return false;
+ } else if (mStateDepository.get(0) != mTransitionTarget) {
+ Log.v(LOG_TAG, String.format("%s is expected, but it is %s",
+ mTransitionTarget.toString(),
+ mStateDepository.get(0).toString()));
+ mReason = String.format("%s is expected, but it is %s",
+ mTransitionTarget.toString(),
+ mStateDepository.get(0).toString());
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Verify the network state to disconnection
+ * @return false if any of the state transitions were not valid
+ */
+ private boolean validateNetworkDisconnection() {
+ // Transition from CONNECTED -> DISCONNECTED: CONNECTED->DISCONNECTING->DISCONNECTED
+ StringBuffer str = new StringBuffer ("States: ");
+ str.append(printStates());
+ if (mStateDepository.get(0) != State.CONNECTED) {
+ str.append(String.format(" Initial state should be CONNECTED, but it is %s.",
+ mStateDepository.get(0)));
+ mReason = str.toString();
+ return false;
+ }
+ State lastState = mStateDepository.get(mStateDepository.size() - 1);
+ if ( lastState != mTransitionTarget) {
+ str.append(String.format(" Last state should be DISCONNECTED, but it is %s",
+ lastState));
+ mReason = str.toString();
+ return false;
+ }
+ for (int i = 1; i < mStateDepository.size() - 1; i++) {
+ State preState = mStateDepository.get(i-1);
+ State curState = mStateDepository.get(i);
+ if ((preState == State.CONNECTED) && ((curState == State.DISCONNECTING) ||
+ (curState == State.DISCONNECTED))) {
+ continue;
+ } else if ((preState == State.DISCONNECTING) && (curState == State.DISCONNECTED)) {
+ continue;
+ } else if ((preState == State.DISCONNECTED) && (curState == State.DISCONNECTED)) {
+ continue;
+ } else {
+ str.append(String.format(" Transition state from %s to %s is not valid",
+ preState.toString(), curState.toString()));
+ mReason = str.toString();
+ return false;
+ }
+ }
+ mReason = str.toString();
+ return true;
+ }
+
+ /**
+ * Verify the network state to connection
+ * @return false if any of the state transitions were not valid
+ */
+ private boolean validateNetworkConnection() {
+ StringBuffer str = new StringBuffer("States ");
+ str.append(printStates());
+ if (mStateDepository.get(0) != State.DISCONNECTED) {
+ str.append(String.format(" Initial state should be DISCONNECTED, but it is %s.",
+ mStateDepository.get(0)));
+ mReason = str.toString();
+ return false;
+ }
+ State lastState = mStateDepository.get(mStateDepository.size() - 1);
+ if ( lastState != mTransitionTarget) {
+ str.append(String.format(" Last state should be %s, but it is %s", mTransitionTarget,
+ lastState));
+ mReason = str.toString();
+ return false;
+ }
+ for (int i = 1; i < mStateDepository.size(); i++) {
+ State preState = mStateDepository.get(i-1);
+ State curState = mStateDepository.get(i);
+ if ((preState == State.DISCONNECTED) && ((curState == State.CONNECTING) ||
+ (curState == State.CONNECTED) || (curState == State.DISCONNECTED))) {
+ continue;
+ } else if ((preState == State.CONNECTING) && (curState == State.CONNECTED)) {
+ continue;
+ } else if ((preState == State.CONNECTED) && (curState == State.CONNECTED)) {
+ continue;
+ } else {
+ str.append(String.format(" Transition state from %s to %s is not valid.",
+ preState.toString(), curState.toString()));
+ mReason = str.toString();
+ return false;
+ }
+ }
+ mReason = str.toString();
+ return true;
+ }
+
+ /**
+ * Fetch the different network state transitions
+ * @return {@link List} of {@link State}
+ */
+ public List<State> getTransitionStates() {
+ return mStateDepository;
+ }
+
+ /**
+ * Fetch the reason for network state transition failure
+ * @return the {@link String} for the failure
+ */
+ public String getFailureReason() {
+ return mReason;
+ }
+
+ /**
+ * Print the network state
+ * @return {@link String} representation of the network state
+ */
+ public String printStates() {
+ StringBuilder stateBuilder = new StringBuilder();
+ for (int i = 0; i < mStateDepository.size(); i++) {
+ stateBuilder.append(" ").append(mStateDepository.get(i).toString()).append("->");
+ }
+ return stateBuilder.toString();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append("mTransitionDirection: ").append(mTransitionDirection.toString()).
+ append("; ").append("states:").
+ append(printStates()).append("; ");
+ return builder.toString();
+ }
+}
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/BandwidthTestUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/BandwidthTestUtil.java
new file mode 100644
index 0000000..d850169
--- /dev/null
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/BandwidthTestUtil.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bandwidthtest.util;
+
+import android.util.Log;
+
+import org.apache.http.util.ByteArrayBuffer;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+
+public class BandwidthTestUtil {
+ private static final String LOG_TAG = "BandwidthTestUtil";
+ /**
+ * Parses the first line in a file if exists.
+ *
+ * @param file {@link File} the input
+ * @return the integer value of the first line of the file.
+ */
+ public static int parseIntValueFromFile(File file) {
+ int value = 0;
+ if (file.exists()) {
+ try {
+ FileInputStream fstream = new FileInputStream(file);
+ DataInputStream in = new DataInputStream(fstream);
+ BufferedReader br = new BufferedReader(new InputStreamReader(in));
+ String strLine = br.readLine();
+ if (strLine != null) {
+ value = Integer.parseInt(strLine);
+ }
+ // Close the input stream
+ in.close();
+ } catch (Exception e) {
+ System.err.println("Error: " + e.getMessage());
+ }
+ }
+ return value;
+ }
+
+ /**
+ * Creates the Download string for the test server.
+ *
+ * @param server url of the test server
+ * @param size in bytes of the file to download
+ * @param deviceId the device id that is downloading
+ * @param timestamp
+ * @return download url
+ */
+ public static String buildDownloadUrl(String server, int size, String deviceId,
+ String timestamp) {
+ String downloadUrl = server + "/download?size=" + size + "&device_id=" + deviceId +
+ "×tamp=" + timestamp;
+ return downloadUrl;
+ }
+
+ /**
+ * Download a given file from a target url to a given destination file.
+ * @param targetUrl the url to download
+ * @param file the {@link File} location where to save to
+ * @return true if it succeeded.
+ */
+ public static boolean DownloadFromUrl(String targetUrl, File file) {
+ try {
+ URL url = new URL(targetUrl);
+ Log.d(LOG_TAG, "Download begining");
+ Log.d(LOG_TAG, "Download url:" + url);
+ Log.d(LOG_TAG, "Downloaded file name:" + file.getAbsolutePath());
+ URLConnection ucon = url.openConnection();
+ InputStream is = ucon.getInputStream();
+ BufferedInputStream bis = new BufferedInputStream(is);
+ ByteArrayBuffer baf = new ByteArrayBuffer(50);
+ int current = 0;
+ while ((current = bis.read()) != -1) {
+ baf.append((byte) current);
+ }
+ FileOutputStream fos = new FileOutputStream(file);
+ fos.write(baf.toByteArray());
+ fos.close();
+ } catch (IOException e) {
+ Log.d(LOG_TAG, "Failed to download file with error: " + e);
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
new file mode 100644
index 0000000..d663aad
--- /dev/null
+++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.bandwidthtest.util;
+
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.State;
+import android.net.Uri;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiConfiguration.KeyMgmt;
+import android.net.wifi.WifiManager;
+import android.os.Handler;
+import android.os.Message;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.bandwidthtest.NetworkState;
+import com.android.bandwidthtest.NetworkState.StateTransitionDirection;
+import com.android.internal.util.AsyncChannel;
+
+import java.util.List;
+
+/*
+ * Utility class used to set the connectivity of the device and to download files.
+ */
+public class ConnectionUtil {
+ private static final String LOG_TAG = "ConnectionUtil";
+ private static final String DOWNLOAD_MANAGER_PKG_NAME = "com.android.providers.downloads";
+ private static final int WAIT_FOR_SCAN_RESULT = 10 * 1000; // 10 seconds
+ private static final int WIFI_SCAN_TIMEOUT = 50 * 1000;
+ public static final int SHORT_TIMEOUT = 5 * 1000;
+ public static final int LONG_TIMEOUT = 10 * 1000;
+ private ConnectivityReceiver mConnectivityReceiver = null;
+ private WifiReceiver mWifiReceiver = null;
+ private DownloadReceiver mDownloadReceiver = null;
+ private DownloadManager mDownloadManager;
+ private NetworkInfo mNetworkInfo;
+ private NetworkInfo mOtherNetworkInfo;
+ private boolean mScanResultIsAvailable = false;
+ private ConnectivityManager mCM;
+ private Object mWifiMonitor = new Object();
+ private Object mConnectivityMonitor = new Object();
+ private Object mDownloadMonitor = new Object();
+ private int mWifiState;
+ private NetworkInfo mWifiNetworkInfo;
+ private WifiManager mWifiManager;
+ private Context mContext;
+ // Verify connectivity state
+ private static final int NUM_NETWORK_TYPES = ConnectivityManager.MAX_NETWORK_TYPE + 1;
+ private NetworkState[] mConnectivityState = new NetworkState[NUM_NETWORK_TYPES];
+
+ public ConnectionUtil(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Initialize the class. Needs to be called before any other methods in {@link ConnectionUtil}
+ *
+ * @throws Exception
+ */
+ public void initialize() throws Exception {
+ // Register a connectivity receiver for CONNECTIVITY_ACTION
+ mConnectivityReceiver = new ConnectivityReceiver();
+ mContext.registerReceiver(mConnectivityReceiver,
+ new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
+
+ // Register a download receiver for ACTION_DOWNLOAD_COMPLETE
+ mDownloadReceiver = new DownloadReceiver();
+ mContext.registerReceiver(mDownloadReceiver,
+ new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
+
+ // Register a wifi receiver
+ mWifiReceiver = new WifiReceiver();
+ IntentFilter mIntentFilter = new IntentFilter();
+ mIntentFilter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
+ mIntentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
+ mIntentFilter.addAction(WifiManager.SUPPLICANT_CONNECTION_CHANGE_ACTION);
+ mIntentFilter.addAction(WifiManager.WIFI_AP_STATE_CHANGED_ACTION);
+ mContext.registerReceiver(mWifiReceiver, mIntentFilter);
+
+ // Get an instance of ConnectivityManager
+ mCM = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+
+ // Get an instance of WifiManager
+ mWifiManager =(WifiManager)mContext.getSystemService(Context.WIFI_SERVICE);
+ mWifiManager.asyncConnect(mContext, new WifiServiceHandler());
+
+ mDownloadManager = (DownloadManager)mContext.getSystemService(Context.DOWNLOAD_SERVICE);
+
+ initializeNetworkStates();
+
+ mWifiManager.setWifiEnabled(true);
+
+ Log.v(LOG_TAG, "Clear Wifi before we start the test.");
+ sleep(SHORT_TIMEOUT);
+ removeConfiguredNetworksAndDisableWifi();
+ }
+
+
+ /**
+ * A wrapper of a broadcast receiver which provides network connectivity information
+ * for all kinds of network: wifi, mobile, etc.
+ */
+ private class ConnectivityReceiver extends BroadcastReceiver {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (isInitialStickyBroadcast()) {
+ Log.d(LOG_TAG, "This is a sticky broadcast don't do anything.");
+ return;
+ }
+ Log.v(LOG_TAG, "ConnectivityReceiver: onReceive() is called with " + intent);
+ String action = intent.getAction();
+ if (!action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ Log.v("ConnectivityReceiver", "onReceive() called with " + intent);
+ return;
+ }
+ if (intent.hasExtra(ConnectivityManager.EXTRA_NETWORK_INFO)) {
+ mNetworkInfo = (NetworkInfo)
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+ }
+
+ if (intent.hasExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO)) {
+ mOtherNetworkInfo = (NetworkInfo)
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_OTHER_NETWORK_INFO);
+ }
+
+ Log.v(LOG_TAG, "mNetworkInfo: " + mNetworkInfo.toString());
+ recordNetworkState(mNetworkInfo.getType(), mNetworkInfo.getState());
+ if (mOtherNetworkInfo != null) {
+ Log.v(LOG_TAG, "mOtherNetworkInfo: " + mOtherNetworkInfo.toString());
+ recordNetworkState(mOtherNetworkInfo.getType(), mOtherNetworkInfo.getState());
+ }
+ notifyNetworkConnectivityChange();
+ }
+ }
+
+ /**
+ * A wrapper of a broadcast receiver which provides wifi information.
+ */
+ private class WifiReceiver extends BroadcastReceiver {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.v("WifiReceiver", "onReceive() is calleld with " + intent);
+ if (action.equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {
+ Log.v(LOG_TAG, "Scan results are available");
+ notifyScanResult();
+ } else if (action.equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ mWifiNetworkInfo =
+ (NetworkInfo) intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+ Log.v(LOG_TAG, "mWifiNetworkInfo: " + mWifiNetworkInfo.toString());
+ if (mWifiNetworkInfo.getState() == State.CONNECTED) {
+ intent.getStringExtra(WifiManager.EXTRA_BSSID);
+ }
+ notifyWifiState();
+ } else if (action.equals(WifiManager.WIFI_STATE_CHANGED_ACTION)) {
+ mWifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE,
+ WifiManager.WIFI_STATE_UNKNOWN);
+ notifyWifiState();
+ } else if (action.equals(WifiManager.WIFI_AP_STATE_CHANGED_ACTION)) {
+ notifyWifiAPState();
+ } else {
+ return;
+ }
+ }
+ }
+
+ /**
+ * A wrapper of a broadcast receiver which provides download manager information.
+ */
+ private class DownloadReceiver extends BroadcastReceiver {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.v("DownloadReceiver", "onReceive() is called with " + intent);
+ // Download complete
+ if (action.equals(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) {
+ notifiyDownloadState();
+ }
+ }
+ }
+
+ private class WifiServiceHandler extends Handler {
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED:
+ if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) {
+ // AsyncChannel in msg.obj
+ } else {
+ Log.v(LOG_TAG, "Failed to establish AsyncChannel connection");
+ }
+ break;
+ default:
+ // Ignore
+ break;
+ }
+ }
+ }
+
+ /**
+ * Initialize all the network states.
+ */
+ public void initializeNetworkStates() {
+ // For each network type, initialize network states to UNKNOWN, and no verification
+ // flag is set.
+ for (int networkType = NUM_NETWORK_TYPES - 1; networkType >= 0; networkType--) {
+ mConnectivityState[networkType] = new NetworkState();
+ Log.v(LOG_TAG, "Initialize network state for " + networkType + ": " +
+ mConnectivityState[networkType].toString());
+ }
+ }
+
+ public void recordNetworkState(int networkType, State networkState) {
+ // deposit a network state
+ Log.v(LOG_TAG, "record network state for network " + networkType +
+ ", state is " + networkState);
+ mConnectivityState[networkType].recordState(networkState);
+ }
+
+ /**
+ * Set the state transition criteria
+ *
+ * @param networkType
+ * @param initState
+ * @param transitionDir
+ * @param targetState
+ */
+ public void setStateTransitionCriteria(int networkType, State initState,
+ StateTransitionDirection transitionDir, State targetState) {
+ mConnectivityState[networkType].setStateTransitionCriteria(
+ initState, transitionDir, targetState);
+ }
+
+ /**
+ * Validate the states recorded.
+ * @param networkType
+ * @return
+ */
+ public boolean validateNetworkStates(int networkType) {
+ Log.v(LOG_TAG, "validate network state for " + networkType + ": ");
+ return mConnectivityState[networkType].validateStateTransition();
+ }
+
+ /**
+ * Fetch the failure reason for the transition.
+ * @param networkType
+ * @return result from network state validation
+ */
+ public String getTransitionFailureReason(int networkType) {
+ Log.v(LOG_TAG, "get network state transition failure reason for " + networkType + ": " +
+ mConnectivityState[networkType].toString());
+ return mConnectivityState[networkType].getFailureReason();
+ }
+
+ /**
+ * Send a notification via the mConnectivityMonitor when the network connectivity changes.
+ */
+ private void notifyNetworkConnectivityChange() {
+ synchronized(mConnectivityMonitor) {
+ Log.v(LOG_TAG, "notify network connectivity changed");
+ mConnectivityMonitor.notifyAll();
+ }
+ }
+
+ /**
+ * Send a notification when a scan for the wifi network is done.
+ */
+ private void notifyScanResult() {
+ synchronized (this) {
+ Log.v(LOG_TAG, "notify that scan results are available");
+ this.notify();
+ }
+ }
+
+ /**
+ * Send a notification via the mWifiMonitor when the wifi state changes.
+ */
+ private void notifyWifiState() {
+ synchronized (mWifiMonitor) {
+ Log.v(LOG_TAG, "notify wifi state changed.");
+ mWifiMonitor.notify();
+ }
+ }
+
+ /**
+ * Send a notification via the mDownloadMonitor when a download is complete.
+ */
+ private void notifiyDownloadState() {
+ synchronized (mDownloadMonitor) {
+ Log.v(LOG_TAG, "notifiy download manager state changed.");
+ mDownloadMonitor.notify();
+ }
+ }
+
+ /**
+ * Send a notification when the wifi ap state changes.
+ */
+ private void notifyWifiAPState() {
+ synchronized (this) {
+ Log.v(LOG_TAG, "notify wifi AP state changed.");
+ this.notify();
+ }
+ }
+
+ /**
+ * Start a download on a given url and wait for completion.
+ *
+ * @param targetUrl the target to download.x
+ * @param timeout to wait for download to finish
+ * @return true if we successfully downloaded the requestedUrl, false otherwise.
+ */
+ public boolean startDownloadAndWait(String targetUrl, long timeout) {
+ if (targetUrl.length() == 0 || targetUrl == null) {
+ Log.v(LOG_TAG, "Empty or Null target url requested to DownloadManager");
+ return true;
+ }
+ Request request = new Request(Uri.parse(targetUrl));
+ long enqueue = mDownloadManager.enqueue(request);
+ Log.v(LOG_TAG, "Sending download request of " + targetUrl + " to DownloadManager");
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ if ((System.currentTimeMillis() - startTime) > timeout) {
+ Log.v(LOG_TAG, "startDownloadAndWait timed out, failed to fetch " + targetUrl +
+ " within " + timeout);
+ return downloadSuccessful(enqueue);
+ }
+ Log.v(LOG_TAG, "Waiting for the download to finish " + targetUrl);
+ synchronized (mDownloadMonitor) {
+ try {
+ mDownloadMonitor.wait(SHORT_TIMEOUT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (!downloadSuccessful(enqueue)) {
+ continue;
+ }
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Fetch the Download Manager's UID.
+ * @return the Download Manager's UID
+ */
+ public int downloadManagerUid() {
+ try {
+ PackageManager pm = mContext.getPackageManager();
+ ApplicationInfo appInfo = pm.getApplicationInfo(DOWNLOAD_MANAGER_PKG_NAME,
+ PackageManager.GET_META_DATA);
+ return appInfo.uid;
+ } catch (NameNotFoundException e) {
+ Log.d(LOG_TAG, "Did not find the package for the download service.");
+ return -1;
+ }
+ }
+
+ /**
+ * Determines if a given download was successful by querying the DownloadManager.
+ *
+ * @param enqueue the id used to identify/query the DownloadManager with.
+ * @return true if download was successful, false otherwise.
+ */
+ private boolean downloadSuccessful(long enqueue) {
+ Query query = new Query();
+ query.setFilterById(enqueue);
+ Cursor c = mDownloadManager.query(query);
+ if (c.moveToFirst()) {
+ int columnIndex = c.getColumnIndex(DownloadManager.COLUMN_STATUS);
+ if (DownloadManager.STATUS_SUCCESSFUL == c.getInt(columnIndex)) {
+ Log.v(LOG_TAG, "Successfully downloaded file!");
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Wait for network connectivity state.
+ * @param networkType the network to check for
+ * @param expectedState the desired state
+ * @param timeout in milliseconds
+ * @return true if the network connectivity state matched what was expected
+ */
+ public boolean waitForNetworkState(int networkType, State expectedState, long timeout) {
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ if ((System.currentTimeMillis() - startTime) > timeout) {
+ Log.v(LOG_TAG, "waitForNetworkState time out, the state of network type " + networkType +
+ " is: " + mCM.getNetworkInfo(networkType).getState());
+ if (mCM.getNetworkInfo(networkType).getState() != expectedState) {
+ return false;
+ } else {
+ // the broadcast has been sent out. the state has been changed.
+ Log.v(LOG_TAG, "networktype: " + networkType + " state: " +
+ mCM.getNetworkInfo(networkType));
+ return true;
+ }
+ }
+ Log.v(LOG_TAG, "Wait for the connectivity state for network: " + networkType +
+ " to be " + expectedState.toString());
+ synchronized (mConnectivityMonitor) {
+ try {
+ mConnectivityMonitor.wait(SHORT_TIMEOUT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if ((mNetworkInfo.getType() != networkType) ||
+ (mNetworkInfo.getState() != expectedState)) {
+ Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() +
+ "is: " + mNetworkInfo.getState());
+ continue;
+ }
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Wait for a given wifi state to occur within a given timeout.
+ * @param expectedState the expected wifi state.
+ * @param timeout for the state to be set in milliseconds.
+ * @return true if the state was achieved within the timeout, false otherwise.
+ */
+ public boolean waitForWifiState(int expectedState, long timeout) {
+ // Wait for Wifi state: WIFI_STATE_DISABLED, WIFI_STATE_DISABLING, WIFI_STATE_ENABLED,
+ // WIFI_STATE_ENALBING, WIFI_STATE_UNKNOWN
+ long startTime = System.currentTimeMillis();
+ while (true) {
+ if ((System.currentTimeMillis() - startTime) > timeout) {
+ if (mWifiState != expectedState) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+ Log.v(LOG_TAG, "Wait for wifi state to be: " + expectedState);
+ synchronized (mWifiMonitor) {
+ try {
+ mWifiMonitor.wait(SHORT_TIMEOUT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (mWifiState != expectedState) {
+ Log.v(LOG_TAG, "Wifi state is: " + mWifiState);
+ continue;
+ }
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Convenience method to determine if we are connected to a mobile network.
+ * @return true if connected to a mobile network, false otherwise.
+ */
+ public boolean isConnectedToMobile() {
+ return (mNetworkInfo.getType() == ConnectivityManager.TYPE_MOBILE);
+ }
+
+ /**
+ * Convenience method to determine if we are connected to wifi.
+ * @return true if connected to wifi, false otherwise.
+ */
+ public boolean isConnectedToWifi() {
+ return (mNetworkInfo.getType() == ConnectivityManager.TYPE_WIFI);
+ }
+
+
+ /**
+ * Associate the device to given SSID
+ * If the device is already associated with a WiFi, disconnect and forget it,
+ * We don't verify whether the connection is successful or not, leave this to the test
+ */
+ public boolean connectToWifi(String knownSSID) {
+ WifiConfiguration config = new WifiConfiguration();
+ config.SSID = knownSSID;
+ config.allowedKeyManagement.set(KeyMgmt.NONE);
+ return connectToWifiWithConfiguration(config);
+ }
+
+ /**
+ * Connect to Wi-Fi with the given configuration.
+ * @param config
+ * @return true if we ar connected to a given
+ */
+ public boolean connectToWifiWithConfiguration(WifiConfiguration config) {
+ // The SSID in the configuration is a pure string, need to convert it to a quoted string.
+ String ssid = config.SSID;
+ config.SSID = convertToQuotedString(ssid);
+
+ // If wifi is not enabled, enable it
+ if (!mWifiManager.isWifiEnabled()) {
+ Log.v(LOG_TAG, "Wifi is not enabled, enable it");
+ mWifiManager.setWifiEnabled(true);
+ // wait for the wifi state change before start scanning.
+ if (!waitForWifiState(WifiManager.WIFI_STATE_ENABLED, 2 * SHORT_TIMEOUT)) {
+ Log.v(LOG_TAG, "Wait for WIFI_STATE_ENABLED failed");
+ return false;
+ }
+ }
+
+ boolean foundApInScanResults = false;
+ for (int retry = 0; retry < 5; retry++) {
+ List<ScanResult> netList = mWifiManager.getScanResults();
+ if (netList != null) {
+ Log.v(LOG_TAG, "size of scan result list: " + netList.size());
+ for (int i = 0; i < netList.size(); i++) {
+ ScanResult sr= netList.get(i);
+ if (sr.SSID.equals(ssid)) {
+ Log.v(LOG_TAG, "Found " + ssid + " in the scan result list.");
+ Log.v(LOG_TAG, "Retry: " + retry);
+ foundApInScanResults = true;
+ mWifiManager.connectNetwork(config);
+ break;
+ }
+ }
+ }
+ if (foundApInScanResults) {
+ return true;
+ } else {
+ // Start an active scan
+ mWifiManager.startScanActive();
+ mScanResultIsAvailable = false;
+ long startTime = System.currentTimeMillis();
+ while (!mScanResultIsAvailable) {
+ if ((System.currentTimeMillis() - startTime) > WIFI_SCAN_TIMEOUT) {
+ Log.v(LOG_TAG, "wait for scan results timeout");
+ return false;
+ }
+ // wait for the scan results to be available
+ synchronized (this) {
+ // wait for the scan result to be available
+ try {
+ this.wait(WAIT_FOR_SCAN_RESULT);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if ((mWifiManager.getScanResults() == null) ||
+ (mWifiManager.getScanResults().size() <= 0)) {
+ continue;
+ }
+ mScanResultIsAvailable = true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /*
+ * Disconnect from the current AP and remove configured networks.
+ */
+ public boolean disconnectAP() {
+ // remove saved networks
+ List<WifiConfiguration> wifiConfigList = mWifiManager.getConfiguredNetworks();
+ Log.v(LOG_TAG, "size of wifiConfigList: " + wifiConfigList.size());
+ for (WifiConfiguration wifiConfig: wifiConfigList) {
+ Log.v(LOG_TAG, "Remove wifi configuration: " + wifiConfig.networkId);
+ int netId = wifiConfig.networkId;
+ mWifiManager.forgetNetwork(netId);
+ }
+ return true;
+ }
+
+ /**
+ * Enable Wifi
+ * @return true if Wifi is enabled successfully
+ */
+ public boolean enableWifi() {
+ return mWifiManager.setWifiEnabled(true);
+ }
+
+ /**
+ * Disable Wifi
+ * @return true if Wifi is disabled successfully
+ */
+ public boolean disableWifi() {
+ return mWifiManager.setWifiEnabled(false);
+ }
+
+ /**
+ * Remove configured networks and disable wifi
+ */
+ public boolean removeConfiguredNetworksAndDisableWifi() {
+ if (!disconnectAP()) {
+ return false;
+ }
+ sleep(SHORT_TIMEOUT);
+ if (!mWifiManager.setWifiEnabled(false)) {
+ return false;
+ }
+ sleep(SHORT_TIMEOUT);
+ return true;
+ }
+
+ /**
+ * Make the current thread sleep.
+ * @param sleeptime the time to sleep in milliseconds
+ */
+ private void sleep(long sleeptime) {
+ try {
+ Thread.sleep(sleeptime);
+ } catch (InterruptedException e) {}
+ }
+
+ /**
+ * Set airplane mode on device, caller is responsible to ensuring correct state.
+ * @param context {@link Context}
+ * @param enableAM to enable or disable airplane mode.
+ */
+ public void setAirplaneMode(Context context, boolean enableAM) {
+ //set the airplane mode
+ Settings.System.putInt(context.getContentResolver(), Settings.System.AIRPLANE_MODE_ON,
+ enableAM ? 1 : 0);
+ // Post the intent
+ Intent intent = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ intent.putExtra("state", enableAM);
+ context.sendBroadcast(intent);
+ }
+
+ /**
+ * Add quotes around the string.
+ * @param string to convert
+ * @return string with quotes around it
+ */
+ protected static String convertToQuotedString(String string) {
+ return "\"" + string + "\"";
+ }
+
+ public void cleanUp() {
+ // Unregister receivers if defined.
+ if (mConnectivityReceiver != null) {
+ mContext.unregisterReceiver(mConnectivityReceiver);
+ }
+ if (mWifiReceiver != null) {
+ mContext.unregisterReceiver(mWifiReceiver);
+ }
+ if (mDownloadReceiver != null) {
+ mContext.unregisterReceiver(mDownloadReceiver);
+ }
+ Log.v(LOG_TAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
+ }
+}
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java
index f970ffc..6ea2845 100644
--- a/core/tests/coretests/src/android/animation/EventsTest.java
+++ b/core/tests/coretests/src/android/animation/EventsTest.java
@@ -21,6 +21,8 @@
import android.test.suitebuilder.annotation.MediumTest;
import android.test.suitebuilder.annotation.SmallTest;
+import java.util.concurrent.TimeUnit;
+
/**
* 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
@@ -40,7 +42,10 @@
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 static final int FUTURE_RELEASE_DELAY = 50;
+ private static final int TIMEOUT = ANIM_DURATION + ANIM_DELAY + FUTURE_RELEASE_DELAY;
+ private boolean mStarted; // tracks whether we've received the onAnimationStart() callback
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
@@ -51,17 +56,44 @@
// setup() method prior to calling the superclass setup()
/**
- * Cancels the given animator. Used to delay cancelation until some later time (after the
+ * Cancels the given animator. Used to delay cancellation until some later time (after the
* animator has started playing).
*/
static class Canceler implements Runnable {
Animator mAnim;
- public Canceler(Animator anim) {
+ FutureWaiter mFuture;
+ public Canceler(Animator anim, FutureWaiter future) {
mAnim = anim;
+ mFuture = future;
}
@Override
public void run() {
- mAnim.cancel();
+ try {
+ mAnim.cancel();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ };
+
+ /**
+ * Ends the given animator. Used to delay ending until some later time (after the
+ * animator has started playing).
+ */
+ static class Ender implements Runnable {
+ Animator mAnim;
+ FutureWaiter mFuture;
+ public Ender(Animator anim, FutureWaiter future) {
+ mAnim = anim;
+ mFuture = future;
+ }
+ @Override
+ public void run() {
+ try {
+ mAnim.end();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
}
};
@@ -76,6 +108,23 @@
public FutureReleaseListener(FutureWaiter future) {
mFuture = future;
}
+
+ /**
+ * Variant constructor that auto-releases the FutureWaiter after the specified timeout.
+ * @param future
+ * @param timeout
+ */
+ public FutureReleaseListener(FutureWaiter future, long timeout) {
+ mFuture = future;
+ Handler handler = new Handler();
+ handler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mFuture.release();
+ }
+ }, timeout);
+ }
+
@Override
public void onAnimationEnd(Animator animation) {
Handler handler = new Handler();
@@ -84,7 +133,7 @@
public void run() {
mFuture.release();
}
- }, ANIM_MID_DURATION);
+ }, FUTURE_RELEASE_DELAY);
}
};
@@ -92,7 +141,6 @@
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
@@ -107,11 +155,20 @@
// are embedded in the listener callbacks that it implements.
mListener = new AnimatorListenerAdapter() {
@Override
+ public void onAnimationStart(Animator animation) {
+ // This should only be called on an animation that has not yet been started
+ assertFalse(mStarted);
+ assertTrue(mRunning);
+ mStarted = true;
+ }
+
+ @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);
+ assertTrue(mStarted);
mCanceled = true;
}
@@ -120,7 +177,9 @@
// This should only be called on an animation that has been started and not
// yet ended
assertTrue(mRunning);
+ assertTrue(mStarted);
mRunning = false;
+ mStarted = false;
super.onAnimationEnd(animation);
}
};
@@ -132,6 +191,7 @@
mRunning = false;
mCanceled = false;
+ mStarted = false;
}
/**
@@ -144,26 +204,104 @@
}
/**
+ * Verify that calling end on an unstarted animator does nothing.
+ */
+ @UiThreadTest
+ @SmallTest
+ public void testEnd() throws Exception {
+ mAnimator.end();
+ }
+
+ /**
* 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();
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Verify that calling end on a started animator does the right thing.
+ */
+ @UiThreadTest
+ @SmallTest
+ public void testStartEnd() throws Exception {
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.end();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
}
/**
* Same as testStartCancel, but with a startDelayed animator
*/
- @UiThreadTest
@SmallTest
public void testStartDelayedCancel() throws Exception {
+ mFutureListener = new FutureReleaseListener(mFuture);
mAnimator.setStartDelay(ANIM_DELAY);
- mRunning = true;
- mAnimator.start();
- mAnimator.cancel();
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.cancel();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Same as testStartEnd, but with a startDelayed animator
+ */
+ @SmallTest
+ public void testStartDelayedEnd() throws Exception {
+ mFutureListener = new FutureReleaseListener(mFuture);
+ mAnimator.setStartDelay(ANIM_DELAY);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.end();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
}
/**
@@ -180,13 +318,36 @@
mAnimator.addListener(mFutureListener);
mRunning = true;
mAnimator.start();
- handler.postDelayed(new Canceler(mAnimator), ANIM_MID_DURATION);
+ handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
} catch (junit.framework.AssertionFailedError e) {
mFuture.setException(new RuntimeException(e));
}
}
});
- mFuture.get();
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Verify that ending an animator that is playing does the right thing.
+ */
+ @MediumTest
+ public void testPlayingEnd() 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 Ender(mAnimator, mFuture), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
}
/**
@@ -204,13 +365,91 @@
mAnimator.addListener(mFutureListener);
mRunning = true;
mAnimator.start();
- handler.postDelayed(new Canceler(mAnimator), ANIM_MID_DURATION);
+ handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DURATION);
} catch (junit.framework.AssertionFailedError e) {
mFuture.setException(new RuntimeException(e));
}
}
});
- mFuture.get();
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Same as testPlayingEnd, but with a startDelayed animator
+ */
+ @MediumTest
+ public void testPlayingDelayedEnd() 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 Ender(mAnimator, mFuture), ANIM_MID_DURATION);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Same as testPlayingDelayedCancel, but cancel during the startDelay period
+ */
+ @MediumTest
+ public void testPlayingDelayedCancelMidDelay() throws Exception {
+ mAnimator.setStartDelay(ANIM_DELAY);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ // Set the listener to automatically timeout after an uncanceled animation
+ // would have finished. This tests to make sure that we're not calling
+ // the listeners with cancel/end callbacks since they won't be called
+ // with the start event.
+ mFutureListener = new FutureReleaseListener(mFuture, TIMEOUT);
+ Handler handler = new Handler();
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Canceler(mAnimator, mFuture), ANIM_MID_DELAY);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT + 100, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Same as testPlayingDelayedEnd, but end during the startDelay period
+ */
+ @MediumTest
+ public void testPlayingDelayedEndMidDelay() throws Exception {
+ mAnimator.setStartDelay(ANIM_DELAY);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ // Set the listener to automatically timeout after an uncanceled animation
+ // would have finished. This tests to make sure that we're not calling
+ // the listeners with cancel/end callbacks since they won't be called
+ // with the start event.
+ mFutureListener = new FutureReleaseListener(mFuture, TIMEOUT);
+ Handler handler = new Handler();
+ mRunning = true;
+ mAnimator.start();
+ handler.postDelayed(new Ender(mAnimator, mFuture), ANIM_MID_DELAY);
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT + 100, TimeUnit.MILLISECONDS);
}
/**
@@ -234,7 +473,31 @@
}
}
});
- mFuture.get();
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+
+ /**
+ * Verifies that ending a started animation after it has already been ended
+ * does nothing.
+ */
+ @MediumTest
+ public void testStartDoubleEnd() throws Exception {
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.end();
+ mAnimator.end();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
}
/**
@@ -258,8 +521,31 @@
}
}
});
- mFuture.get();
- }
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
+ }
+ /**
+ * Same as testStartDoubleEnd, but with a startDelayed animator
+ */
+ @MediumTest
+ public void testStartDelayedDoubleEnd() throws Exception {
+ mAnimator.setStartDelay(ANIM_DELAY);
+ mFutureListener = new FutureReleaseListener(mFuture);
+ getActivity().runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ mRunning = true;
+ mAnimator.start();
+ mAnimator.end();
+ mAnimator.end();
+ mFuture.release();
+ } catch (junit.framework.AssertionFailedError e) {
+ mFuture.setException(new RuntimeException(e));
+ }
+ }
+ });
+ mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS);
+ }
}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index 242057c..4db4ea5 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -16,6 +16,14 @@
package android.net;
+import static android.net.NetworkStatsHistory.FIELD_ALL;
+import static android.net.NetworkStatsHistory.FIELD_OPERATIONS;
+import static android.net.NetworkStatsHistory.FIELD_RX_BYTES;
+import static android.net.NetworkStatsHistory.FIELD_RX_PACKETS;
+import static android.net.NetworkStatsHistory.FIELD_TX_BYTES;
+import static android.net.NetworkStatsHistory.DataStreamUtils.readVarLong;
+import static android.net.NetworkStatsHistory.DataStreamUtils.writeVarLong;
+import static android.net.NetworkStatsHistory.Entry.UNKNOWN;
import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.HOUR_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -30,7 +38,10 @@
import com.android.frameworks.coretests.R;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
+import java.io.DataOutputStream;
import java.util.Random;
@SmallTest
@@ -39,6 +50,10 @@
private static final long TEST_START = 1194220800000L;
+ private static final long KB_IN_BYTES = 1024;
+ private static final long MB_IN_BYTES = KB_IN_BYTES * 1024;
+ private static final long GB_IN_BYTES = MB_IN_BYTES * 1024;
+
private NetworkStatsHistory stats;
@Override
@@ -80,10 +95,11 @@
stats = new NetworkStatsHistory(BUCKET_SIZE);
// record data into narrow window to get single bucket
- stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L);
+ stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS,
+ new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 2L));
assertEquals(1, stats.size());
- assertValues(stats, 0, 1024L, 2048L);
+ assertValues(stats, 0, 1024L, 10L, 2048L, 20L, 2L);
}
public void testRecordEqualBuckets() throws Exception {
@@ -92,11 +108,12 @@
// split equally across two buckets
final long recordStart = TEST_START + (bucketDuration / 2);
- stats.recordData(recordStart, recordStart + bucketDuration, 1024L, 128L);
+ stats.recordData(recordStart, recordStart + bucketDuration,
+ new NetworkStats.Entry(1024L, 10L, 128L, 2L, 2L));
assertEquals(2, stats.size());
- assertValues(stats, 0, 512L, 64L);
- assertValues(stats, 1, 512L, 64L);
+ assertValues(stats, 0, 512L, 5L, 64L, 1L, 1L);
+ assertValues(stats, 1, 512L, 5L, 64L, 1L, 1L);
}
public void testRecordTouchingBuckets() throws Exception {
@@ -107,15 +124,16 @@
// overlap into neighboring buckets. total record is 20 minutes.
final long recordStart = (TEST_START + BUCKET_SIZE) - MINUTE_IN_MILLIS;
final long recordEnd = (TEST_START + (BUCKET_SIZE * 2)) + (MINUTE_IN_MILLIS * 4);
- stats.recordData(recordStart, recordEnd, 1000L, 5000L);
+ stats.recordData(recordStart, recordEnd,
+ new NetworkStats.Entry(1000L, 2000L, 5000L, 10000L, 100L));
assertEquals(3, stats.size());
// first bucket should have (1/20 of value)
- assertValues(stats, 0, 50L, 250L);
+ assertValues(stats, 0, 50L, 100L, 250L, 500L, 5L);
// second bucket should have (15/20 of value)
- assertValues(stats, 1, 750L, 3750L);
+ assertValues(stats, 1, 750L, 1500L, 3750L, 7500L, 75L);
// final bucket should have (4/20 of value)
- assertValues(stats, 2, 200L, 1000L);
+ assertValues(stats, 2, 200L, 400L, 1000L, 2000L, 20L);
}
public void testRecordGapBuckets() throws Exception {
@@ -125,25 +143,28 @@
// record some data today and next week with large gap
final long firstStart = TEST_START;
final long lastStart = TEST_START + WEEK_IN_MILLIS;
- stats.recordData(firstStart, firstStart + SECOND_IN_MILLIS, 128L, 256L);
- stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS, 64L, 512L);
+ stats.recordData(firstStart, firstStart + SECOND_IN_MILLIS,
+ new NetworkStats.Entry(128L, 2L, 256L, 4L, 1L));
+ stats.recordData(lastStart, lastStart + SECOND_IN_MILLIS,
+ new NetworkStats.Entry(64L, 1L, 512L, 8L, 2L));
// we should have two buckets, far apart from each other
assertEquals(2, stats.size());
- assertValues(stats, 0, 128L, 256L);
- assertValues(stats, 1, 64L, 512L);
+ assertValues(stats, 0, 128L, 2L, 256L, 4L, 1L);
+ assertValues(stats, 1, 64L, 1L, 512L, 8L, 2L);
// now record something in middle, spread across two buckets
final long middleStart = TEST_START + DAY_IN_MILLIS;
final long middleEnd = middleStart + (HOUR_IN_MILLIS * 2);
- stats.recordData(middleStart, middleEnd, 2048L, 2048L);
+ stats.recordData(middleStart, middleEnd,
+ new NetworkStats.Entry(2048L, 4L, 2048L, 4L, 2L));
// now should have four buckets, with new record in middle two buckets
assertEquals(4, stats.size());
- assertValues(stats, 0, 128L, 256L);
- assertValues(stats, 1, 1024L, 1024L);
- assertValues(stats, 2, 1024L, 1024L);
- assertValues(stats, 3, 64L, 512L);
+ assertValues(stats, 0, 128L, 2L, 256L, 4L, 1L);
+ assertValues(stats, 1, 1024L, 2L, 1024L, 2L, 1L);
+ assertValues(stats, 2, 1024L, 2L, 1024L, 2L, 1L);
+ assertValues(stats, 3, 64L, 1L, 512L, 8L, 2L);
}
public void testRecordOverlapBuckets() throws Exception {
@@ -151,14 +172,16 @@
stats = new NetworkStatsHistory(BUCKET_SIZE);
// record some data in one bucket, and another overlapping buckets
- stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 256L, 256L);
+ stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS,
+ new NetworkStats.Entry(256L, 2L, 256L, 2L, 1L));
final long midStart = TEST_START + (HOUR_IN_MILLIS / 2);
- stats.recordData(midStart, midStart + HOUR_IN_MILLIS, 1024L, 1024L);
+ stats.recordData(midStart, midStart + HOUR_IN_MILLIS,
+ new NetworkStats.Entry(1024L, 10L, 1024L, 10L, 10L));
// should have two buckets, with some data mixed together
assertEquals(2, stats.size());
- assertValues(stats, 0, 768L, 768L);
- assertValues(stats, 1, 512L, 512L);
+ assertValues(stats, 0, 768L, 7L, 768L, 7L, 6L);
+ assertValues(stats, 1, 512L, 5L, 512L, 5L, 5L);
}
public void testRecordEntireGapIdentical() throws Exception {
@@ -283,6 +306,7 @@
public void testFuzzing() throws Exception {
try {
// fuzzing with random events, looking for crashes
+ final NetworkStats.Entry entry = new NetworkStats.Entry();
final Random r = new Random();
for (int i = 0; i < 500; i++) {
stats = new NetworkStatsHistory(r.nextLong());
@@ -291,7 +315,12 @@
// add range
final long start = r.nextLong();
final long end = start + r.nextInt();
- stats.recordData(start, end, r.nextLong(), r.nextLong());
+ entry.rxBytes = nextPositiveLong(r);
+ entry.rxPackets = nextPositiveLong(r);
+ entry.txBytes = nextPositiveLong(r);
+ entry.txPackets = nextPositiveLong(r);
+ entry.operations = nextPositiveLong(r);
+ stats.recordData(start, end, entry);
} else {
// trim something
stats.removeBucketsBefore(r.nextLong());
@@ -305,6 +334,88 @@
}
}
+ private static long nextPositiveLong(Random r) {
+ final long value = r.nextLong();
+ return value < 0 ? -value : value;
+ }
+
+ public void testIgnoreFields() throws Exception {
+ final NetworkStatsHistory history = new NetworkStatsHistory(
+ MINUTE_IN_MILLIS, 0, FIELD_RX_BYTES | FIELD_TX_BYTES);
+
+ history.recordData(0, MINUTE_IN_MILLIS,
+ new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
+ history.recordData(0, MINUTE_IN_MILLIS * 2,
+ new NetworkStats.Entry(2L, 2L, 2L, 2L, 2L));
+
+ assertValues(
+ history, Long.MIN_VALUE, Long.MAX_VALUE, 1026L, UNKNOWN, 2050L, UNKNOWN, UNKNOWN);
+ }
+
+ public void testIgnoreFieldsRecordIn() throws Exception {
+ final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL);
+ final NetworkStatsHistory partial = new NetworkStatsHistory(
+ MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS);
+
+ full.recordData(0, MINUTE_IN_MILLIS,
+ new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
+ partial.recordEntireHistory(full);
+
+ assertValues(partial, Long.MIN_VALUE, Long.MAX_VALUE, UNKNOWN, 10L, UNKNOWN, UNKNOWN, 4L);
+ }
+
+ public void testIgnoreFieldsRecordOut() throws Exception {
+ final NetworkStatsHistory full = new NetworkStatsHistory(MINUTE_IN_MILLIS, 0, FIELD_ALL);
+ final NetworkStatsHistory partial = new NetworkStatsHistory(
+ MINUTE_IN_MILLIS, 0, FIELD_RX_PACKETS | FIELD_OPERATIONS);
+
+ partial.recordData(0, MINUTE_IN_MILLIS,
+ new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
+ full.recordEntireHistory(partial);
+
+ assertValues(full, Long.MIN_VALUE, Long.MAX_VALUE, 0L, 10L, 0L, 0L, 4L);
+ }
+
+ public void testSerialize() throws Exception {
+ final NetworkStatsHistory before = new NetworkStatsHistory(MINUTE_IN_MILLIS, 40, FIELD_ALL);
+ before.recordData(0, MINUTE_IN_MILLIS * 4,
+ new NetworkStats.Entry(1024L, 10L, 2048L, 20L, 4L));
+ before.recordData(DAY_IN_MILLIS, DAY_IN_MILLIS + MINUTE_IN_MILLIS,
+ new NetworkStats.Entry(10L, 20L, 30L, 40L, 50L));
+
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ before.writeToStream(new DataOutputStream(out));
+ out.close();
+
+ final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ final NetworkStatsHistory after = new NetworkStatsHistory(new DataInputStream(in));
+
+ // must have identical totals before and after
+ assertValues(before, Long.MIN_VALUE, Long.MAX_VALUE, 1034L, 30L, 2078L, 60L, 54L);
+ assertValues(after, Long.MIN_VALUE, Long.MAX_VALUE, 1034L, 30L, 2078L, 60L, 54L);
+ }
+
+ public void testVarLong() throws Exception {
+ assertEquals(0L, performVarLong(0L));
+ assertEquals(-1L, performVarLong(-1L));
+ assertEquals(1024L, performVarLong(1024L));
+ assertEquals(-1024L, performVarLong(-1024L));
+ assertEquals(40 * MB_IN_BYTES, performVarLong(40 * MB_IN_BYTES));
+ assertEquals(512 * GB_IN_BYTES, performVarLong(512 * GB_IN_BYTES));
+ assertEquals(Long.MIN_VALUE, performVarLong(Long.MIN_VALUE));
+ assertEquals(Long.MAX_VALUE, performVarLong(Long.MAX_VALUE));
+ assertEquals(Long.MIN_VALUE + 40, performVarLong(Long.MIN_VALUE + 40));
+ assertEquals(Long.MAX_VALUE - 40, performVarLong(Long.MAX_VALUE - 40));
+ }
+
+ private static long performVarLong(long before) throws Exception {
+ final ByteArrayOutputStream out = new ByteArrayOutputStream();
+ writeVarLong(new DataOutputStream(out), before);
+
+ final ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
+ return readVarLong(new DataInputStream(in));
+ }
+
private static void assertConsistent(NetworkStatsHistory stats) {
// verify timestamps are monotonic
long lastStart = Long.MIN_VALUE;
@@ -330,4 +441,23 @@
assertEquals("unexpected txBytes", txBytes, entry.txBytes);
}
+ private static void assertValues(NetworkStatsHistory stats, int index, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, long operations) {
+ final NetworkStatsHistory.Entry entry = stats.getValues(index, null);
+ assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+ assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
+ assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+ assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+ assertEquals("unexpected operations", operations, entry.operations);
+ }
+
+ private static void assertValues(NetworkStatsHistory stats, long start, long end, long rxBytes,
+ long rxPackets, long txBytes, long txPackets, long operations) {
+ final NetworkStatsHistory.Entry entry = stats.getValues(start, end, null);
+ assertEquals("unexpected rxBytes", rxBytes, entry.rxBytes);
+ assertEquals("unexpected rxPackets", rxPackets, entry.rxPackets);
+ assertEquals("unexpected txBytes", txBytes, entry.txBytes);
+ assertEquals("unexpected txPackets", txPackets, entry.txPackets);
+ assertEquals("unexpected operations", operations, entry.operations);
+ }
}
diff --git a/docs/html/guide/topics/manifest/uses-feature-element.jd b/docs/html/guide/topics/manifest/uses-feature-element.jd
index ef98a6a..49d6b62 100644
--- a/docs/html/guide/topics/manifest/uses-feature-element.jd
+++ b/docs/html/guide/topics/manifest/uses-feature-element.jd
@@ -677,20 +677,36 @@
<td>The application uses basic touch interaction events, such as "click down", "click
up", and drag.</td>
<td>When declared, this indicates that the application is compatible with a device that offers an
-emulated touchscreen (or better). A device that offers an emulated touchscreen provides a user input
-system that can emulate a subset of touchscreen capabilities. An example of such an input system is
-a mouse or remote control that drives an on-screen cursor. If your application does not require
-complicated gestures and you want your application available to devices that use an on-screen
-cursor to emulate touch events, you should declare this feature.</td>
+emulated touchscreen ("fake touch" interface), or better. A device that offers a fake touch
+interface provides a user input system that emulates a subset of touchscreen capabilities. For
+example, a mouse or remote control that drives an on-screen cursor provides a fake touch interface.
+If your application requires only basic point and click interaction, you should declare this
+feature. Because this is the minimum level of touch interaction, your app will also be compatible
+with devices that offer more complex touch interfaces.
+ <p class="note"><strong>Note:</strong> Because applications require the {@code
+android.hardware.touchscreen} feature by default, if you want your application to be available to
+devices that provide a fake touch interface, you must also explicitly declare that a touch screen is
+<em>not</em> required by declaring {@code <uses-feature
+android:name="android.hardware.touchscreen" <strong>android:required="false"</strong>
+/>}</p></td>
</tr>
<tr>
<td><code>android.hardware.touchscreen</code></td>
- <td>The application uses touchscreen capabilities, for gestures more interactive
-than basic touches, such as a fling. This is a superset of the faketouch features.</td>
- <td>By default, this is assumed to be required, unless you declare
-<code>android.hardware.faketouch</code> (the subset touch mode). As such, your application is
-<em>not</em> available to devices that provide only an emulated touch interface ("fake touch") by
-default.</td>
+ <td>The application uses touchscreen capabilities for gestures that are more interactive
+than basic touch events, such as a fling. This is a superset of the faketouch features.</td>
+ <td>By default, your application requires this. As such, your application is
+<em>not</em> available to devices that provide only an emulated touch interface ("fake touch"), by
+default. If you want your application available to devices that provide a fake touch interface,
+you must explicitly declare that a touch screen is not required, by
+declaring {@code android.hardware.touchscreen} with {@code android:required="false"}. You should
+do so even if your application uses—but does not <em>require</em>—a real touch screen
+interface.
+<p>If your application <em>does require</em> a basic touch interface (in order to perform touch
+gestures such as a fling), then you don't need to do anything, because this is required by default.
+However, it's best if you explicitly declare all features used by your application, so you should
+still declare this if your app uses it.</p>
+ <p>If you require more complex touch interaction, such as multi-finger gestures, you
+should declare the advanced touch screen features below.</p></td>
</tr>
<tr>
<td><code>android.hardware.touchscreen.multitouch</code></td>
diff --git a/drm/libdrmframework/plugins/common/util/include/SessionMap.h b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
index 3dff58c..e563894 100644
--- a/drm/libdrmframework/plugins/common/util/include/SessionMap.h
+++ b/drm/libdrmframework/plugins/common/util/include/SessionMap.h
@@ -13,141 +13,175 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
#ifndef __SESSIONMAP_H__
#define __SESSIONMAP_H__
#include <utils/KeyedVector.h>
+#include <utils/threads.h>
namespace android {
/**
- * A wrapper template class for handling DRM Engine sessions.
+ * A thread safe wrapper template class for session handlings for Drm Engines. It wraps a
+ * pointer type over KeyedVector. It keeps pointer as data in the vector and free up memory
+ * allocated pointer can be of any type of structure/class meant for keeping session data.
+ * so session object here means pointer to the session data.
*/
-template <typename NODE>
+template <typename TValue>
class SessionMap {
public:
- KeyedVector<int, NODE> map;
-
SessionMap() {}
virtual ~SessionMap() {
+ Mutex::Autolock lock(mLock);
destroyMap();
}
-/**
- * Adds a new value in the session map table. It expects memory to be allocated already
- * for the session object
- *
- * @param key - key or Session ID
- * @param value - session object to add
- *
- * @return boolean result of adding value. returns false if key is already exist.
- */
-bool addValue(int key, NODE value) {
- bool result = false;
-
- if (!isCreated(key)) {
- map.add(key, value);
- result = true;
+ /**
+ * Adds a new value in the session map table. It expects memory to be allocated already
+ * for the session object
+ *
+ * @param key - key or Session ID
+ * @param value - session object to add
+ *
+ * @return boolean result of adding value. returns false if key is already exist.
+ */
+ bool addValue(int key, TValue value) {
+ Mutex::Autolock lock(mLock);
+ if (!isCreatedInternal(key)) {
+ map.add(key, value);
+ return true;
+ }
+ return false;
}
- return result;
-}
-
-
-/**
- * returns the session object by the key
- *
- * @param key - key or Session ID
- *
- * @return session object as per the key
- */
-NODE getValue(int key) {
- NODE value = NULL;
-
- if (isCreated(key)) {
- value = (NODE) map.valueFor(key);
+ /**
+ * returns the session object by the key
+ *
+ * @param key - key or Session ID
+ *
+ * @return session object as per the key
+ */
+ TValue getValue(int key) {
+ Mutex::Autolock lock(mLock);
+ return getValueInternal(key);
}
- return value;
-}
-
-/**
- * returns the number of objects in the session map table
- *
- * @return count of number of session objects.
- */
-int getSize() {
- return map.size();
-}
-
-/**
- * returns the session object by the index in the session map table
- *
- * @param index - index of the value required
- *
- * @return session object as per the index
- */
-NODE getValueAt(unsigned int index) {
- NODE value = NULL;
-
- if (map.size() > index) {
- value = map.valueAt(index);
+ /**
+ * returns the number of objects in the session map table
+ *
+ * @return count of number of session objects.
+ */
+ int getSize() {
+ Mutex::Autolock lock(mLock);
+ return map.size();
}
- return value;
-}
+ /**
+ * returns the session object by the index in the session map table
+ *
+ * @param index - index of the value required
+ *
+ * @return session object as per the index
+ */
+ TValue getValueAt(unsigned int index) {
+ TValue value = NULL;
+ Mutex::Autolock lock(mLock);
-/**
- * deletes the object from session map. It also frees up memory for the session object.
- *
- * @param key - key of the value to be deleted
- *
- */
-void removeValue(int key) {
- deleteValue(getValue(key));
- map.removeItem(key);
-}
-
-/**
- * decides if session is already created.
- *
- * @param key - key of the value for the session
- *
- * @return boolean result of whether session is created
- */
-bool isCreated(int key) {
- return (0 <= map.indexOfKey(key));
-}
-
-/**
- * empty the entire session table. It releases all the memory for session objects.
- */
-void destroyMap() {
- int size = map.size();
- int i = 0;
-
- for (i = 0; i < size; i++) {
- deleteValue(map.valueAt(i));
+ if (map.size() > index) {
+ value = map.valueAt(index);
+ }
+ return value;
}
- map.clear();
-}
+ /**
+ * deletes the object from session map. It also frees up memory for the session object.
+ *
+ * @param key - key of the value to be deleted
+ *
+ */
+ void removeValue(int key) {
+ Mutex::Autolock lock(mLock);
+ deleteValue(getValueInternal(key));
+ map.removeItem(key);
+ }
-/**
- * free up the memory for the session object.
- * Make sure if any reference to the session object anywhere, otherwise it will be a
- * dangle pointer after this call.
- *
- * @param value - session object to free
- *
- */
-void deleteValue(NODE value) {
- delete value;
-}
+ /**
+ * decides if session is already created.
+ *
+ * @param key - key of the value for the session
+ *
+ * @return boolean result of whether session is created
+ */
+ bool isCreated(int key) {
+ Mutex::Autolock lock(mLock);
+ return isCreatedInternal(key);
+ }
+ SessionMap<TValue> & operator=(const SessionMap<TValue> & objectCopy) {
+ Mutex::Autolock lock(mLock);
+
+ destroyMap();
+ map = objectCopy.map;
+ return *this;
+ }
+
+private:
+ KeyedVector<int, TValue> map;
+ Mutex mLock;
+
+ /**
+ * free up the memory for the session object.
+ * Make sure if any reference to the session object anywhere, otherwise it will be a
+ * dangle pointer after this call.
+ *
+ * @param value - session object to free
+ *
+ */
+ void deleteValue(TValue value) {
+ delete value;
+ }
+
+ /**
+ * free up the memory for the entire map.
+ * free up any resources in the sessions before calling this funtion.
+ *
+ */
+ void destroyMap() {
+ int size = map.size();
+
+ for (int i = 0; i < size; i++) {
+ deleteValue(map.valueAt(i));
+ }
+ map.clear();
+ }
+
+ /**
+ * decides if session is already created.
+ *
+ * @param key - key of the value for the session
+ *
+ * @return boolean result of whether session is created
+ */
+ bool isCreatedInternal(int key) {
+ return(0 <= map.indexOfKey(key));
+ }
+
+ /**
+ * returns the session object by the key
+ *
+ * @param key - key or Session ID
+ *
+ * @return session object as per the key
+ */
+ TValue getValueInternal(int key) {
+ TValue value = NULL;
+ if (isCreatedInternal(key)) {
+ value = (TValue) map.valueFor(key);
+ }
+ return value;
+ }
};
};
diff --git a/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
index 4ee903e..57ef799 100644
--- a/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
+++ b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp
@@ -22,6 +22,13 @@
#undef LOG_TAG
#define LOG_TAG "MimeTypeUtil"
+#ifdef DRM_OMA_FL_ENGINE_DEBUG
+#define LOG_NDEBUG 0
+#define LOG_DEBUG(...) LOGD(__VA_ARGS__)
+#else
+#define LOG_DEBUG(...)
+#endif
+
enum {
MIMETYPE_AUDIO = 0,
MIMETYPE_APPLICATION = 1,
@@ -59,6 +66,7 @@
static const char mime_group_application[] = "application/";
static const char mime_group_image[] = "image/";
static const char mime_group_video[] = "video/";
+static const char mime_type_unsupported[] = "unsupported/drm.mimetype";
static struct MimeGroup mimeGroup[] = {
{MIMETYPE_AUDIO, mime_group_audio, sizeof(mime_group_audio)-1},
@@ -107,48 +115,52 @@
* replacement mimetype otherwise the original mimetype
* is returned.
*
+ * If the mimetype is of unsupported group i.e. application/*
+ * then "unsupported/drm.mimetype" will be returned.
+ *
* @param mimeType - mimetype in lower case to convert.
*
- * @return mimetype or null.
+ * @return mimetype or "unsupported/drm.mimetype".
*/
String8 MimeTypeUtil::convertMimeType(String8& mimeType) {
String8 result = mimeType;
- const char* pTmp;
const char* pMimeType;
struct MimeGroup* pGroup;
struct MimeTypeList* pMimeItem;
int len;
-
pMimeType = mimeType.string();
if (NULL != pMimeType) {
- /* Check which group the mimetype is */
- pGroup = mimeGroup;
-
- while (MIMETYPE_LAST != pGroup->type) {
- if (0 == strncmp(pMimeType, pGroup->pGroup, pGroup->size)) {
- break;
- }
- pGroup++;
- }
-
- /* Go through the mimetype list. Only check items of the correct group */
- if (MIMETYPE_LAST != pGroup->type) {
- pMimeItem = mimeTypeList;
- len = strlen (pMimeType+pGroup->size);
-
- while (MIMETYPE_LAST != pMimeItem->type) {
- if ((len == pMimeItem->size) &&
- (0 == strcmp(pMimeType+pGroup->size, pMimeItem->pMimeExt))) {
- result = String8(pMimeItem->pMimeType);
+ if ((0 == strncmp(pMimeType, mime_group_audio, (sizeof mime_group_audio) - 1)) ||
+ (0 == strncmp(pMimeType, mime_group_video, (sizeof mime_group_video) - 1))) {
+ /* Check which group the mimetype is */
+ pGroup = mimeGroup;
+ while (MIMETYPE_LAST != pGroup->type) {
+ if (0 == strncmp(pMimeType, pGroup->pGroup, pGroup->size)) {
break;
}
- pMimeItem++;
+ pGroup++;
}
- }
- LOGI("convertMimeType got mimetype %s, converted into mimetype %s",
- pMimeType, result.string());
- }
+ /* Go through the mimetype list. Only check items of the correct group */
+ if (MIMETYPE_LAST != pGroup->type) {
+ pMimeItem = mimeTypeList;
+ len = strlen (pMimeType+pGroup->size);
+ while (MIMETYPE_LAST != pMimeItem->type) {
+ if ((pGroup->type == pMimeItem->type) &&
+ (len == pMimeItem->size) &&
+ (0 == strcmp(pMimeType+pGroup->size, pMimeItem->pMimeExt))) {
+ result = String8(pMimeItem->pMimeType);
+ break;
+ }
+ pMimeItem++;
+ }
+ }
+ } else {
+ result = String8(mime_type_unsupported);
+ }
+ LOG_DEBUG("convertMimeType got mimetype %s, converted into mimetype %s",
+ pMimeType, result.string());
+ }
return result;
}
};
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
index 9805a40..e359dbd 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk
@@ -17,6 +17,9 @@
include $(CLEAR_VARS)
+# The flag below turns on local debug printouts
+#LOCAL_CFLAGS += -DDRM_OMA_FL_ENGINE_DEBUG
+
base := frameworks/base
# Determine whether the DRM framework uses 64-bit data types for file offsets and do the same.
diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
index 31c3c14..e184545 100644
--- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
+++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp
@@ -41,6 +41,13 @@
#undef LOG_TAG
#define LOG_TAG "FwdLockEngine"
+#ifdef DRM_OMA_FL_ENGINE_DEBUG
+#define LOG_NDEBUG 0
+#define LOG_VERBOSE(...) LOGV(__VA_ARGS__)
+#else
+#define LOG_VERBOSE(...)
+#endif
+
using namespace android;
// This extern "C" is mandatory to be managed by TPlugInManager
extern "C" IDrmEngine* create() {
@@ -53,14 +60,25 @@
}
FwdLockEngine::FwdLockEngine() {
- LOGV("FwdLockEngine Construction");
+ LOG_VERBOSE("FwdLockEngine Construction");
}
FwdLockEngine::~FwdLockEngine() {
- LOGV("FwdLockEngine Destruction");
+ LOG_VERBOSE("FwdLockEngine Destruction");
- convertSessionMap.destroyMap();
- decodeSessionMap.destroyMap();
+ int size = decodeSessionMap.getSize();
+
+ for (int i = 0; i < size; i++) {
+ DecodeSession *session = (DecodeSession*) decodeSessionMap.getValueAt(i);
+ FwdLockFile_detach(session->fileDesc);
+ ::close(session->fileDesc);
+ }
+
+ size = convertSessionMap.getSize();
+ for (int i = 0; i < size; i++) {
+ ConvertSession *convSession = (ConvertSession*) convertSessionMap.getValueAt(i);
+ FwdLockConv_CloseSession(convSession->uniqueId, &(convSession->output));
+ }
}
int FwdLockEngine::getConvertedStatus(FwdLockConv_Status_t status) {
@@ -74,12 +92,12 @@
case FwdLockConv_Status_InvalidArgument:
case FwdLockConv_Status_UnsupportedFileFormat:
case FwdLockConv_Status_UnsupportedContentTransferEncoding:
- LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+ LOGE("FwdLockEngine getConvertedStatus: file conversion Error %d. "
"Returning STATUS_INPUTDATA_ERROR", status);
retStatus = DrmConvertedStatus::STATUS_INPUTDATA_ERROR;
break;
default:
- LOGD("FwdLockEngine getConvertedStatus: file conversion Error %d. " \
+ LOGE("FwdLockEngine getConvertedStatus: file conversion Error %d. "
"Returning STATUS_ERROR", status);
retStatus = DrmConvertedStatus::STATUS_ERROR;
break;
@@ -91,7 +109,7 @@
DrmConstraints* FwdLockEngine::onGetConstraints(int uniqueId, const String8* path, int action) {
DrmConstraints* drmConstraints = NULL;
- LOGV("FwdLockEngine::onGetConstraints");
+ LOG_VERBOSE("FwdLockEngine::onGetConstraints");
if (NULL != path &&
(RightsStatus::RIGHTS_VALID == onCheckRightsStatus(uniqueId, *path, action))) {
@@ -105,7 +123,7 @@
DrmMetadata* FwdLockEngine::onGetMetadata(int uniqueId, const String8* path) {
DrmMetadata* drmMetadata = NULL;
- LOGV("FwdLockEngine::onGetMetadata");
+ LOG_VERBOSE("FwdLockEngine::onGetMetadata");
if (NULL != path) {
// Returns empty metadata to show no error condition.
@@ -116,13 +134,12 @@
}
android::status_t FwdLockEngine::onInitialize(int uniqueId) {
- LOGV("FwdLockEngine::onInitialize");
-
+ LOG_VERBOSE("FwdLockEngine::onInitialize");
if (FwdLockGlue_InitializeKeyEncryption()) {
- LOGV("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption succeeded");
+ LOG_VERBOSE("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption succeeded");
} else {
- LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption failed:"
+ LOGE("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption failed:"
"errno = %d", errno);
}
@@ -132,13 +149,13 @@
android::status_t
FwdLockEngine::onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener) {
// Not used
- LOGV("FwdLockEngine::onSetOnInfoListener");
+ LOG_VERBOSE("FwdLockEngine::onSetOnInfoListener");
return DRM_NO_ERROR;
}
android::status_t FwdLockEngine::onTerminate(int uniqueId) {
- LOGV("FwdLockEngine::onTerminate");
+ LOG_VERBOSE("FwdLockEngine::onTerminate");
return DRM_NO_ERROR;
}
@@ -146,7 +163,7 @@
DrmSupportInfo* FwdLockEngine::onGetSupportInfo(int uniqueId) {
DrmSupportInfo* pSupportInfo = new DrmSupportInfo();
- LOGV("FwdLockEngine::onGetSupportInfo");
+ LOG_VERBOSE("FwdLockEngine::onGetSupportInfo");
// fill all Forward Lock mimetypes and extensions
if (NULL != pSupportInfo) {
@@ -182,7 +199,7 @@
drmInfoStatus = new DrmInfoStatus((int)DrmInfoStatus::STATUS_OK, 0, NULL, String8(""));
- LOGV("FwdLockEngine::onProcessDrmInfo");
+ LOG_VERBOSE("FwdLockEngine::onProcessDrmInfo");
return drmInfoStatus;
}
@@ -193,7 +210,7 @@
const String8& rightsPath,
const String8& contentPath) {
// No rights to save. Return
- LOGV("FwdLockEngine::onSaveRights");
+ LOG_VERBOSE("FwdLockEngine::onSaveRights");
return DRM_ERROR_UNKNOWN;
}
@@ -201,7 +218,7 @@
DrmInfo* drmInfo = NULL;
// Nothing to be done for Forward Lock file
- LOGV("FwdLockEngine::onAcquireDrmInfo");
+ LOG_VERBOSE("FwdLockEngine::onAcquireDrmInfo");
return drmInfo;
}
@@ -211,7 +228,7 @@
int action) {
int result = RightsStatus::RIGHTS_INVALID;
- LOGV("FwdLockEngine::onCheckRightsStatus");
+ LOG_VERBOSE("FwdLockEngine::onCheckRightsStatus");
// Only Transfer action is not allowed for forward Lock files.
if (onCanHandle(uniqueId, path)) {
@@ -241,7 +258,7 @@
int action,
bool reserve) {
// No rights consumption
- LOGV("FwdLockEngine::onConsumeRights");
+ LOG_VERBOSE("FwdLockEngine::onConsumeRights");
return DRM_NO_ERROR;
}
@@ -249,14 +266,14 @@
const String8& path,
int action,
const ActionDescription& description) {
- LOGV("FwdLockEngine::onValidateAction");
+ LOG_VERBOSE("FwdLockEngine::onValidateAction");
// For the forwardlock engine checkRights and ValidateAction are the same.
return (onCheckRightsStatus(uniqueId, path, action) == RightsStatus::RIGHTS_VALID);
}
String8 FwdLockEngine::onGetOriginalMimeType(int uniqueId, const String8& path) {
- LOGV("FwdLockEngine::onGetOriginalMimeType");
+ LOG_VERBOSE("FwdLockEngine::onGetOriginalMimeType");
String8 mimeString = String8("");
int fileDesc = FwdLockFile_open(path.string());
@@ -280,7 +297,7 @@
const String8& mimeType) {
String8 mimeStr = String8(mimeType);
- LOGV("FwdLockEngine::onGetDrmObjectType");
+ LOG_VERBOSE("FwdLockEngine::onGetDrmObjectType");
mimeStr.toLower();
@@ -301,13 +318,13 @@
status_t FwdLockEngine::onRemoveRights(int uniqueId, const String8& path) {
// No Rights to remove
- LOGV("FwdLockEngine::onRemoveRights");
+ LOG_VERBOSE("FwdLockEngine::onRemoveRights");
return DRM_NO_ERROR;
}
status_t FwdLockEngine::onRemoveAllRights(int uniqueId) {
// No rights to remove
- LOGV("FwdLockEngine::onRemoveAllRights");
+ LOG_VERBOSE("FwdLockEngine::onRemoveAllRights");
return DRM_NO_ERROR;
}
@@ -319,14 +336,14 @@
int playbackStatus, int position) {
#endif
// Not used
- LOGV("FwdLockEngine::onSetPlaybackStatus");
+ LOG_VERBOSE("FwdLockEngine::onSetPlaybackStatus");
return DRM_NO_ERROR;
}
status_t FwdLockEngine::onOpenConvertSession(int uniqueId,
int convertId) {
status_t result = DRM_ERROR_UNKNOWN;
- LOGV("FwdLockEngine::onOpenConvertSession");
+ LOG_VERBOSE("FwdLockEngine::onOpenConvertSession");
if (!convertSessionMap.isCreated(convertId)) {
ConvertSession *newSession = new ConvertSession();
if (FwdLockConv_Status_OK ==
@@ -334,7 +351,7 @@
convertSessionMap.addValue(convertId, newSession);
result = DRM_NO_ERROR;
} else {
- LOGD("FwdLockEngine::onOpenConvertSession -- FwdLockConv_OpenSession failed.");
+ LOGE("FwdLockEngine::onOpenConvertSession -- FwdLockConv_OpenSession failed.");
delete newSession;
}
}
@@ -383,7 +400,7 @@
DrmBuffer *convResult = new DrmBuffer(NULL, 0);
int offset = -1;
- LOGV("FwdLockEngine::onCloseConvertSession");
+ LOG_VERBOSE("FwdLockEngine::onCloseConvertSession");
if (convertSessionMap.isCreated(convertId)) {
ConvertSession *convSession = convertSessionMap.getValue(convertId);
@@ -424,14 +441,14 @@
status_t result = DRM_ERROR_CANNOT_HANDLE;
int fileDesc = -1;
- LOGV("FwdLockEngine::onOpenDecryptSession");
+ LOG_VERBOSE("FwdLockEngine::onOpenDecryptSession");
if ((-1 < fd) &&
(NULL != decryptHandle) &&
(!decodeSessionMap.isCreated(decryptHandle->decryptId))) {
fileDesc = dup(fd);
} else {
- LOGD("FwdLockEngine::onOpenDecryptSession parameter error");
+ LOGE("FwdLockEngine::onOpenDecryptSession parameter error");
return result;
}
@@ -453,7 +470,7 @@
decryptHandle->decryptInfo = NULL;
result = DRM_NO_ERROR;
} else {
- LOGD("FwdLockEngine::onOpenDecryptSession Integrity Check failed for the fd");
+ LOG_VERBOSE("FwdLockEngine::onOpenDecryptSession Integrity Check failed for the fd");
FwdLockFile_detach(fileDesc);
delete decodeSession;
}
@@ -463,7 +480,7 @@
::close(fileDesc);
}
- LOGV("FwdLockEngine::onOpenDecryptSession Exit. result = %d", result);
+ LOG_VERBOSE("FwdLockEngine::onOpenDecryptSession Exit. result = %d", result);
return result;
}
@@ -500,7 +517,7 @@
status_t FwdLockEngine::onCloseDecryptSession(int uniqueId,
DecryptHandle* decryptHandle) {
status_t result = DRM_ERROR_UNKNOWN;
- LOGV("FwdLockEngine::onCloseDecryptSession");
+ LOG_VERBOSE("FwdLockEngine::onCloseDecryptSession");
if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) {
DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId);
@@ -525,7 +542,7 @@
decryptHandle = NULL;
}
- LOGV("FwdLockEngine::onCloseDecryptSession Exit");
+ LOG_VERBOSE("FwdLockEngine::onCloseDecryptSession Exit");
return result;
}
@@ -533,13 +550,13 @@
DecryptHandle* decryptHandle,
int decryptUnitId,
const DrmBuffer* headerInfo) {
- LOGV("FwdLockEngine::onInitializeDecryptUnit");
+ LOGE("FwdLockEngine::onInitializeDecryptUnit is not supported for this DRM scheme");
return DRM_ERROR_UNKNOWN;
}
status_t FwdLockEngine::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId,
const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) {
- LOGV("FwdLockEngine::onDecrypt");
+ LOGE("FwdLockEngine::onDecrypt is not supported for this DRM scheme");
return DRM_ERROR_UNKNOWN;
}
@@ -548,14 +565,14 @@
int decryptUnitId,
const DrmBuffer* encBuffer,
DrmBuffer** decBuffer) {
- LOGV("FwdLockEngine::onDecrypt");
+ LOGE("FwdLockEngine::onDecrypt is not supported for this DRM scheme");
return DRM_ERROR_UNKNOWN;
}
status_t FwdLockEngine::onFinalizeDecryptUnit(int uniqueId,
DecryptHandle* decryptHandle,
int decryptUnitId) {
- LOGV("FwdLockEngine::onFinalizeDecryptUnit");
+ LOGE("FwdLockEngine::onFinalizeDecryptUnit is not supported for this DRM scheme");
return DRM_ERROR_UNKNOWN;
}
@@ -633,11 +650,11 @@
if (((off_t)-1) != decoderSession->offset) {
bytesRead = onRead(uniqueId, decryptHandle, buffer, numBytes);
if (bytesRead < 0) {
- LOGD("FwdLockEngine::onPread error reading");
+ LOGE("FwdLockEngine::onPread error reading");
}
}
} else {
- LOGD("FwdLockEngine::onPread decryptId not found");
+ LOGE("FwdLockEngine::onPread decryptId not found");
}
return bytesRead;
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
index 14ea9e9..299116d 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/converter/FwdLockConv.c
@@ -275,17 +275,18 @@
}
/**
- * Checks whether a given character is valid in a boundary. Note that the boundary may contain
- * leading and internal spaces.
+ * Checks whether a given character is valid in a boundary. Allows some non-standard characters that
+ * are invalid according to RFC 2046 but nevertheless used by one vendor's DRM packager. Note that
+ * the boundary may contain leading and internal spaces.
*
* @param[in] ch The character to check.
*
* @return A Boolean value indicating whether the given character is valid in a boundary.
*/
static int FwdLockConv_IsBoundaryChar(int ch) {
- return isalnum(ch) || ch == '\'' ||
- ch == '(' || ch == ')' || ch == '+' || ch == '_' || ch == ',' || ch == '-' ||
- ch == '.' || ch == '/' || ch == ':' || ch == '=' || ch == '?' || ch == ' ';
+ return isalnum(ch) || ch == '\'' || ch == '(' || ch == ')' || ch == '+' || ch == '_' ||
+ ch == ',' || ch == '-' || ch == '.' || ch == '/' || ch == ':' || ch == '=' ||
+ ch == '?' || ch == ' ' || ch == '%' || ch == '[' || ch == '&' || ch == '*' || ch == '^';
}
/**
@@ -1085,6 +1086,13 @@
status = FwdLockConv_MatchBinaryEncodedData(pSession, ch, pOutput);
break;
case FwdLockConv_ParserState_WantsBase64EncodedData:
+ if (ch == '\n' && pSession->scannerState != FwdLockConv_ScannerState_WantsLF) {
+ // Repair base64-encoded data that doesn't have carriage returns in its line breaks.
+ status = FwdLockConv_MatchBase64EncodedData(pSession, '\r', pOutput);
+ if (status != FwdLockConv_Status_OK) {
+ break;
+ }
+ }
status = FwdLockConv_MatchBase64EncodedData(pSession, ch, pOutput);
break;
case FwdLockConv_ParserState_Done:
@@ -1199,7 +1207,7 @@
status = FwdLockConv_Status_SyntaxError;
} else {
// Finalize the data signature.
- size_t signatureSize;
+ unsigned int signatureSize = SHA1_HASH_SIZE;
HMAC_Final(&pSession->signingContext, pOutput->fromCloseSession.signatures,
&signatureSize);
if (signatureSize != SHA1_HASH_SIZE) {
@@ -1214,9 +1222,9 @@
HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
pSession->encryptedSessionKeyLength);
HMAC_Update(&pSession->signingContext, pOutput->fromCloseSession.signatures,
- signatureSize);
- HMAC_Final(&pSession->signingContext, &pOutput->fromCloseSession.
- signatures[signatureSize], &signatureSize);
+ SHA1_HASH_SIZE);
+ HMAC_Final(&pSession->signingContext,
+ &pOutput->fromCloseSession.signatures[SHA1_HASH_SIZE], &signatureSize);
if (signatureSize != SHA1_HASH_SIZE) {
status = FwdLockConv_Status_ProgramError;
} else {
diff --git a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
index 98284e72..dacf00e 100644
--- a/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
+++ b/drm/libdrmframework/plugins/forward-lock/internal-format/decoder/FwdLockFile.c
@@ -114,7 +114,7 @@
}
/**
- * Finds the file session associated to the given file descriptor.
+ * Finds the file session associated with the given file descriptor.
*
* @param[in] fileDesc A file descriptor.
*
@@ -389,7 +389,7 @@
result = FALSE;
} else {
ssize_t numBytesRead;
- size_t signatureSize = SHA1_HASH_SIZE;
+ unsigned int signatureSize = SHA1_HASH_SIZE;
while ((numBytesRead =
read(pSession->fileDesc, pData->buffer, SIG_CALC_BUFFER_SIZE)) > 0) {
HMAC_Update(&pSession->signingContext, pData->buffer, (size_t)numBytesRead);
@@ -399,7 +399,7 @@
} else {
HMAC_Final(&pSession->signingContext, pData->signature, &signatureSize);
assert(signatureSize == SHA1_HASH_SIZE);
- result = memcmp(pData->signature, pSession->dataSignature, signatureSize) == 0;
+ result = memcmp(pData->signature, pSession->dataSignature, SHA1_HASH_SIZE) == 0;
}
HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
(void)lseek64(pSession->fileDesc, pSession->dataOffset + pSession->filePos,
@@ -419,16 +419,16 @@
} else {
FwdLockFile_Session_t *pSession = sessionPtrs[sessionId];
unsigned char signature[SHA1_HASH_SIZE];
- size_t signatureSize = SHA1_HASH_SIZE;
+ unsigned int signatureSize = SHA1_HASH_SIZE;
HMAC_Update(&pSession->signingContext, pSession->topHeader, TOP_HEADER_SIZE);
HMAC_Update(&pSession->signingContext, (unsigned char *)pSession->pContentType,
pSession->contentTypeLength);
HMAC_Update(&pSession->signingContext, pSession->pEncryptedSessionKey,
pSession->encryptedSessionKeyLength);
- HMAC_Update(&pSession->signingContext, pSession->dataSignature, signatureSize);
+ HMAC_Update(&pSession->signingContext, pSession->dataSignature, SHA1_HASH_SIZE);
HMAC_Final(&pSession->signingContext, signature, &signatureSize);
assert(signatureSize == SHA1_HASH_SIZE);
- result = memcmp(signature, pSession->headerSignature, signatureSize) == 0;
+ result = memcmp(signature, pSession->headerSignature, SHA1_HASH_SIZE) == 0;
HMAC_Init_ex(&pSession->signingContext, NULL, KEY_SIZE, NULL, NULL);
}
return result;
diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java
index 4856ab6..571b895 100644
--- a/graphics/java/android/renderscript/RenderScript.java
+++ b/graphics/java/android/renderscript/RenderScript.java
@@ -19,6 +19,8 @@
import java.lang.reflect.Field;
import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -28,6 +30,7 @@
import android.view.Surface;
+
/**
* RenderScript base master class. An instance of this class creates native
* worker threads for processing commands from this object. This base class
@@ -79,26 +82,26 @@
// Methods below are wrapped to protect the non-threadsafe
// lockless fifo.
- native int rsnContextCreateGL(int dev, int ver,
+ native int rsnContextCreateGL(int dev, int ver, int sdkVer,
int colorMin, int colorPref,
int alphaMin, int alphaPref,
int depthMin, int depthPref,
int stencilMin, int stencilPref,
int samplesMin, int samplesPref, float samplesQ, int dpi);
- synchronized int nContextCreateGL(int dev, int ver,
+ synchronized int nContextCreateGL(int dev, int ver, int sdkVer,
int colorMin, int colorPref,
int alphaMin, int alphaPref,
int depthMin, int depthPref,
int stencilMin, int stencilPref,
int samplesMin, int samplesPref, float samplesQ, int dpi) {
- return rsnContextCreateGL(dev, ver, colorMin, colorPref,
+ return rsnContextCreateGL(dev, ver, sdkVer, colorMin, colorPref,
alphaMin, alphaPref, depthMin, depthPref,
stencilMin, stencilPref,
samplesMin, samplesPref, samplesQ, dpi);
}
- native int rsnContextCreate(int dev, int ver);
- synchronized int nContextCreate(int dev, int ver) {
- return rsnContextCreate(dev, ver);
+ native int rsnContextCreate(int dev, int ver, int sdkVer);
+ synchronized int nContextCreate(int dev, int ver, int sdkVer) {
+ return rsnContextCreate(dev, ver, sdkVer);
}
native void rsnContextDestroy(int con);
synchronized void nContextDestroy() {
@@ -864,6 +867,16 @@
return mApplicationContext;
}
+ static int getTargetSdkVersion(Context ctx) {
+ try {
+ PackageManager pm = ctx.getPackageManager();
+ ApplicationInfo app = pm.getApplicationInfo(ctx.getPackageName(), 0);
+ return app.targetSdkVersion;
+ } catch (Exception e) {
+ throw new RSDriverException("Error calculating target SDK version for RS.");
+ }
+ }
+
/**
* Create a basic RenderScript context.
*
@@ -873,8 +886,10 @@
public static RenderScript create(Context ctx) {
RenderScript rs = new RenderScript(ctx);
+ int sdkVersion = getTargetSdkVersion(ctx);
+
rs.mDev = rs.nDeviceCreate();
- rs.mContext = rs.nContextCreate(rs.mDev, 0);
+ rs.mContext = rs.nContextCreate(rs.mDev, 0, sdkVersion);
if (rs.mContext == 0) {
throw new RSDriverException("Failed to create RS context.");
}
diff --git a/graphics/java/android/renderscript/RenderScriptGL.java b/graphics/java/android/renderscript/RenderScriptGL.java
index 935b75a..2dfcc83f 100644
--- a/graphics/java/android/renderscript/RenderScriptGL.java
+++ b/graphics/java/android/renderscript/RenderScriptGL.java
@@ -160,11 +160,13 @@
super(ctx);
mSurfaceConfig = new SurfaceConfig(sc);
+ int sdkVersion = getTargetSdkVersion(ctx);
+
mWidth = 0;
mHeight = 0;
mDev = nDeviceCreate();
int dpi = ctx.getResources().getDisplayMetrics().densityDpi;
- mContext = nContextCreateGL(mDev, 0,
+ mContext = nContextCreateGL(mDev, 0, sdkVersion,
mSurfaceConfig.mColorMin, mSurfaceConfig.mColorPref,
mSurfaceConfig.mAlphaMin, mSurfaceConfig.mAlphaPref,
mSurfaceConfig.mDepthMin, mSurfaceConfig.mDepthPref,
diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp
index 3476bd5..d7ac5d8 100644
--- a/graphics/jni/android_renderscript_RenderScript.cpp
+++ b/graphics/jni/android_renderscript_RenderScript.cpp
@@ -149,14 +149,14 @@
}
static jint
-nContextCreate(JNIEnv *_env, jobject _this, jint dev, jint ver)
+nContextCreate(JNIEnv *_env, jobject _this, jint dev, jint ver, jint sdkVer)
{
LOG_API("nContextCreate");
- return (jint)rsContextCreate((RsDevice)dev, ver);
+ return (jint)rsContextCreate((RsDevice)dev, ver, sdkVer);
}
static jint
-nContextCreateGL(JNIEnv *_env, jobject _this, jint dev, jint ver,
+nContextCreateGL(JNIEnv *_env, jobject _this, jint dev, jint ver, jint sdkVer,
int colorMin, int colorPref,
int alphaMin, int alphaPref,
int depthMin, int depthPref,
@@ -176,7 +176,7 @@
sc.samplesQ = samplesQ;
LOG_API("nContextCreateGL");
- return (jint)rsContextCreateGL((RsDevice)dev, ver, sc, dpi);
+ return (jint)rsContextCreateGL((RsDevice)dev, ver, sdkVer, sc, dpi);
}
static void
@@ -1213,8 +1213,8 @@
// All methods below are thread protected in java.
-{"rsnContextCreate", "(II)I", (void*)nContextCreate },
-{"rsnContextCreateGL", "(IIIIIIIIIIIIFI)I", (void*)nContextCreateGL },
+{"rsnContextCreate", "(III)I", (void*)nContextCreate },
+{"rsnContextCreateGL", "(IIIIIIIIIIIIIFI)I", (void*)nContextCreateGL },
{"rsnContextFinish", "(I)V", (void*)nContextFinish },
{"rsnContextSetPriority", "(II)V", (void*)nContextSetPriority },
{"rsnContextSetSurface", "(IIILandroid/view/Surface;)V", (void*)nContextSetSurface },
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
index c1c4f94..3d79596 100644
--- a/include/media/stagefright/MediaBuffer.h
+++ b/include/media/stagefright/MediaBuffer.h
@@ -25,6 +25,7 @@
namespace android {
+struct ABuffer;
class GraphicBuffer;
class MediaBuffer;
class MediaBufferObserver;
@@ -51,6 +52,8 @@
MediaBuffer(const sp<GraphicBuffer>& graphicBuffer);
+ MediaBuffer(const sp<ABuffer> &buffer);
+
// Decrements the reference count and returns the buffer to its
// associated MediaBufferGroup if the reference count drops to 0.
void release();
@@ -100,6 +103,7 @@
void *mData;
size_t mSize, mRangeOffset, mRangeLength;
sp<GraphicBuffer> mGraphicBuffer;
+ sp<ABuffer> mBuffer;
bool mOwnsData;
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 535f713..3ba0123 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -52,8 +52,8 @@
RsDevice rsDeviceCreate();
void rsDeviceDestroy(RsDevice dev);
void rsDeviceSetConfig(RsDevice dev, RsDeviceParam p, int32_t value);
-RsContext rsContextCreate(RsDevice dev, uint32_t version);
-RsContext rsContextCreateGL(RsDevice dev, uint32_t version, RsSurfaceConfig sc, uint32_t dpi);
+RsContext rsContextCreate(RsDevice dev, uint32_t version, uint32_t sdkVersion);
+RsContext rsContextCreateGL(RsDevice dev, uint32_t version, uint32_t sdkVersion, RsSurfaceConfig sc, uint32_t dpi);
#include "rsgApiFuncDecl.h"
diff --git a/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp
index 86bec6e..fb2df37 100644
--- a/libs/rs/driver/rsdBcc.cpp
+++ b/libs/rs/driver/rsdBcc.cpp
@@ -19,7 +19,7 @@
#include "rsdBcc.h"
#include "rsdRuntime.h"
-#include <bcinfo/bcinfo.h>
+#include <bcinfo/MetadataExtractor.h>
#include "rsContext.h"
#include "rsScriptC.h"
@@ -40,7 +40,7 @@
BCCScriptRef mBccScript;
- struct BCScriptMetadata *mScriptMetadata;
+ bcinfo::MetadataExtractor *ME;
InvokeFunc_t *mInvokeFunctions;
void ** mFieldAddress;
@@ -71,7 +71,9 @@
pthread_mutex_lock(&rsdgInitMutex);
char *cachePath = NULL;
- struct BCScriptMetadata *md = NULL;
+ size_t exportFuncCount = 0;
+ size_t exportVarCount = 0;
+ size_t objectSlotCount = 0;
DrvScript *drv = (DrvScript *)calloc(1, sizeof(DrvScript));
if (drv == NULL) {
@@ -84,13 +86,13 @@
drv->mScriptText = bitcode;
drv->mScriptTextLength = bitcodeSize;
- md = bcinfoGetScriptMetadata((const char*)drv->mScriptText,
- drv->mScriptTextLength, 0);
- if (!md) {
+
+ drv->ME = new bcinfo::MetadataExtractor((const char*)drv->mScriptText,
+ drv->mScriptTextLength);
+ if (!drv->ME->extract()) {
LOGE("bcinfo: failed to read script metadata");
goto error;
}
- drv->mScriptMetadata = md;
//LOGE("mBccScript %p", script->mBccScript);
@@ -122,40 +124,41 @@
drv->mRoot = reinterpret_cast<int (*)()>(bccGetFuncAddr(drv->mBccScript, "root"));
drv->mInit = reinterpret_cast<void (*)()>(bccGetFuncAddr(drv->mBccScript, "init"));
- if (md->exportFuncCount > 0) {
- drv->mInvokeFunctions = (InvokeFunc_t*) calloc(md->exportFuncCount,
+ exportFuncCount = drv->ME->getExportFuncCount();
+ if (exportFuncCount > 0) {
+ drv->mInvokeFunctions = (InvokeFunc_t*) calloc(exportFuncCount,
sizeof(InvokeFunc_t));
- bccGetExportFuncList(drv->mBccScript,
- md->exportFuncCount,
+ bccGetExportFuncList(drv->mBccScript, exportFuncCount,
(void **) drv->mInvokeFunctions);
} else {
drv->mInvokeFunctions = NULL;
}
- if (md->exportVarCount > 0) {
- drv->mFieldAddress = (void **) calloc(md->exportVarCount,
- sizeof(void*));
- drv->mFieldIsObject = (bool *) calloc(md->exportVarCount, sizeof(bool));
- bccGetExportVarList(drv->mBccScript,
- md->exportVarCount,
+ exportVarCount = drv->ME->getExportVarCount();
+ if (exportVarCount > 0) {
+ drv->mFieldAddress = (void **) calloc(exportVarCount, sizeof(void*));
+ drv->mFieldIsObject = (bool *) calloc(exportVarCount, sizeof(bool));
+ bccGetExportVarList(drv->mBccScript, exportVarCount,
(void **) drv->mFieldAddress);
} else {
drv->mFieldAddress = NULL;
drv->mFieldIsObject = NULL;
}
- if (md->objectSlotCount) {
- for (uint32_t ct=0; ct < md->objectSlotCount; ct++) {
- drv->mFieldIsObject[md->objectSlotList[ct]] = true;
+ objectSlotCount = drv->ME->getObjectSlotCount();
+ if (objectSlotCount > 0) {
+ const uint32_t *objectSlotList = drv->ME->getObjectSlotList();
+ for (uint32_t ct=0; ct < objectSlotCount; ct++) {
+ drv->mFieldIsObject[objectSlotList[ct]] = true;
}
}
// Copy info over to runtime
- script->mHal.info.exportedFunctionCount = md->exportFuncCount;
- script->mHal.info.exportedVariableCount = md->exportVarCount;
- script->mHal.info.exportedPragmaCount = md->pragmaCount;
- script->mHal.info.exportedPragmaKeyList = md->pragmaKeyList;
- script->mHal.info.exportedPragmaValueList = md->pragmaValueList;
+ script->mHal.info.exportedFunctionCount = drv->ME->getExportFuncCount();
+ script->mHal.info.exportedVariableCount = drv->ME->getExportVarCount();
+ script->mHal.info.exportedPragmaCount = drv->ME->getPragmaCount();
+ script->mHal.info.exportedPragmaKeyList = drv->ME->getPragmaKeyList();
+ script->mHal.info.exportedPragmaValueList = drv->ME->getPragmaValueList();
script->mHal.info.root = drv->mRoot;
pthread_mutex_unlock(&rsdgInitMutex);
@@ -164,6 +167,10 @@
error:
pthread_mutex_unlock(&rsdgInitMutex);
+ if (drv->ME) {
+ delete drv->ME;
+ drv->ME = NULL;
+ }
free(drv);
return false;
@@ -445,10 +452,10 @@
void rsdScriptDestroy(const Context *dc, Script *script) {
DrvScript *drv = (DrvScript *)script->mHal.drv;
- struct BCScriptMetadata *md = drv->mScriptMetadata;
if (drv->mFieldAddress) {
- for (size_t ct = 0; ct < md->exportVarCount; ct++) {
+ size_t exportVarCount = drv->ME->getExportVarCount();
+ for (size_t ct = 0; ct < exportVarCount; ct++) {
if (drv->mFieldIsObject[ct]) {
// The field address can be NULL if the script-side has
// optimized the corresponding global variable away.
@@ -467,7 +474,8 @@
drv->mInvokeFunctions = NULL;
}
- bcinfoReleaseScriptMetadata(&drv->mScriptMetadata);
+ delete drv->ME;
+ drv->ME = NULL;
free(drv);
script->mHal.drv = NULL;
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index decd9f1..6a30b17 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -333,6 +333,7 @@
mPaused = false;
mObjHead = NULL;
mError = RS_ERROR_NONE;
+ mTargetSdkVersion = 14;
mDPI = 96;
mIsContextLite = false;
}
@@ -678,19 +679,25 @@
}
}
-RsContext rsContextCreate(RsDevice vdev, uint32_t version) {
+RsContext rsContextCreate(RsDevice vdev, uint32_t version,
+ uint32_t sdkVersion) {
LOGV("rsContextCreate %p", vdev);
Device * dev = static_cast<Device *>(vdev);
Context *rsc = Context::createContext(dev, NULL);
+ if (rsc) {
+ rsc->setTargetSdkVersion(sdkVersion);
+ }
return rsc;
}
RsContext rsContextCreateGL(RsDevice vdev, uint32_t version,
- RsSurfaceConfig sc, uint32_t dpi) {
+ uint32_t sdkVersion, RsSurfaceConfig sc,
+ uint32_t dpi) {
LOGV("rsContextCreateGL %p", vdev);
Device * dev = static_cast<Device *>(vdev);
Context *rsc = Context::createContext(dev, &sc);
if (rsc) {
+ rsc->setTargetSdkVersion(sdkVersion);
rsc->setDPI(dpi);
}
LOGV("rsContextCreateGL ret %p ", rsc);
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 309fe95..3c7a3d2 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -199,9 +199,13 @@
uint32_t getDPI() const {return mDPI;}
void setDPI(uint32_t dpi) {mDPI = dpi;}
+ uint32_t getTargetSdkVersion() const {return mTargetSdkVersion;}
+ void setTargetSdkVersion(uint32_t sdkVer) {mTargetSdkVersion = sdkVer;}
+
Device *mDev;
protected:
+ uint32_t mTargetSdkVersion;
uint32_t mDPI;
uint32_t mWidth;
uint32_t mHeight;
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index e8b1014..dccf71f 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -19,6 +19,10 @@
#include "utils/Timers.h"
#include "utils/StopWatch.h"
+#ifndef ANDROID_RS_SERIALIZE
+#include <bcinfo/BitcodeTranslator.h>
+#endif
+
using namespace android;
using namespace android::renderscript;
@@ -28,9 +32,18 @@
ScriptC * sc = (ScriptC *) tls->mScript
ScriptC::ScriptC(Context *rsc) : Script(rsc) {
+#ifndef ANDROID_RS_SERIALIZE
+ BT = NULL;
+#endif
}
ScriptC::~ScriptC() {
+#ifndef ANDROID_RS_SERIALIZE
+ if (BT) {
+ delete BT;
+ BT = NULL;
+ }
+#endif
mRSC->mHal.funcs.script.destroy(mRSC, this);
}
@@ -181,6 +194,22 @@
size_t bitcodeLen) {
//LOGE("runCompiler %p %p %p %p %p %i", rsc, this, resName, cacheDir, bitcode, bitcodeLen);
+#ifndef ANDROID_RS_SERIALIZE
+ uint32_t sdkVersion = rsc->getTargetSdkVersion();
+ if (BT) {
+ delete BT;
+ }
+ BT = new bcinfo::BitcodeTranslator((const char *)bitcode, bitcodeLen,
+ sdkVersion);
+ if (!BT->translate()) {
+ LOGE("Failed to translate bitcode from version: %u", sdkVersion);
+ delete BT;
+ BT = NULL;
+ return false;
+ }
+ bitcode = (const uint8_t *) BT->getTranslatedBitcode();
+ bitcodeLen = BT->getTranslatedBitcodeSize();
+#endif
rsc->mHal.funcs.script.init(rsc, this, resName, cacheDir, bitcode, bitcodeLen, 0);
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
index 5c191d9..c65a5bf 100644
--- a/libs/rs/rsScriptC.h
+++ b/libs/rs/rsScriptC.h
@@ -21,6 +21,9 @@
#include "RenderScriptEnv.h"
+#ifndef ANDROID_RS_SERIALIZE
+#include "bcinfo/BitcodeTranslator.h"
+#endif
// ---------------------------------------------------------------------------
namespace android {
@@ -61,6 +64,10 @@
void setupScript(Context *);
void setupGLState(Context *);
Script * setTLS(Script *);
+ private:
+#ifndef ANDROID_RS_SERIALIZE
+ bcinfo::BitcodeTranslator *BT;
+#endif
};
class ScriptCState {
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
index a8fadf2c..0b14f1e 100644
--- a/media/libstagefright/MediaBuffer.cpp
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -21,6 +21,7 @@
#include <pthread.h>
#include <stdlib.h>
+#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDebug.h>
#include <media/stagefright/MetaData.h>
@@ -70,6 +71,20 @@
mOriginal(NULL) {
}
+MediaBuffer::MediaBuffer(const sp<ABuffer> &buffer)
+ : mObserver(NULL),
+ mNextBuffer(NULL),
+ mRefCount(0),
+ mData(buffer->data()),
+ mSize(buffer->size()),
+ mRangeOffset(0),
+ mRangeLength(mSize),
+ mBuffer(buffer),
+ mOwnsData(false),
+ mMetaData(new MetaData),
+ mOriginal(NULL) {
+}
+
void MediaBuffer::release() {
if (mObserver == NULL) {
CHECK_EQ(mRefCount, 0);
diff --git a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
index 07a9eb8..887fe7c 100644
--- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
+++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp
@@ -47,6 +47,8 @@
delete mDelegate;
mDelegate = NULL;
+ clearDRMState_l();
+
if (mDrmManagerClient != NULL) {
delete mDrmManagerClient;
mDrmManagerClient = NULL;
@@ -116,8 +118,6 @@
// mURI.clear();
mIOResult = err;
-
- clearDRMState_l();
}
void ChromiumHTTPDataSource::disconnect() {
@@ -251,8 +251,6 @@
// mURI.clear();
mCondition.broadcast();
-
- clearDRMState_l();
}
sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization() {
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 59de17e..2e66a2c 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -105,12 +105,10 @@
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
- MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
+ MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
+
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
- // hexdump(buffer->data(), buffer->size());
-
- memcpy(mediaBuffer->data(), buffer->data(), buffer->size());
*out = mediaBuffer;
return OK;
}
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index a02591f..4ecb92f 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -628,14 +628,12 @@
updateNormalPlayTime_l(buffer);
- MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
-
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+ MediaBuffer *mediaBuffer = new MediaBuffer(buffer);
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
- memcpy(mediaBuffer->data(), buffer->data(), buffer->size());
*out = mediaBuffer;
mBuffers.erase(mBuffers.begin());
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index f527447..f4890e0 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -907,20 +907,12 @@
if (upgradeVersion == 68) {
// Enable all system sounds by default
db.beginTransaction();
- SQLiteStatement stmt = null;
try {
- stmt = db.compileStatement("INSERT OR REPLACE INTO system(name,value)"
- + " VALUES(?,?);");
- loadDefaultHapticSettings(stmt);
- loadUISoundEffectsSettings(stmt);
db.execSQL("DELETE FROM system WHERE name='"
+ Settings.System.NOTIFICATIONS_USE_RING_VOLUME + "'");
- stmt.close();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
- if (stmt != null)
- stmt.close();
}
upgradeVersion = 69;
}
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
index 55e57ab..a204f17 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
@@ -35,18 +35,6 @@
android:clipChildren="false"
>
- <ImageView android:id="@+id/clear_all_button"
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:layout_alignParentRight="true"
- android:layout_marginTop="1dp"
- android:layout_marginRight="20dp"
- android:paddingLeft="15dp"
- android:paddingRight="15dp"
- android:src="@drawable/ic_notify_clear"
- android:visibility="invisible"
- />
-
<!-- notification icons & panel access -->
<include layout="@layout/status_bar_notification_area"
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
index 9f11e08..1641c70 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_panel.xml
@@ -24,6 +24,18 @@
android:gravity="right"
>
+ <ImageView android:id="@+id/clear_all_button"
+ android:layout_width="wrap_content"
+ android:layout_height="@*android:dimen/status_bar_height"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentBottom="true"
+ android:layout_marginRight="20dp"
+ android:paddingLeft="15dp"
+ android:paddingRight="15dp"
+ android:src="@drawable/ic_notify_clear"
+ android:visibility="invisible"
+ />
+
<RelativeLayout
android:id="@+id/content_parent"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index 25eab26..7d1aedc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -87,6 +87,9 @@
static final boolean SPEW = false;
public static final boolean DEBUG = false;
+ // additional instrumentation for testing purposes; intended to be left on during development
+ public static final boolean CHATTY = DEBUG || true;
+
public static final String ACTION_STATUSBAR_START
= "com.android.internal.policy.statusbar.START";
@@ -1261,6 +1264,10 @@
}
void prepareTracking(int y, boolean opening) {
+ if (CHATTY) {
+ Slog.d(TAG, "panel: beginning to track the user's touch, y=" + y + " opening=" + opening);
+ }
+
mTracking = true;
mVelocityTracker = VelocityTracker.obtain();
if (opening) {
@@ -1290,6 +1297,10 @@
}
void performFling(int y, float vel, boolean always) {
+ if (CHATTY) {
+ Slog.d(TAG, "panel: will fling, y=" + y + " vel=" + vel);
+ }
+
mAnimatingReveal = false;
mAnimY = y;
@@ -1352,6 +1363,12 @@
if (SPEW) {
Slog.d(TAG, "Touch: rawY=" + event.getRawY() + " event=" + event + " mDisabled="
+ mDisabled);
+ } else if (CHATTY) {
+ if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ Slog.d(TAG, String.format(
+ "panel: ACTION_DOWN at (%f, %f) mDisabled=0x%08x",
+ event.getRawX(), event.getRawY(), mDisabled));
+ }
}
if ((mDisabled & StatusBarManager.DISABLE_EXPAND) != 0) {
@@ -1873,7 +1890,7 @@
}
void updateExpandedSize() {
- if (mExpandedDialog != null && mExpandedParams != null) {
+ if (mExpandedDialog != null && mExpandedParams != null && mDisplaySize != null) {
mExpandedParams.width = mDisplaySize.x;
mExpandedParams.height = getExpandedHeight(mDisplaySize.y);
if (!mExpandedVisible) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
index 100ed55..fd58174 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodButton.java
@@ -22,6 +22,7 @@
import android.util.AttributeSet;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
import android.view.View;
import android.widget.ImageView;
@@ -48,6 +49,9 @@
private boolean mScreenLocked = false;
private boolean mHardKeyboardAvailable;
+ // Please refer to InputMethodManagerService.TAG_TRY_SUPPRESSING_IME_SWITCHER
+ private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher";
+
public InputMethodButton(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -64,10 +68,49 @@
refreshStatusIcon();
}
- // Display IME switcher icon only when all of the followings are true:
- // * There is only one enabled IME on the device. (Note that the IME should be the system IME)
- // * There are no explicitly enabled (by the user) subtypes of the IME, or the IME doesn't have
- // its subtypes at all
+ // Refer to InputMethodManagerService.needsToShowImeSwitchOngoingNotification()
+ private boolean needsToShowIMEButtonWhenVisibilityAuto() {
+ List<InputMethodInfo> imis = mImm.getEnabledInputMethodList();
+ final int N = imis.size();
+ if (N > 2) return true;
+ if (N < 1) return false;
+ int nonAuxCount = 0;
+ int auxCount = 0;
+ InputMethodSubtype nonAuxSubtype = null;
+ InputMethodSubtype auxSubtype = null;
+ for(int i = 0; i < N; ++i) {
+ final InputMethodInfo imi = imis.get(i);
+ final List<InputMethodSubtype> subtypes = mImm.getEnabledInputMethodSubtypeList(
+ imi, true);
+ final int subtypeCount = subtypes.size();
+ if (subtypeCount == 0) {
+ ++nonAuxCount;
+ } else {
+ for (int j = 0; j < subtypeCount; ++j) {
+ final InputMethodSubtype subtype = subtypes.get(j);
+ if (!subtype.isAuxiliary()) {
+ ++nonAuxCount;
+ nonAuxSubtype = subtype;
+ } else {
+ ++auxCount;
+ auxSubtype = subtype;
+ }
+ }
+ }
+ }
+ if (nonAuxCount > 1 || auxCount > 1) {
+ return true;
+ } else if (nonAuxCount == 1 && auxCount == 1) {
+ if (nonAuxSubtype != null && auxSubtype != null
+ && nonAuxSubtype.getLocale().equals(auxSubtype.getLocale())
+ && nonAuxSubtype.containsExtraValueKey(TAG_TRY_SUPPRESSING_IME_SWITCHER)) {
+ return false;
+ }
+ return true;
+ }
+ return false;
+ }
+
private boolean needsToShowIMEButton() {
if (!mShowButton || mScreenLocked) return false;
@@ -75,13 +118,10 @@
return true;
}
- List<InputMethodInfo> imis = mImm.getEnabledInputMethodList();
- final int size = imis.size();
final int visibility = loadInputMethodSelectorVisibility();
switch (visibility) {
case ID_IME_BUTTON_VISIBILITY_AUTO:
- return size > 1 || (size == 1
- && mImm.getEnabledInputMethodSubtypeList(imis.get(0), false).size() > 1);
+ return needsToShowIMEButtonWhenVisibilityAuto();
case ID_IME_BUTTON_VISIBILITY_ALWAYS_SHOW:
return true;
case ID_IME_BUTTON_VISIBILITY_ALWAYS_HIDE:
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
index 74dbfef..d9cb4e8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/NotificationPanel.java
@@ -40,6 +40,7 @@
final static int PANEL_FADE_DURATION = 150;
boolean mShowing;
+ boolean mHasClearableNotifications = false;
int mNotificationCount = 0;
NotificationPanelTitle mTitleArea;
View mSettingsButton;
@@ -50,6 +51,7 @@
View mSettingsView;
ViewGroup mContentParent;
TabletStatusBar mBar;
+ View mClearButton;
// amount to slide mContentParent down by when mContentFrame is missing
float mContentFrameMissingTranslation;
@@ -84,14 +86,27 @@
mNotificationScroller = findViewById(R.id.notification_scroller);
mContentFrame = (ViewGroup)findViewById(R.id.content_frame);
- mContentFrameMissingTranslation =
- mContentFrame.getBackground().getMinimumHeight() + 10;
+ mContentFrameMissingTranslation = 0; // not needed with current assets
+
+ // the "X" that appears in place of the clock when the panel is showing notifications
+ mClearButton = findViewById(R.id.clear_all_button);
+ mClearButton.setOnClickListener(mClearButtonListener);
mShowing = false;
setContentFrameVisible(mNotificationCount > 0, false);
}
+ private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
+ public void onClick(View v) {
+ mBar.clearAll();
+ }
+ };
+
+ public View getClearButton() {
+ return mClearButton;
+ }
+
public void show(boolean show, boolean animate) {
if (show && !mShowing) {
setContentFrameVisible(mSettingsView != null || mNotificationCount > 0, false);
@@ -264,12 +279,16 @@
if (mBar != null) {
final boolean showX
= (isShowing()
- && mNotificationScroller.getVisibility() == View.VISIBLE
- && mNotificationCount > 0);
- mBar.getClearButton().setVisibility(showX ? View.VISIBLE : View.INVISIBLE);
+ && mHasClearableNotifications
+ && mNotificationScroller.getVisibility() == View.VISIBLE);
+ getClearButton().setVisibility(showX ? View.VISIBLE : View.INVISIBLE);
}
}
+ public void setClearable(boolean clearable) {
+ mHasClearableNotifications = clearable;
+ }
+
public void updatePanelModeButtons() {
final boolean settingsVisible = (mSettingsView != null);
mSettingsButton.setVisibility(!settingsVisible ? View.VISIBLE : View.INVISIBLE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index c7b6f20..7f56d45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -122,7 +122,6 @@
View mNotificationTrigger;
NotificationIconArea mNotificationIconArea;
ViewGroup mNavigationArea;
- View mClearButton;
boolean mNotificationDNDMode;
NotificationData.Entry mNotificationDNDDummyEntry;
@@ -453,10 +452,6 @@
// the more notifications icon
mNotificationIconArea = (NotificationIconArea)sb.findViewById(R.id.notificationIcons);
- // the "X" that appears in place of the clock when the panel is showing notifications
- mClearButton = sb.findViewById(R.id.clear_all_button);
- mClearButton.setOnClickListener(mClearButtonListener);
-
// where the icons go
mIconLayout = (NotificationIconArea.IconLayout) sb.findViewById(R.id.icons);
mIconLayout.setOnTouchListener(new NotificationIconTouchListener());
@@ -587,21 +582,6 @@
return sb;
}
- private View.OnClickListener mClearButtonListener = new View.OnClickListener() {
- public void onClick(View v) {
- try {
- mBarService.onClearAllNotifications();
- } catch (RemoteException ex) {
- // system process is dead if we're here.
- }
- animateCollapse();
- }
- };
-
- public View getClearButton() {
- return mClearButton;
- }
-
public int getStatusBarHeight() {
return mHeightReceiver.getHeight();
}
@@ -1097,7 +1077,10 @@
mCompatModeButton.refresh();
if (mCompatModeButton.getVisibility() == View.VISIBLE) {
+ if (DEBUG_COMPAT_HELP
+ || ! Prefs.read(mContext).getBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, false)) {
showCompatibilityHelp();
+ }
} else {
hideCompatibilityHelp();
mCompatModePanel.closePanel();
@@ -1204,6 +1187,9 @@
}
private void setAreThereNotifications() {
+ if (mNotificationPanel != null) {
+ mNotificationPanel.setClearable(mNotificationData.hasClearableItems());
+ }
}
/**
@@ -1779,6 +1765,15 @@
return true;
}
+ public void clearAll() {
+ try {
+ mBarService.onClearAllNotifications();
+ } catch (RemoteException ex) {
+ // system process is dead if we're here.
+ }
+ animateCollapse();
+ }
+
public void userActivity() {
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
index dff1f6a..7d11251 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java
@@ -94,6 +94,11 @@
mHandler = h;
}
+ /**
+ * Let the status bar know that if you tap on ignore while panel is showing, don't do anything.
+ *
+ * Debounces taps on, say, a popup's trigger when the popup is already showing.
+ */
public void setIgnoreChildren(int index, View ignore, View panel) {
mIgnoreChildren[index] = ignore;
mPanels[index] = panel;
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
index 7a14480..21a8c14 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java
@@ -232,10 +232,14 @@
* @param resId resource id of the message
*/
public void setCarrierHelpText(int resId) {
- mCarrierHelpText = getContext().getText(resId);
+ mCarrierHelpText = getText(resId);
update(CARRIER_HELP_TEXT, mCarrierHelpText);
}
+ private CharSequence getText(int resId) {
+ return resId == 0 ? null : getContext().getText(resId);
+ }
+
/**
* Unlock help message. This is typically for help with unlock widgets, e.g. "wrong password"
* or "try again."
@@ -244,7 +248,7 @@
* @param lockIcon
*/
public void setHelpMessage(int textResId, int lockIcon) {
- mHelpMessageText = getContext().getString(textResId);
+ mHelpMessageText = getText(textResId).toString();
update(HELP_MESSAGE_TEXT, mHelpMessageText);
}
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index 4a14dd9..9d360ac 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -23,7 +23,9 @@
import com.android.internal.widget.multiwaveview.MultiWaveView;
import android.app.ActivityManager;
+import android.content.ActivityNotFoundException;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.view.KeyEvent;
@@ -181,14 +183,33 @@
UnlockWidgetCommonMethods {
private final MultiWaveView mMultiWaveView;
+ private boolean mCameraDisabled;
MultiWaveViewMethods(MultiWaveView multiWaveView) {
mMultiWaveView = multiWaveView;
+ final boolean cameraDisabled = mLockPatternUtils.getDevicePolicyManager()
+ .getCameraDisabled(null);
+ if (cameraDisabled) {
+ Log.v(TAG, "Camera disabled by Device Policy");
+ mCameraDisabled = true;
+ } else {
+ // Camera is enabled if resource is initially defined for MultiWaveView
+ // in the lockscreen layout file
+ mCameraDisabled = mMultiWaveView.getTargetResourceId()
+ != R.array.lockscreen_targets_with_camera;
+ }
}
public void updateResources() {
- mMultiWaveView.setTargetResources(mSilentMode ? R.array.lockscreen_targets_when_silent
- : R.array.lockscreen_targets_when_soundon);
+ int resId;
+ if (mCameraDisabled) {
+ // Fall back to showing ring/silence if camera is disabled by DPM...
+ resId = mSilentMode ? R.array.lockscreen_targets_when_silent
+ : R.array.lockscreen_targets_when_soundon;
+ } else {
+ resId = R.array.lockscreen_targets_with_camera;
+ }
+ mMultiWaveView.setTargetResources(resId);
}
public void onGrabbed(View v, int handle) {
@@ -200,12 +221,19 @@
}
public void onTrigger(View v, int target) {
- if (target == 0) { // TODO: Use resources to determine which handle was used
+ if (target == 0 || target == 1) { // 0 = unlock/portrait, 1 = unlock/landscape
mCallback.goToUnlockScreen();
- } else if (target == 2) {
- toggleRingMode();
- mUnlockWidgetMethods.updateResources();
- mCallback.pokeWakelock();
+ } else if (target == 2 || target == 3) { // 2 = alt/portrait, 3 = alt/landscape
+ if (!mCameraDisabled) {
+ // Broadcast an intent to start the Camera
+ Intent intent = new Intent(Intent.ACTION_CAMERA_BUTTON, null);
+ mContext.sendOrderedBroadcast(intent, null);
+ mCallback.goToUnlockScreen();
+ } else {
+ toggleRingMode();
+ mUnlockWidgetMethods.updateResources();
+ mCallback.pokeWakelock();
+ }
}
}
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index bf9e014..3ae7a3f 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -254,6 +254,12 @@
private static final int EVENT_RESTORE_DNS =
MAX_NETWORK_STATE_TRACKER_EVENT + 11;
+ /**
+ * used internally to send a sticky broadcast delayed.
+ */
+ private static final int EVENT_SEND_STICKY_BROADCAST_INTENT =
+ MAX_NETWORK_STATE_TRACKER_EVENT + 12;
+
private Handler mHandler;
// list of DeathRecipients used to make sure features are turned off when
@@ -559,6 +565,17 @@
}
}
+ private int getConnectivityChangeDelay() {
+ final ContentResolver cr = mContext.getContentResolver();
+
+ /** Check system properties for the default value then use secure settings value, if any. */
+ int defaultDelay = SystemProperties.getInt(
+ "conn." + Settings.Secure.CONNECTIVITY_CHANGE_DELAY,
+ Settings.Secure.CONNECTIVITY_CHANGE_DELAY_DEFAULT);
+ return Settings.Secure.getInt(cr, Settings.Secure.CONNECTIVITY_CHANGE_DELAY,
+ defaultDelay);
+ }
+
private int getPersistedNetworkPreference() {
final ContentResolver cr = mContext.getContentResolver();
@@ -1437,13 +1454,14 @@
// do this before we broadcast the change
handleConnectivityChange(prevNetType, doReset);
- sendStickyBroadcast(intent);
+ sendStickyBroadcastDelayed(intent, getConnectivityChangeDelay());
/*
* If the failover network is already connected, then immediately send
* out a followup broadcast indicating successful failover
*/
if (mActiveDefaultNetwork != -1) {
- sendConnectedBroadcast(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo());
+ sendConnectedBroadcastDelayed(mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(),
+ getConnectivityChangeDelay());
}
}
@@ -1497,11 +1515,15 @@
sendGeneralBroadcast(info, ConnectivityManager.CONNECTIVITY_ACTION);
}
+ private void sendConnectedBroadcastDelayed(NetworkInfo info, int delayMs) {
+ sendGeneralBroadcastDelayed(info, ConnectivityManager.CONNECTIVITY_ACTION, delayMs);
+ }
+
private void sendInetConditionBroadcast(NetworkInfo info) {
sendGeneralBroadcast(info, ConnectivityManager.INET_CONDITION_ACTION);
}
- private void sendGeneralBroadcast(NetworkInfo info, String bcastType) {
+ private Intent makeGeneralIntent(NetworkInfo info, String bcastType) {
Intent intent = new Intent(bcastType);
intent.putExtra(ConnectivityManager.EXTRA_NETWORK_INFO, info);
if (info.isFailover()) {
@@ -1516,7 +1538,15 @@
info.getExtraInfo());
}
intent.putExtra(ConnectivityManager.EXTRA_INET_CONDITION, mDefaultInetConditionPublished);
- sendStickyBroadcast(intent);
+ return intent;
+ }
+
+ private void sendGeneralBroadcast(NetworkInfo info, String bcastType) {
+ sendStickyBroadcast(makeGeneralIntent(info, bcastType));
+ }
+
+ private void sendGeneralBroadcastDelayed(NetworkInfo info, String bcastType, int delayMs) {
+ sendStickyBroadcastDelayed(makeGeneralIntent(info, bcastType), delayMs);
}
/**
@@ -1581,10 +1611,25 @@
mInitialBroadcast = new Intent(intent);
}
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
+ if (DBG) {
+ log("sendStickyBroadcast: NetworkInfo=" +
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO));
+ }
+
mContext.sendStickyBroadcast(intent);
}
}
+ private void sendStickyBroadcastDelayed(Intent intent, int delayMs) {
+ if (delayMs <= 0) {
+ sendStickyBroadcast(intent);
+ } else {
+ if (DBG) log("sendStickyBroadcastDelayed: delayMs=" + delayMs + " intent=" + intent);
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(
+ EVENT_SEND_STICKY_BROADCAST_INTENT, intent), delayMs);
+ }
+ }
+
void systemReady() {
synchronized(this) {
mSystemReady = true;
@@ -1658,7 +1703,7 @@
thisNet.setTeardownRequested(false);
updateNetworkSettings(thisNet);
handleConnectivityChange(type, false);
- sendConnectedBroadcast(info);
+ sendConnectedBroadcastDelayed(info, getConnectivityChangeDelay());
}
/**
@@ -2240,6 +2285,13 @@
}
break;
}
+ case EVENT_SEND_STICKY_BROADCAST_INTENT:
+ {
+ Intent intent = (Intent)msg.obj;
+ log("EVENT_SEND_STICKY_BROADCAST_INTENT: sendStickyBroadcast intent=" + intent);
+ sendStickyBroadcast(intent);
+ break;
+ }
}
}
}
@@ -2435,10 +2487,13 @@
if (DBG) log("event hold for obsolete network - aborting");
return;
}
- if (mDefaultInetConditionPublished == mDefaultInetCondition) {
- if (DBG) log("no change in condition - aborting");
- return;
- }
+ // TODO: Figure out why this optimization sometimes causes a
+ // change in mDefaultInetCondition to be missed and the
+ // UI to not be updated.
+ //if (mDefaultInetConditionPublished == mDefaultInetCondition) {
+ // if (DBG) log("no change in condition - aborting");
+ // return;
+ //}
NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo();
if (networkInfo.isConnected() == false) {
if (DBG) log("default network not connected - aborting");
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index a59b6c0..30de385 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -1231,6 +1231,13 @@
}
}
+ @Override
+ public boolean isBandwidthControlEnabled() {
+ mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+ return mBandwidthControlEnabled;
+ }
+
+ @Override
public NetworkStats getNetworkStatsUidDetail(int uid) {
if (Binder.getCallingUid() != uid) {
mContext.enforceCallingOrSelfPermission(
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index e97df84..238b747 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -31,19 +31,17 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.provider.Settings;
-import android.text.TextUtils;
import android.service.textservice.SpellCheckerService;
-import android.util.Log;
+import android.text.TextUtils;
import android.util.Slog;
import android.view.textservice.SpellCheckerInfo;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
public class TextServicesManagerService extends ITextServicesManager.Stub {
@@ -180,7 +178,8 @@
@Override
public void getSpellCheckerService(String sciId, String locale,
- ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener) {
+ ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
+ Bundle bundle) {
if (!mSystemReady) {
return;
}
@@ -199,7 +198,7 @@
if (bindGroup != null) {
final InternalDeathRecipient recipient =
mSpellCheckerBindGroups.get(sciId).addListener(
- tsListener, locale, scListener, uid);
+ tsListener, locale, scListener, uid, bundle);
if (recipient == null) {
if (DBG) {
Slog.w(TAG, "Didn't create a death recipient.");
@@ -217,7 +216,7 @@
try {
final ISpellCheckerSession session =
bindGroup.mSpellChecker.getISpellCheckerSession(
- recipient.mScLocale, recipient.mScListener);
+ recipient.mScLocale, recipient.mScListener, bundle);
if (session != null) {
tsListener.onServiceConnected(session);
return;
@@ -236,7 +235,8 @@
}
final long ident = Binder.clearCallingIdentity();
try {
- startSpellCheckerServiceInnerLocked(sci, locale, tsListener, scListener, uid);
+ startSpellCheckerServiceInnerLocked(
+ sci, locale, tsListener, scListener, uid, bundle);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -246,13 +246,13 @@
private void startSpellCheckerServiceInnerLocked(SpellCheckerInfo info, String locale,
ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
- int uid) {
+ int uid, Bundle bundle) {
if (DBG) {
Slog.w(TAG, "Start spell checker session inner locked.");
}
final String sciId = info.getId();
final InternalServiceConnection connection = new InternalServiceConnection(
- sciId, locale, scListener);
+ sciId, locale, scListener, bundle);
final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE);
serviceIntent.setComponent(info.getComponent());
if (DBG) {
@@ -263,7 +263,7 @@
return;
}
final SpellCheckerBindGroup group = new SpellCheckerBindGroup(
- connection, tsListener, locale, scListener, uid);
+ connection, tsListener, locale, scListener, uid, bundle);
mSpellCheckerBindGroups.put(sciId, group);
}
@@ -332,10 +332,10 @@
public SpellCheckerBindGroup(InternalServiceConnection connection,
ITextServicesSessionListener listener, String locale,
- ISpellCheckerSessionListener scListener, int uid) {
+ ISpellCheckerSessionListener scListener, int uid, Bundle bundle) {
mInternalConnection = connection;
mConnected = false;
- addListener(listener, locale, scListener, uid);
+ addListener(listener, locale, scListener, uid, bundle);
}
public void onServiceConnected(ISpellCheckerService spellChecker) {
@@ -346,7 +346,7 @@
for (InternalDeathRecipient listener : mListeners) {
try {
final ISpellCheckerSession session = spellChecker.getISpellCheckerSession(
- listener.mScLocale, listener.mScListener);
+ listener.mScLocale, listener.mScListener, listener.mBundle);
listener.mTsListener.onServiceConnected(session);
} catch (RemoteException e) {
Slog.e(TAG, "Exception in getting the spell checker session: " + e);
@@ -360,7 +360,7 @@
}
public InternalDeathRecipient addListener(ITextServicesSessionListener tsListener,
- String locale, ISpellCheckerSessionListener scListener, int uid) {
+ String locale, ISpellCheckerSessionListener scListener, int uid, Bundle bundle) {
if (DBG) {
Slog.d(TAG, "addListener: " + locale);
}
@@ -375,7 +375,7 @@
}
}
recipient = new InternalDeathRecipient(
- this, tsListener, locale, scListener, uid);
+ this, tsListener, locale, scListener, uid, bundle);
scListener.asBinder().linkToDeath(recipient, 0);
mListeners.add(recipient);
} catch(RemoteException e) {
@@ -440,11 +440,13 @@
private final ISpellCheckerSessionListener mListener;
private final String mSciId;
private final String mLocale;
+ private final Bundle mBundle;
public InternalServiceConnection(
- String id, String locale, ISpellCheckerSessionListener listener) {
+ String id, String locale, ISpellCheckerSessionListener listener, Bundle bundle) {
mSciId = id;
mLocale = locale;
mListener = listener;
+ mBundle = bundle;
}
@Override
@@ -473,14 +475,16 @@
public final String mScLocale;
private final SpellCheckerBindGroup mGroup;
public final int mUid;
+ public final Bundle mBundle;
public InternalDeathRecipient(SpellCheckerBindGroup group,
ITextServicesSessionListener tsListener, String scLocale,
- ISpellCheckerSessionListener scListener, int uid) {
+ ISpellCheckerSessionListener scListener, int uid, Bundle bundle) {
mTsListener = tsListener;
mScListener = scListener;
mScLocale = scLocale;
mGroup = group;
mUid = uid;
+ mBundle = bundle;
}
public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) {
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index f9f63b1..242a93d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -1592,7 +1592,10 @@
for (int i = scanResults.size() - 1; i >= 0; i--) {
ScanResult scanResult = scanResults.get(i);
- if (TextUtils.isEmpty(scanResult.capabilities)) {
+ //A capability of [ESS] represents an open access point
+ //that is available for an STA to connect
+ if (scanResult.capabilities != null &&
+ scanResult.capabilities.equals("[ESS]")) {
numOpenNetworks++;
}
}
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 0ad58d0..0808d5d 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -313,7 +313,7 @@
if (moveDelta > mTouchExplorationTapSlop) {
mLastTouchExploreEvent = null;
mPerformLongPressDelayed.remove();
- break;
+ break;
}
}
} break;
@@ -388,6 +388,11 @@
mTouchExploreGestureInProgress = false;
mLastTouchExploreEvent = MotionEvent.obtain(event);
sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
+ final int lastAction = mPointerTracker.getLastInjectedHoverAction();
+ if (lastAction != MotionEvent.ACTION_HOVER_EXIT) {
+ sendMotionEvent(event, MotionEvent.ACTION_HOVER_EXIT,
+ pointerIdBits, policyFlags);
+ }
break;
}
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 24188ca..deca7a9 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -16,10 +16,10 @@
package com.android.server.net;
-import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.ACCESS_NETWORK_STATE;
-import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING;
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
import static android.Manifest.permission.DUMP;
+import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING;
import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY;
import static android.content.Intent.ACTION_SHUTDOWN;
import static android.content.Intent.ACTION_UID_REMOVED;
@@ -68,7 +68,6 @@
import android.os.SystemClock;
import android.provider.Settings;
import android.telephony.TelephonyManager;
-import android.util.Log;
import android.util.LongSparseArray;
import android.util.NtpTrustedTime;
import android.util.Slog;
@@ -282,13 +281,13 @@
}
@Override
- public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template) {
+ public NetworkStatsHistory getHistoryForNetwork(NetworkTemplate template, int fields) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
synchronized (mStatsLock) {
// combine all interfaces that match template
final NetworkStatsHistory combined = new NetworkStatsHistory(
- mSettings.getNetworkBucketDuration(), estimateNetworkBuckets());
+ mSettings.getNetworkBucketDuration(), estimateNetworkBuckets(), fields);
for (NetworkIdentitySet ident : mNetworkStats.keySet()) {
if (templateMatches(template, ident)) {
final NetworkStatsHistory history = mNetworkStats.get(ident);
@@ -302,7 +301,8 @@
}
@Override
- public NetworkStatsHistory getHistoryForUid(NetworkTemplate template, int uid, int tag) {
+ public NetworkStatsHistory getHistoryForUid(
+ NetworkTemplate template, int uid, int tag, int fields) {
mContext.enforceCallingOrSelfPermission(READ_NETWORK_USAGE_HISTORY, TAG);
synchronized (mStatsLock) {
@@ -311,7 +311,7 @@
// combine all interfaces that match template
final NetworkStatsHistory combined = new NetworkStatsHistory(
- mSettings.getUidBucketDuration(), estimateUidBuckets());
+ mSettings.getUidBucketDuration(), estimateUidBuckets(), fields);
for (NetworkIdentitySet ident : mUidStats.keySet()) {
if (templateMatches(template, ident)) {
final NetworkStatsHistory history = mUidStats.get(ident).get(packed);
@@ -596,7 +596,7 @@
// decide if enough has changed to trigger persist
final NetworkStats persistDelta = computeStatsDelta(
- mLastPersistNetworkSnapshot, networkSnapshot);
+ mLastPersistNetworkSnapshot, networkSnapshot, true);
final long persistThreshold = mSettings.getPersistThreshold();
NetworkStats.Entry entry = null;
@@ -626,7 +626,7 @@
private void performNetworkPollLocked(NetworkStats networkSnapshot, long currentTime) {
final HashSet<String> unknownIface = Sets.newHashSet();
- final NetworkStats delta = computeStatsDelta(mLastNetworkSnapshot, networkSnapshot);
+ final NetworkStats delta = computeStatsDelta(mLastNetworkSnapshot, networkSnapshot, false);
final long timeStart = currentTime - delta.getElapsedRealtime();
NetworkStats.Entry entry = null;
@@ -661,9 +661,9 @@
private void performUidPollLocked(NetworkStats uidSnapshot, long currentTime) {
ensureUidStatsLoadedLocked();
- final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot);
+ final NetworkStats delta = computeStatsDelta(mLastUidSnapshot, uidSnapshot, false);
final NetworkStats operationsDelta = computeStatsDelta(
- mLastOperationsSnapshot, mOperations);
+ mLastOperationsSnapshot, mOperations, false);
final long timeStart = currentTime - delta.getElapsedRealtime();
NetworkStats.Entry entry = null;
@@ -932,6 +932,7 @@
out.flush();
mNetworkFile.finishWrite(fos);
} catch (IOException e) {
+ Slog.w(TAG, "problem writing stats: ", e);
if (fos != null) {
mNetworkFile.failWrite(fos);
}
@@ -978,6 +979,7 @@
out.flush();
mUidFile.finishWrite(fos);
} catch (IOException e) {
+ Slog.w(TAG, "problem writing stats: ", e);
if (fos != null) {
mUidFile.failWrite(fos);
}
@@ -1052,15 +1054,20 @@
*/
@Deprecated
private void generateRandomLocked() {
- long networkEnd = System.currentTimeMillis();
- long networkStart = networkEnd - mSettings.getNetworkMaxHistory();
- long networkRx = 3 * GB_IN_BYTES;
- long networkTx = 2 * GB_IN_BYTES;
+ final long NET_END = System.currentTimeMillis();
+ final long NET_START = NET_END - mSettings.getNetworkMaxHistory();
+ final long NET_RX_BYTES = 3 * GB_IN_BYTES;
+ final long NET_RX_PACKETS = NET_RX_BYTES / 1024;
+ final long NET_TX_BYTES = 2 * GB_IN_BYTES;
+ final long NET_TX_PACKETS = NET_TX_BYTES / 1024;
- long uidEnd = System.currentTimeMillis();
- long uidStart = uidEnd - mSettings.getUidMaxHistory();
- long uidRx = 500 * MB_IN_BYTES;
- long uidTx = 100 * MB_IN_BYTES;
+ final long UID_END = System.currentTimeMillis();
+ final long UID_START = UID_END - mSettings.getUidMaxHistory();
+ final long UID_RX_BYTES = 500 * MB_IN_BYTES;
+ final long UID_RX_PACKETS = UID_RX_BYTES / 1024;
+ final long UID_TX_BYTES = 100 * MB_IN_BYTES;
+ final long UID_TX_PACKETS = UID_TX_BYTES / 1024;
+ final long UID_OPERATIONS = UID_RX_BYTES / 2048;
final List<ApplicationInfo> installedApps = mContext
.getPackageManager().getInstalledApplications(0);
@@ -1068,13 +1075,13 @@
mNetworkStats.clear();
mUidStats.clear();
for (NetworkIdentitySet ident : mActiveIfaces.values()) {
- findOrCreateNetworkStatsLocked(ident).generateRandom(
- networkStart, networkEnd, networkRx, networkTx);
+ findOrCreateNetworkStatsLocked(ident).generateRandom(NET_START, NET_END, NET_RX_BYTES,
+ NET_RX_PACKETS, NET_TX_BYTES, NET_TX_PACKETS, 0L);
for (ApplicationInfo info : installedApps) {
final int uid = info.uid;
- findOrCreateUidStatsLocked(ident, uid, TAG_NONE).generateRandom(
- uidStart, uidEnd, uidRx, uidTx);
+ findOrCreateUidStatsLocked(ident, uid, TAG_NONE).generateRandom(UID_START, UID_END,
+ UID_RX_BYTES, UID_RX_PACKETS, UID_TX_BYTES, UID_TX_PACKETS, UID_OPERATIONS);
}
}
}
@@ -1083,9 +1090,13 @@
* Return the delta between two {@link NetworkStats} snapshots, where {@code
* before} can be {@code null}.
*/
- private static NetworkStats computeStatsDelta(NetworkStats before, NetworkStats current) {
+ private static NetworkStats computeStatsDelta(
+ NetworkStats before, NetworkStats current, boolean collectStale) {
if (before != null) {
return current.subtractClamped(before);
+ } else if (collectStale) {
+ // caller is okay collecting stale stats for first call.
+ return current;
} else {
// this is first snapshot; to prevent from double-counting we only
// observe traffic occuring between known snapshots.
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 91fef22..09f8ff3 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -20,14 +20,18 @@
import static android.content.Intent.EXTRA_UID;
import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkPolicy.LIMIT_DISABLED;
import static android.net.NetworkPolicy.SNOOZE_NEVER;
+import static android.net.NetworkPolicy.WARNING_DISABLED;
import static android.net.NetworkPolicyManager.POLICY_NONE;
import static android.net.NetworkPolicyManager.POLICY_REJECT_METERED_BACKGROUND;
import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
import static android.net.NetworkPolicyManager.RULE_REJECT_METERED;
import static android.net.NetworkPolicyManager.computeLastCycleBoundary;
+import static android.net.NetworkPolicyManager.computeNextCycleBoundary;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT;
import static com.android.server.net.NetworkPolicyManagerService.TYPE_LIMIT_SNOOZED;
@@ -79,6 +83,7 @@
import org.easymock.IAnswer;
import java.io.File;
+import java.util.LinkedHashSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
@@ -402,7 +407,7 @@
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 5, 1024L, 1024L, SNOOZE_NEVER);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
- assertEquals(expectedCycle, actualCycle);
+ assertTimeEquals(expectedCycle, actualCycle);
}
public void testLastCycleBoundaryLastMonth() throws Exception {
@@ -413,7 +418,7 @@
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 20, 1024L, 1024L, SNOOZE_NEVER);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
- assertEquals(expectedCycle, actualCycle);
+ assertTimeEquals(expectedCycle, actualCycle);
}
public void testLastCycleBoundaryThisMonthFebruary() throws Exception {
@@ -424,18 +429,48 @@
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
- assertEquals(expectedCycle, actualCycle);
+ assertTimeEquals(expectedCycle, actualCycle);
}
public void testLastCycleBoundaryLastMonthFebruary() throws Exception {
// assume cycle day of "30th" in february, which should clamp
final long currentTime = parseTime("2007-03-14T00:00:00.000Z");
- final long expectedCycle = parseTime("2007-03-01T00:00:00.000Z");
+ final long expectedCycle = parseTime("2007-02-28T23:59:59.000Z");
final NetworkPolicy policy = new NetworkPolicy(
sTemplateWifi, 30, 1024L, 1024L, SNOOZE_NEVER);
final long actualCycle = computeLastCycleBoundary(currentTime, policy);
- assertEquals(expectedCycle, actualCycle);
+ assertTimeEquals(expectedCycle, actualCycle);
+ }
+
+ public void testNextCycleSane() throws Exception {
+ final NetworkPolicy policy = new NetworkPolicy(
+ sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER);
+ final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
+
+ // walk forwards, ensuring that cycle boundaries don't get stuck
+ long currentCycle = computeNextCycleBoundary(parseTime("2011-08-01T00:00:00.000Z"), policy);
+ for (int i = 0; i < 128; i++) {
+ long nextCycle = computeNextCycleBoundary(currentCycle, policy);
+ assertEqualsFuzzy(DAY_IN_MILLIS * 30, nextCycle - currentCycle, DAY_IN_MILLIS * 3);
+ assertUnique(seen, nextCycle);
+ currentCycle = nextCycle;
+ }
+ }
+
+ public void testLastCycleSane() throws Exception {
+ final NetworkPolicy policy = new NetworkPolicy(
+ sTemplateWifi, 31, WARNING_DISABLED, LIMIT_DISABLED, SNOOZE_NEVER);
+ final LinkedHashSet<Long> seen = new LinkedHashSet<Long>();
+
+ // walk backwards, ensuring that cycle boundaries look sane
+ long currentCycle = computeLastCycleBoundary(parseTime("2011-08-04T00:00:00.000Z"), policy);
+ for (int i = 0; i < 128; i++) {
+ long lastCycle = computeLastCycleBoundary(currentCycle, policy);
+ assertEqualsFuzzy(DAY_IN_MILLIS * 30, currentCycle - lastCycle, DAY_IN_MILLIS * 3);
+ assertUnique(seen, lastCycle);
+ currentCycle = lastCycle;
+ }
}
public void testNetworkPolicyAppliedCycleLastMonth() throws Exception {
@@ -734,6 +769,32 @@
}
}
+ private static void assertTimeEquals(long expected, long actual) {
+ if (expected != actual) {
+ fail("expected " + formatTime(expected) + " but was actually " + formatTime(actual));
+ }
+ }
+
+ private static String formatTime(long millis) {
+ final Time time = new Time(Time.TIMEZONE_UTC);
+ time.set(millis);
+ return time.format3339(false);
+ }
+
+ private static void assertEqualsFuzzy(long expected, long actual, long fuzzy) {
+ final long low = expected - fuzzy;
+ final long high = expected + fuzzy;
+ if (actual < low || actual > high) {
+ fail("value " + actual + " is outside [" + low + "," + high + "]");
+ }
+ }
+
+ private static void assertUnique(LinkedHashSet<Long> seen, Long value) {
+ if (!seen.add(value)) {
+ fail("found duplicate time " + value + " in series " + seen.toString());
+ }
+ }
+
private static void assertNotificationType(int expected, String actualTag) {
assertEquals(
Integer.toString(expected), actualTag.substring(actualTag.lastIndexOf(':') + 1));
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index cf69fd5..8eb9cc3 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -25,6 +25,7 @@
import static android.net.NetworkStats.IFACE_ALL;
import static android.net.NetworkStats.TAG_NONE;
import static android.net.NetworkStats.UID_ALL;
+import static android.net.NetworkStatsHistory.FIELD_ALL;
import static android.net.NetworkTemplate.buildTemplateMobileAll;
import static android.net.NetworkTemplate.buildTemplateWifi;
import static android.net.TrafficStats.UID_REMOVED;
@@ -302,7 +303,7 @@
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
// verify service recorded history
- history = mService.getHistoryForNetwork(sTemplateWifi);
+ history = mService.getHistoryForNetwork(sTemplateWifi, FIELD_ALL);
assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0);
assertEquals(HOUR_IN_MILLIS, history.getBucketDuration());
assertEquals(2, history.size());
@@ -319,7 +320,7 @@
mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
// verify identical stats, but spread across 4 buckets now
- history = mService.getHistoryForNetwork(sTemplateWifi);
+ history = mService.getHistoryForNetwork(sTemplateWifi, FIELD_ALL);
assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, 512L, 4L, 512L, 4L, 0);
assertEquals(30 * MINUTE_IN_MILLIS, history.getBucketDuration());
assertEquals(4, history.size());
@@ -631,14 +632,15 @@
private void assertNetworkTotal(NetworkTemplate template, long rxBytes, long rxPackets,
long txBytes, long txPackets, int operations) {
- final NetworkStatsHistory history = mService.getHistoryForNetwork(template);
+ final NetworkStatsHistory history = mService.getHistoryForNetwork(template, FIELD_ALL);
assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
txPackets, operations);
}
private void assertUidTotal(NetworkTemplate template, int uid, long rxBytes, long rxPackets,
long txBytes, long txPackets, int operations) {
- final NetworkStatsHistory history = mService.getHistoryForUid(template, uid, TAG_NONE);
+ final NetworkStatsHistory history = mService.getHistoryForUid(
+ template, uid, TAG_NONE, FIELD_ALL);
assertValues(history, Long.MIN_VALUE, Long.MAX_VALUE, rxBytes, rxPackets, txBytes,
txPackets, operations);
}
diff --git a/tests/GridLayoutTest/res/layout/grid3.xml b/tests/GridLayoutTest/res/layout/grid3.xml
index 2eca384..0e53613 100644
--- a/tests/GridLayoutTest/res/layout/grid3.xml
+++ b/tests/GridLayoutTest/res/layout/grid3.xml
@@ -22,6 +22,7 @@
android:useDefaultMargins="true"
android:alignmentMode="alignBounds"
+ android:rowOrderPreserved="false"
android:columnCount="4"
>
@@ -49,7 +50,7 @@
/>
<EditText
- android:layout_width="64dip"
+ android:ems="10"
/>
<TextView
@@ -60,13 +61,13 @@
/>
<EditText
- android:layout_width="32dip"
+ android:ems="8"
/>
<Space
- android:layout_row="4"
+ android:layout_row="2"
+ android:layout_rowSpan="3"
android:layout_column="2"
- android:layout_margin="0dip"
android:layout_gravity="fill"
/>
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
index 907ee9c..8974f37 100644
--- a/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
+++ b/tests/GridLayoutTest/src/com/android/test/layout/Activity2.java
@@ -38,7 +38,6 @@
p.setUseDefaultMargins(true);
p.setAlignmentMode(ALIGN_BOUNDS);
p.setRowOrderPreserved(false);
- p.setPadding(0, 0, 0, 0);
Spec row1 = spec(0);
Spec row2 = spec(1);
@@ -75,12 +74,9 @@
}
{
EditText c = new EditText(context);
+ c.setEms(10);
c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_EMAIL_ADDRESS);
- {
- LayoutParams lp = new LayoutParams(row3, col2);
- lp.width = (int) c.getPaint().measureText("Frederick.W.Flintstone");
- p.addView(c, lp);
- }
+ p.addView(c, new LayoutParams(row3, col2));
}
{
TextView c = new TextView(context);
@@ -89,17 +85,13 @@
}
{
TextView c = new EditText(context);
+ c.setEms(8);
c.setInputType(TYPE_CLASS_TEXT | TYPE_TEXT_VARIATION_PASSWORD);
- {
- LayoutParams lp = new LayoutParams(row4, col2);
- lp.width = (int) c.getPaint().measureText("************");
- p.addView(c, lp);
- }
+ p.addView(c, new LayoutParams(row4, col2));
}
{
Space c = new Space(context);
LayoutParams lp = new LayoutParams(row5, col3);
- lp.setMargins(0, 0, 0, 0);
p.addView(c, lp);
}
{
diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
index be9dfcf..168c68b 100644
--- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java
@@ -44,9 +44,11 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
+import java.net.InetAddress;
import java.net.URL;
import java.util.HashSet;
import java.util.List;
+import java.util.Set;
/**
* {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi
@@ -82,7 +84,6 @@
private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7;
private static final int DEFAULT_NUM_DNS_PINGS = 5;
private static final int DEFAULT_MIN_DNS_RESPONSES = 3;
- private static final long DNS_PING_INTERVAL_MS = 100;
private static final int DEFAULT_DNS_PING_TIMEOUT_MS = 2000;
@@ -92,6 +93,7 @@
private static final String DEFAULT_WALLED_GARDEN_URL =
"http://clients3.google.com/generate_204";
private static final int WALLED_GARDEN_SOCKET_TIMEOUT_MS = 10000;
+ private static final int DNS_INTRATEST_PING_INTERVAL = 20;
private static final int BASE = Protocol.BASE_WIFI_WATCHDOG;
@@ -114,9 +116,8 @@
private static final int EVENT_WIFI_RADIO_STATE_CHANGE = BASE + 5;
private static final int EVENT_WATCHDOG_SETTINGS_CHANGE = BASE + 6;
- private static final int MESSAGE_CHECK_STEP = BASE + 100;
- private static final int MESSAGE_HANDLE_WALLED_GARDEN = BASE + 101;
- private static final int MESSAGE_HANDLE_BAD_AP = BASE + 102;
+ private static final int MESSAGE_HANDLE_WALLED_GARDEN = BASE + 100;
+ private static final int MESSAGE_HANDLE_BAD_AP = BASE + 101;
/**
* arg1 == mOnlineWatchState.checkCount
*/
@@ -189,8 +190,9 @@
mContext = context;
mContentResolver = context.getContentResolver();
mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
- mDnsPinger = new DnsPinger("WifiWatchdogServer.DnsPinger", context,
- ConnectivityManager.TYPE_WIFI);
+ mDnsPinger = new DnsPinger(mContext, "WifiWatchdogStateMachine.DnsPinger",
+ this.getHandler().getLooper(), this.getHandler(),
+ ConnectivityManager.TYPE_WIFI);
setupNetworkReceiver();
@@ -637,36 +639,43 @@
}
class DnsCheckingState extends State {
- int dnsCheckTries = 0;
int dnsCheckSuccesses = 0;
+ int dnsCheckTries = 0;
String dnsCheckLogStr = "";
+ Set<Integer> ids = new HashSet<Integer>();
@Override
public void enter() {
dnsCheckSuccesses = 0;
dnsCheckTries = 0;
+ ids.clear();
+ InetAddress dns = mDnsPinger.getDns();
if (DBG) {
Slog.d(WWSM_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime());
dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ",
- mDnsPinger.getDns(), mInitialConnInfo.getSSID());
+ dns, mInitialConnInfo.getSSID());
}
- sendCheckStepMessage(0);
+ for (int i=0; i < mNumDnsPings; i++) {
+ ids.add(mDnsPinger.pingDnsAsync(dns, mDnsPingTimeoutMs,
+ DNS_INTRATEST_PING_INTERVAL * i));
+ }
}
@Override
public boolean processMessage(Message msg) {
- if (msg.what != MESSAGE_CHECK_STEP) {
+ if (msg.what != DnsPinger.DNS_PING_RESULT) {
return NOT_HANDLED;
}
- if (msg.arg1 != mNetEventCounter) {
- Slog.d(WWSM_TAG, "Check step out of sync, ignoring...");
+
+ int pingID = msg.arg1;
+ int pingResponseTime = msg.arg2;
+
+ if (!ids.contains(pingID)) {
+ Slog.w(WWSM_TAG, "Received a Dns response with unknown ID!");
return HANDLED;
}
-
- long pingResponseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
- mDnsPingTimeoutMs);
-
+ ids.remove(pingID);
dnsCheckTries++;
if (pingResponseTime >= 0)
dnsCheckSuccesses++;
@@ -730,11 +739,15 @@
return HANDLED;
}
- // Still in dns check step
- sendCheckStepMessage(DNS_PING_INTERVAL_MS);
return HANDLED;
}
+ @Override
+ public void exit() {
+ mDnsPinger.cancelPings();
+ }
+
+
private boolean shouldCheckWalledGarden() {
if (!mWalledGardenTestEnabled) {
if (VDBG)
@@ -752,11 +765,6 @@
}
return true;
}
-
- private void sendCheckStepMessage(long delay) {
- sendMessageDelayed(obtainMessage(MESSAGE_CHECK_STEP, mNetEventCounter, 0), delay);
- }
-
}
class OnlineWatchState extends State {
@@ -779,12 +787,15 @@
int checkGuard = 0;
Long lastCheckTime = null;
+ int curPingID = 0;
+
@Override
public void enter() {
lastCheckTime = SystemClock.elapsedRealtime();
signalUnstable = false;
checkGuard++;
unstableSignalChecks = false;
+ curPingID = 0;
triggerSingleDnsCheck();
}
@@ -820,8 +831,18 @@
return HANDLED;
}
lastCheckTime = SystemClock.elapsedRealtime();
- long responseTime = mDnsPinger.pingDns(mDnsPinger.getDns(),
- mDnsPingTimeoutMs);
+ curPingID = mDnsPinger.pingDnsAsync(mDnsPinger.getDns(),
+ mDnsPingTimeoutMs, 0);
+ return HANDLED;
+ case DnsPinger.DNS_PING_RESULT:
+ if ((short) msg.arg1 != curPingID) {
+ if (VDBG) {
+ Slog.v(WWSM_TAG, "Received non-matching DnsPing w/ id: " +
+ msg.arg1);
+ }
+ return HANDLED;
+ }
+ int responseTime = msg.arg2;
if (responseTime >= 0) {
if (VDBG) {
Slog.v(WWSM_TAG, "Ran a single DNS ping. Response time: "
@@ -842,6 +863,11 @@
return NOT_HANDLED;
}
+ @Override
+ public void exit() {
+ mDnsPinger.cancelPings();
+ }
+
/**
* Times a dns check with an interval based on {@link #signalUnstable}
*/