diff options
66 files changed, 1581 insertions, 1115 deletions
diff --git a/api/current.txt b/api/current.txt index 11804400dd35..bdc695ff8771 100644 --- a/api/current.txt +++ b/api/current.txt @@ -84,7 +84,6 @@ package android { field public static final java.lang.String READ_SMS = "android.permission.READ_SMS"; field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS"; field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS"; - field public static final java.lang.String READ_WRITE_OWN_VOICEMAIL = "com.android.voicemail.permission.READ_WRITE_OWN_VOICEMAIL"; field public static final java.lang.String REBOOT = "android.permission.REBOOT"; field public static final java.lang.String RECEIVE_BOOT_COMPLETED = "android.permission.RECEIVE_BOOT_COMPLETED"; field public static final java.lang.String RECEIVE_MMS = "android.permission.RECEIVE_MMS"; diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java index 5fe3644f1297..57e0583a8b2c 100644 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -32,10 +32,9 @@ public abstract class Animator implements Cloneable { /** * 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 61a12eec6baa..ce3dd13e4dfb 100644 --- a/core/java/android/animation/AnimatorSet.java +++ b/core/java/android/animation/AnimatorSet.java @@ -349,7 +349,8 @@ public final class AnimatorSet extends Animator { return true; } } - return false; + // Also return true if we're currently running the startDelay animator + return (mDelayAnim != null && mDelayAnim.isRunning()); } /** @@ -487,7 +488,6 @@ public final class AnimatorSet extends Animator { 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 90d676ea2904..c22306abcdc7 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -186,6 +186,16 @@ public class ValueAnimator extends Animator { 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 @@ public class ValueAnimator extends Animator { 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 @@ public class ValueAnimator extends Animator { // 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 @@ public class ValueAnimator extends Animator { // 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 @@ public class ValueAnimator extends Animator { @Override public boolean isRunning() { - return (mPlayingState == RUNNING); + return (mPlayingState == RUNNING || mStarted); } /** @@ -1000,7 +1013,7 @@ public class ValueAnimator extends Animator { 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 @@ public class ValueAnimator extends Animator { tmpListeners.get(i).onAnimationEnd(this); } } + mStarted = false; } /** diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 6767747705db..58c79fccd557 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -1708,6 +1708,7 @@ public class Camera { private static final String PIXEL_FORMAT_YUV420P = "yuv420p"; private static final String PIXEL_FORMAT_RGB565 = "rgb565"; private static final String PIXEL_FORMAT_JPEG = "jpeg"; + private static final String PIXEL_FORMAT_BAYER_RGGB = "bayer-rggb"; private HashMap<String, String> mMap; @@ -2258,6 +2259,7 @@ public class Camera { case ImageFormat.YV12: return PIXEL_FORMAT_YUV420P; case ImageFormat.RGB_565: return PIXEL_FORMAT_RGB565; case ImageFormat.JPEG: return PIXEL_FORMAT_JPEG; + case ImageFormat.BAYER_RGGB: return PIXEL_FORMAT_BAYER_RGGB; default: return null; } } diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java index f2d84ebc9889..81738f3827e9 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 @@ import java.util.Random; * 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 @@ public final class DnsPinger { 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 @@ public final class DnsPinger { } } - /** - * @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 c41d182cfa11..b65506c411e8 100644 --- a/core/java/android/net/INetworkStatsService.aidl +++ b/core/java/android/net/INetworkStatsService.aidl @@ -24,9 +24,9 @@ import android.net.NetworkTemplate; 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 1e9d81319297..9d253c73614c 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -189,9 +189,10 @@ public class NetworkPolicyManager { */ 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 0e8e7fc5ecfe..f2fcb8fad852 100644 --- a/core/java/android/net/NetworkStats.java +++ b/core/java/android/net/NetworkStats.java @@ -58,7 +58,7 @@ public class NetworkStats implements Parcelable { 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 class NetworkStats implements Parcelable { 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 @@ public class NetworkStats implements Parcelable { 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 @@ public class NetworkStats implements Parcelable { rxPackets = parcel.createLongArray(); txBytes = parcel.createLongArray(); txPackets = parcel.createLongArray(); - operations = parcel.createIntArray(); + operations = parcel.createLongArray(); } /** {@inheritDoc} */ @@ -123,16 +128,16 @@ public class NetworkStats implements Parcelable { 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 class NetworkStats implements Parcelable { } 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 7a4b811763aa..c917af919e28 100644 --- a/core/java/android/net/NetworkStatsHistory.java +++ b/core/java/android/net/NetworkStatsHistory.java @@ -19,11 +19,11 @@ package android.net; 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 @@ import java.util.Random; */ 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 @@ public class NetworkStatsHistory implements Parcelable { rxPackets = readLongArray(in); txBytes = readLongArray(in); txPackets = readLongArray(in); - operations = readIntArray(in); + operations = readLongArray(in); bucketCount = bucketStart.length; } @@ -109,21 +120,31 @@ public class NetworkStatsHistory implements Parcelable { 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 class NetworkStatsHistory implements Parcelable { } 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 @@ public class NetworkStatsHistory implements Parcelable { 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 @@ public class NetworkStatsHistory implements Parcelable { @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 @@ public class NetworkStatsHistory implements Parcelable { 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 class NetworkStatsHistory implements Parcelable { */ 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 @@ public class NetworkStatsHistory implements Parcelable { 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 @@ public class NetworkStatsHistory implements Parcelable { 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 @@ public class NetworkStatsHistory implements Parcelable { 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 @@ public class NetworkStatsHistory implements Parcelable { 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 @@ public class NetworkStatsHistory implements Parcelable { 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 @@ public class NetworkStatsHistory implements Parcelable { * @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 @@ public class NetworkStatsHistory implements Parcelable { 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 @@ public class NetworkStatsHistory implements Parcelable { } }; + 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 @@ public class NetworkStatsHistory implements Parcelable { 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 class NetworkStatsHistory implements Parcelable { 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,31 +588,16 @@ public class NetworkStatsHistory implements Parcelable { } public static void writeLongArray(Parcel out, long[] 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.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(); + if (values == null) { + out.writeInt(-1); + return; } - 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]); + out.writeLong(values[i]); } } } diff --git a/core/java/android/net/VpnBuilder.java b/core/java/android/net/VpnBuilder.java deleted file mode 100644 index 458252345152..000000000000 --- a/core/java/android/net/VpnBuilder.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net; - -import android.app.Activity; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.os.ServiceManager; - -import com.android.internal.net.VpnConfig; - -import java.net.InetAddress; -import java.net.Inet4Address; -import java.net.Inet6Address; -import java.net.DatagramSocket; -import java.net.Socket; -import java.util.ArrayList; - -/** - * VpnBuilder is a framework which enables applications to build their - * own VPN solutions. In general, it creates a virtual network interface, - * configures addresses and routing rules, and returns a file descriptor - * to the application. Each read from the descriptor retrieves an outgoing - * packet which was routed to the interface. Each write to the descriptor - * injects an incoming packet just like it was received from the interface. - * The framework is running on Internet Protocol (IP), so packets are - * always started with IP headers. The application then completes a VPN - * connection by processing and exchanging packets with a remote server - * over a secured tunnel. - * - * <p>Letting applications intercept packets raises huge security concerns. - * Besides, a VPN application can easily break the network, and two of them - * may conflict with each other. The framework takes several actions to - * address these issues. Here are some key points: - * <ul> - * <li>User action is required to create a VPN connection.</li> - * <li>There can be only one VPN connection running at the same time. The - * existing interface is deactivated when a new one is created.</li> - * <li>A system-managed notification is shown during the lifetime of a - * VPN connection.</li> - * <li>A system-managed dialog gives the information of the current VPN - * connection. It also provides a button to disconnect.</li> - * <li>The network is restored automatically when the file descriptor is - * closed. It also covers the cases when a VPN application is crashed - * or killed by the system.</li> - * </ul> - * - * <p>There are two primary methods in this class: {@link #prepare} and - * {@link #establish}. The former deals with the user action and stops - * the existing VPN connection created by another application. The latter - * creates a VPN interface using the parameters supplied to this builder. - * An application must call {@link #prepare} to grant the right to create - * an interface, and it can be revoked at any time by another application. - * The application got revoked is notified by an {@link #ACTION_VPN_REVOKED} - * broadcast. Here are the general steps to create a VPN connection: - * <ol> - * <li>When the user press the button to connect, call {@link #prepare} - * and launch the intent if necessary.</li> - * <li>Register a receiver for {@link #ACTION_VPN_REVOKED} broadcasts. - * <li>Connect to the remote server and negotiate the network parameters - * of the VPN connection.</li> - * <li>Use those parameters to configure a VpnBuilder and create a VPN - * interface by calling {@link #establish}.</li> - * <li>Start processing packets between the returned file descriptor and - * the VPN tunnel.</li> - * <li>When an {@link #ACTION_VPN_REVOKED} broadcast is received, the - * interface is already deactivated by the framework. Close the file - * descriptor and shut down the VPN tunnel gracefully. - * </ol> - * Methods in this class can be used in activities and services. However, - * the intent returned from {@link #prepare} must be launched from an - * activity. The broadcast receiver can be registered at any time, but doing - * it before calling {@link #establish} effectively avoids race conditions. - * - * <p class="note">Using this class requires - * {@link android.Manifest.permission#VPN} permission. - * @hide - */ -public class VpnBuilder { - - /** - * Broadcast intent action indicating that the VPN application has been - * revoked. This can be only received by the target application on the - * receiver explicitly registered using {@link Context#registerReceiver}. - * - * <p>This is a protected intent that can only be sent by the system. - */ - public static final String ACTION_VPN_REVOKED = VpnConfig.ACTION_VPN_REVOKED; - - /** - * Use IConnectivityManager instead since those methods are hidden and - * not available in ConnectivityManager. - */ - private static IConnectivityManager getService() { - return IConnectivityManager.Stub.asInterface( - ServiceManager.getService(Context.CONNECTIVITY_SERVICE)); - } - - /** - * Prepare to establish a VPN connection. This method returns {@code null} - * if the VPN application is already prepared. Otherwise, it returns an - * {@link Intent} to a system activity. The application should launch the - * activity using {@link Activity#startActivityForResult} to get itself - * prepared. The activity may pop up a dialog to require user action, and - * the result will come back to the application through its - * {@link Activity#onActivityResult}. The application becomes prepared if - * the result is {@link Activity#RESULT_OK}, and it is granted to create a - * VPN interface by calling {@link #establish}. - * - * <p>Only one application can be granted at the same time. The right - * is revoked when another application is granted. The application - * losing the right will be notified by an {@link #ACTION_VPN_REVOKED} - * broadcast, and its VPN interface will be deactivated by the system. - * The application should then notify the remote server and disconnect - * gracefully. Unless the application becomes prepared again, subsequent - * calls to {@link #establish} will return {@code null}. - * - * @see #establish - * @see #ACTION_VPN_REVOKED - */ - public static Intent prepare(Context context) { - try { - if (getService().prepareVpn(context.getPackageName(), null)) { - return null; - } - } catch (RemoteException e) { - // ignore - } - return VpnConfig.getIntentForConfirmation(); - } - - private VpnConfig mConfig = new VpnConfig(); - private StringBuilder mAddresses = new StringBuilder(); - private StringBuilder mRoutes = new StringBuilder(); - - /** - * Set the name of this session. It will be displayed in system-managed - * dialogs and notifications. This is recommended not required. - */ - public VpnBuilder setSession(String session) { - mConfig.session = session; - return this; - } - - /** - * Set the {@link PendingIntent} to an activity for users to configure - * the VPN connection. If it is not set, the button to configure will - * not be shown in system-managed dialogs. - */ - public VpnBuilder setConfigureIntent(PendingIntent intent) { - mConfig.configureIntent = intent; - return this; - } - - /** - * Set the maximum transmission unit (MTU) of the VPN interface. If it - * is not set, the default value in the operating system will be used. - * - * @throws IllegalArgumentException if the value is not positive. - */ - public VpnBuilder setMtu(int mtu) { - if (mtu <= 0) { - throw new IllegalArgumentException("Bad mtu"); - } - mConfig.mtu = mtu; - return this; - } - - /** - * Private method to validate address and prefixLength. - */ - private static void check(InetAddress address, int prefixLength) { - if (address.isLoopbackAddress()) { - throw new IllegalArgumentException("Bad address"); - } - if (address instanceof Inet4Address) { - if (prefixLength < 0 || prefixLength > 32) { - throw new IllegalArgumentException("Bad prefixLength"); - } - } else if (address instanceof Inet6Address) { - if (prefixLength < 0 || prefixLength > 128) { - throw new IllegalArgumentException("Bad prefixLength"); - } - } else { - throw new IllegalArgumentException("Unsupported family"); - } - } - - /** - * Convenience method to add a network address to the VPN interface - * using a numeric address string. See {@link InetAddress} for the - * definitions of numeric address formats. - * - * @throws IllegalArgumentException if the address is invalid. - * @see #addAddress(InetAddress, int) - */ - public VpnBuilder addAddress(String address, int prefixLength) { - return addAddress(InetAddress.parseNumericAddress(address), prefixLength); - } - - /** - * Add a network address to the VPN interface. Both IPv4 and IPv6 - * addresses are supported. At least one address must be set before - * calling {@link #establish}. - * - * @throws IllegalArgumentException if the address is invalid. - */ - public VpnBuilder addAddress(InetAddress address, int prefixLength) { - check(address, prefixLength); - - if (address.isAnyLocalAddress()) { - throw new IllegalArgumentException("Bad address"); - } - - mAddresses.append(String.format(" %s/%d", address.getHostAddress(), prefixLength)); - return this; - } - - /** - * Convenience method to add a network route to the VPN interface - * using a numeric address string. See {@link InetAddress} for the - * definitions of numeric address formats. - * - * @see #addRoute(InetAddress, int) - * @throws IllegalArgumentException if the route is invalid. - */ - public VpnBuilder addRoute(String address, int prefixLength) { - return addRoute(InetAddress.parseNumericAddress(address), prefixLength); - } - - /** - * Add a network route to the VPN interface. Both IPv4 and IPv6 - * routes are supported. - * - * @throws IllegalArgumentException if the route is invalid. - */ - public VpnBuilder addRoute(InetAddress address, int prefixLength) { - check(address, prefixLength); - - int offset = prefixLength / 8; - byte[] bytes = address.getAddress(); - if (offset < bytes.length) { - if ((byte)(bytes[offset] << (prefixLength % 8)) != 0) { - throw new IllegalArgumentException("Bad address"); - } - while (++offset < bytes.length) { - if (bytes[offset] != 0) { - throw new IllegalArgumentException("Bad address"); - } - } - } - - mRoutes.append(String.format(" %s/%d", address.getHostAddress(), prefixLength)); - return this; - } - - /** - * Convenience method to add a DNS server to the VPN connection - * using a numeric address string. See {@link InetAddress} for the - * definitions of numeric address formats. - * - * @throws IllegalArgumentException if the address is invalid. - * @see #addDnsServer(InetAddress) - */ - public VpnBuilder addDnsServer(String address) { - return addDnsServer(InetAddress.parseNumericAddress(address)); - } - - /** - * Add a DNS server to the VPN connection. Both IPv4 and IPv6 - * addresses are supported. If none is set, the DNS servers of - * the default network will be used. - * - * @throws IllegalArgumentException if the address is invalid. - */ - public VpnBuilder addDnsServer(InetAddress address) { - if (address.isLoopbackAddress() || address.isAnyLocalAddress()) { - throw new IllegalArgumentException("Bad address"); - } - if (mConfig.dnsServers == null) { - mConfig.dnsServers = new ArrayList<String>(); - } - mConfig.dnsServers.add(address.getHostAddress()); - return this; - } - - /** - * Add a search domain to the DNS resolver. - */ - public VpnBuilder addSearchDomain(String domain) { - if (mConfig.searchDomains == null) { - mConfig.searchDomains = new ArrayList<String>(); - } - mConfig.searchDomains.add(domain); - return this; - } - - /** - * Create a VPN interface using the parameters supplied to this builder. - * The interface works on IP packets, and a file descriptor is returned - * for the application to access them. Each read retrieves an outgoing - * packet which was routed to the interface. Each write injects an - * incoming packet just like it was received from the interface. The file - * descriptor is put into non-blocking mode by default to avoid blocking - * Java threads. To use the file descriptor completely in native space, - * see {@link ParcelFileDescriptor#detachFd()}. The application MUST - * close the file descriptor when the VPN connection is terminated. The - * VPN interface will be removed and the network will be restored by the - * framework automatically. - * - * <p>To avoid conflicts, there can be only one active VPN interface at - * the same time. Usually network parameters are never changed during the - * lifetime of a VPN connection. It is also common for an application to - * create a new file descriptor after closing the previous one. However, - * it is rare but not impossible to have two interfaces while performing a - * seamless handover. In this case, the old interface will be deactivated - * when the new one is configured successfully. Both file descriptors are - * valid but now outgoing packets will be routed to the new interface. - * Therefore, after draining the old file descriptor, the application MUST - * close it and start using the new file descriptor. If the new interface - * cannot be created, the existing interface and its file descriptor remain - * untouched. - * - * <p>An exception will be thrown if the interface cannot be created for - * any reason. However, this method returns {@code null} if the application - * is not prepared or is revoked by another application. This helps solve - * possible race conditions while handling {@link #ACTION_VPN_REVOKED} - * broadcasts. - * - * @return {@link ParcelFileDescriptor} of the VPN interface, or - * {@code null} if the application is not prepared. - * @throws IllegalArgumentException if a parameter is not accepted by the - * operating system. - * @throws IllegalStateException if a parameter cannot be applied by the - * operating system. - * @see #prepare - */ - public ParcelFileDescriptor establish() { - mConfig.addresses = mAddresses.toString(); - mConfig.routes = mRoutes.toString(); - - try { - return getService().establishVpn(mConfig); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - } - - /** - * Protect a socket from VPN connections. The socket will be bound to the - * current default network interface, so its traffic will not be forwarded - * through VPN. This method is useful if some connections need to be kept - * outside of VPN. For example, a VPN tunnel should protect itself if its - * destination is covered by VPN routes. Otherwise its outgoing packets - * will be sent back to the VPN interface and cause an infinite loop. - * - * <p>The socket is NOT closed by this method. - * - * @return {@code true} on success. - */ - public static boolean protect(int socket) { - ParcelFileDescriptor dup = null; - try { - dup = ParcelFileDescriptor.fromFd(socket); - return getService().protectVpn(dup); - } catch (Exception e) { - return false; - } finally { - try { - dup.close(); - } catch (Exception e) { - // ignore - } - } - } - - /** - * Protect a {@link Socket} from VPN connections. - * - * @return {@code true} on success. - * @see #protect(int) - */ - public static boolean protect(Socket socket) { - return protect(socket.getFileDescriptor$().getInt$()); - } - - /** - * Protect a {@link DatagramSocket} from VPN connections. - * - * @return {@code true} on success. - * @see #protect(int) - */ - public static boolean protect(DatagramSocket socket) { - return protect(socket.getFileDescriptor$().getInt$()); - } -} diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl index 3704248e8076..bc4e00cc4d21 100644 --- a/core/java/android/os/INetworkManagementService.aidl +++ b/core/java/android/os/INetworkManagementService.aidl @@ -240,6 +240,11 @@ interface INetworkManagementService 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/VoicemailContract.java b/core/java/android/provider/VoicemailContract.java index 521762479181..814f50ba5646 100644 --- a/core/java/android/provider/VoicemailContract.java +++ b/core/java/android/provider/VoicemailContract.java @@ -41,7 +41,7 @@ import android.provider.CallLog.Calls; * </ul> * * <P> The minimum permission needed to access this content provider is - * {@link Manifest.permission#READ_WRITE_OWN_VOICEMAIL} + * {@link Manifest.permission#ADD_VOICEMAIL} * * <P>Voicemails are inserted by what is called as a "voicemail source" * application, which is responsible for syncing voicemail data between a remote diff --git a/core/java/android/text/TextDirectionHeuristics.java b/core/java/android/text/TextDirectionHeuristics.java index 5f9ffc5bf2f0..20170bf9615d 100644 --- a/core/java/android/text/TextDirectionHeuristics.java +++ b/core/java/android/text/TextDirectionHeuristics.java @@ -164,6 +164,7 @@ public class TextDirectionHeuristics { case Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC: case Character.DIRECTIONALITY_RIGHT_TO_LEFT_EMBEDDING: case Character.DIRECTIONALITY_RIGHT_TO_LEFT_OVERRIDE: + case Character.DIRECTIONALITY_ARABIC_NUMBER: return TriState.TRUE; default: return TriState.UNKNOWN; diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index cadf2aba0cec..9737a5a9bc07 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -628,6 +628,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private int mGlowPaddingLeft; private int mGlowPaddingRight; + private int mLastAccessibilityScrollEventFromIndex; + private int mLastAccessibilityScrollEventToIndex; + /** * Interface definition for a callback to be invoked when the list or grid * has been scrolled. @@ -1265,6 +1268,24 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } @Override + public void sendAccessibilityEvent(int eventType) { + // Since this class calls onScrollChanged even if the mFirstPosition and the + // child count have not changed we will avoid sending duplicate accessibility + // events. + if (eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) { + final int lastPosition = mFirstPosition + getChildCount(); + if (mLastAccessibilityScrollEventFromIndex == mFirstPosition + && mLastAccessibilityScrollEventToIndex == lastPosition) { + return; + } else { + mLastAccessibilityScrollEventFromIndex = mFirstPosition; + mLastAccessibilityScrollEventToIndex = lastPosition; + } + } + super.sendAccessibilityEvent(eventType); + } + + @Override public void onInitializeAccessibilityEvent(AccessibilityEvent event) { super.onInitializeAccessibilityEvent(event); event.setScrollable(true); diff --git a/core/java/android/widget/AdapterView.java b/core/java/android/widget/AdapterView.java index 00c75a947a2a..9f5737e21f90 100644 --- a/core/java/android/widget/AdapterView.java +++ b/core/java/android/widget/AdapterView.java @@ -277,6 +277,7 @@ public abstract class AdapterView<T extends Adapter> extends ViewGroup { * called, false otherwise is returned. */ public boolean performItemClick(View view, int position, long id) { + view.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); if (mOnItemClickListener != null) { playSoundEffect(SoundEffectConstants.CLICK); mOnItemClickListener.onItemClick(this, view, position, id); diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index 00ebe0d8976a..7ad5d6c94853 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -116,6 +116,8 @@ class FastScroller { private int mOverlayPosition; + private boolean mMatchDragPosition; + private static final int FADE_TIMEOUT = 1500; private final Rect mTmpRect = new Rect(); @@ -262,6 +264,9 @@ class FastScroller { ta.recycle(); + mMatchDragPosition = context.getApplicationInfo().targetSdkVersion >= + android.os.Build.VERSION_CODES.HONEYCOMB; + setScrollbarPosition(mList.getVerticalScrollbarPosition()); } @@ -417,7 +422,7 @@ class FastScroller { } return; } - if (totalItemCount - visibleItemCount > 0 && mState != STATE_DRAGGING ) { + if (totalItemCount - visibleItemCount > 0 && mState != STATE_DRAGGING) { mThumbY = getThumbPositionForListPosition(firstVisibleItem, visibleItemCount, totalItemCount); if (mChangedBounds) { @@ -595,7 +600,7 @@ class FastScroller { if (mSectionIndexer == null) { getSectionsFromIndexer(); } - if (mSectionIndexer == null) { + if (mSectionIndexer == null || !mMatchDragPosition) { return ((mList.getHeight() - mThumbH) * firstVisibleItem) / (totalItemCount - visibleItemCount); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 968d61215bc7..04cf69b48925 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8449,6 +8449,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener info.setPassword(isPassword); } + @Override + public void sendAccessibilityEvent(int eventType) { + // Do not send scroll events since first they are not interesting for + // accessibility and second such events a generated too frequently. + // For details see the implementation of bringTextIntoView(). + if (eventType == AccessibilityEvent.TYPE_VIEW_SCROLLED) { + return; + } + super.sendAccessibilityEvent(eventType); + } + void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText, int fromIndex, int removedCount, int addedCount) { AccessibilityEvent event = diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index d36be10e0038..d61a5795fb85 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -32,7 +32,7 @@ import java.util.List; */ public class VpnConfig implements Parcelable { - public static final String ACTION_VPN_REVOKED = "android.net.vpn.action.REVOKED"; + public static final String SERVICE_INTERFACE = "android.net.VpnService"; public static final String LEGACY_VPN = "[Legacy VPN]"; @@ -52,7 +52,7 @@ public class VpnConfig implements Parcelable { PendingIntent.FLAG_NO_CREATE : PendingIntent.FLAG_CANCEL_CURRENT); } - public String packagz; + public String user; public String interfaze; public String session; public int mtu = -1; @@ -70,7 +70,7 @@ public class VpnConfig implements Parcelable { @Override public void writeToParcel(Parcel out, int flags) { - out.writeString(packagz); + out.writeString(user); out.writeString(interfaze); out.writeString(session); out.writeInt(mtu); @@ -87,7 +87,7 @@ public class VpnConfig implements Parcelable { @Override public VpnConfig createFromParcel(Parcel in) { VpnConfig config = new VpnConfig(); - config.packagz = in.readString(); + config.user = in.readString(); config.interfaze = in.readString(); config.session = in.readString(); config.mtu = in.readInt(); diff --git a/core/java/com/android/internal/util/Protocol.java b/core/java/com/android/internal/util/Protocol.java index 9ecd29ff81b9..0cadb1625c65 100644 --- a/core/java/com/android/internal/util/Protocol.java +++ b/core/java/com/android/internal/util/Protocol.java @@ -49,5 +49,6 @@ public class Protocol { 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 cbe72dd2ada4..36f02469ae6a 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -1226,6 +1226,12 @@ public class StateMachine { * 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/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 4878b0f088ac..61df5c79b277 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -583,7 +583,7 @@ public class ActionBarView extends AbsActionBarView { } public void setLogo(int resId) { - mContext.getResources().getDrawable(resId); + setLogo(mContext.getResources().getDrawable(resId)); } /** diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 6e7388955986..170957cf9783 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -178,6 +178,7 @@ LOCAL_C_INCLUDES += \ external/icu4c/i18n \ external/icu4c/common \ external/jpeg \ + external/harfbuzz/contrib \ external/harfbuzz/src \ external/zlib \ frameworks/opt/emoji \ diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp index 30fe298207a2..a29eb3898a92 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -17,6 +17,10 @@ #include "TextLayoutCache.h" #include "TextLayout.h" +extern "C" { +#include "harfbuzz-unicode.h" +} + namespace android { TextLayoutCache::TextLayoutCache() : @@ -355,7 +359,9 @@ void TextLayoutCacheValue::setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec shaperItem->item.pos = start; shaperItem->item.length = count; shaperItem->item.bidiLevel = isRTL; - shaperItem->item.script = isRTL ? HB_Script_Arabic : HB_Script_Common; + + ssize_t iter = 0; + shaperItem->item.script = code_point_to_script(utf16_to_code_point(chars, count, &iter)); shaperItem->string = chars; shaperItem->stringLength = contextCount; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 21c3f1e2d4c3..57b686a0a23a 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -63,7 +63,7 @@ <protected-broadcast android:name="android.app.action.EXIT_CAR_MODE" /> <protected-broadcast android:name="android.app.action.ENTER_DESK_MODE" /> <protected-broadcast android:name="android.app.action.EXIT_DESK_MODE" /> - + <protected-broadcast android:name="android.backup.intent.RUN" /> <protected-broadcast android:name="android.backup.intent.CLEAR" /> <protected-broadcast android:name="android.backup.intent.INIT" /> @@ -292,16 +292,6 @@ android:description="@string/permdesc_setAlarm" android:protectionLevel="normal" /> - <!-- Allows an application to read/write the voicemails owned by its own - package. --> - <!-- TODO: delete this permission when dependent content provider & - application code has been migrated to use ADD_VOICEMAIL instead --> - <permission android:name="com.android.voicemail.permission.READ_WRITE_OWN_VOICEMAIL" - android:permissionGroup="android.permission-group.PERSONAL_INFO" - android:protectionLevel="dangerous" - android:label="@string/permlab_readWriteOwnVoicemail" - android:description="@string/permdesc_readWriteOwnVoicemail" /> - <!-- Allows an application to add voicemails into the system. --> <permission android:name="com.android.voicemail.permission.ADD_VOICEMAIL" android:permissionGroup="android.permission-group.PERSONAL_INFO" @@ -402,14 +392,6 @@ android:description="@string/permdesc_nfc" android:label="@string/permlab_nfc" /> - <!-- Allows applications to provide VPN functionality. - @hide Pending API council approval --> - <permission android:name="android.permission.VPN" - android:permissionGroup="android.permission-group.NETWORK" - android:protectionLevel="dangerous" - android:description="@string/permdesc_vpn" - android:label="@string/permlab_vpn" /> - <!-- Allows an application to use SIP service --> <permission android:name="android.permission.USE_SIP" android:permissionGroup="android.permission-group.NETWORK" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index a6c92f2e26b8..6a3c2d588f9a 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1358,14 +1358,6 @@ with Near Field Communication (NFC) tags, cards, and readers.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permlab_vpn">intercept and modify all network traffic</string> - <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> - <string name="permdesc_vpn">Allows an application to intercept and - inspect all network traffic to establish a VPN connection. - Malicious applications may monitor, redirect, or modify network packets - without your knowledge.</string> - - <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_disableKeyguard">disable keylock</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_disableKeyguard">Allows an application to disable @@ -2159,14 +2151,6 @@ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> - <string name="permlab_readWriteOwnVoicemail">Access voicemails managed by this application</string> - <!-- Description of an application permission, listed so the user can choose whether - they want to allow the application to do this. [CHAR LIMIT=NONE] --> - <string name="permdesc_readWriteOwnVoicemail">Allows the application to store and retrieve only - voicemails that its associated service can access.</string> - - <!-- Title of an application permission, listed so the user can choose whether - they want to allow the application to do this. [CHAR LIMIT=NONE] --> <string name="permlab_addVoicemail">add voicemail</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE] --> diff --git a/core/tests/coretests/src/android/animation/EventsTest.java b/core/tests/coretests/src/android/animation/EventsTest.java index f970ffc59806..6ea28452c961 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.UiThreadTest; 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 @@ public abstract class EventsTest 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 @@ public abstract class EventsTest // 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() { + 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() { - mAnim.cancel(); + try { + mAnim.end(); + } catch (junit.framework.AssertionFailedError e) { + mFuture.setException(new RuntimeException(e)); + } } }; @@ -76,6 +108,23 @@ public abstract class EventsTest 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 abstract class EventsTest public void run() { mFuture.release(); } - }, ANIM_MID_DURATION); + }, FUTURE_RELEASE_DELAY); } }; @@ -92,7 +141,6 @@ public abstract class EventsTest super(BasicAnimatorActivity.class); } - /** * Sets up the fields used by each test. Subclasses must override this method to create * the protected mAnimator object used in all tests. Overrides must create that animator @@ -107,11 +155,20 @@ public abstract class EventsTest // 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 @@ public abstract class EventsTest // 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 @@ public abstract class EventsTest mRunning = false; mCanceled = false; + mStarted = false; } /** @@ -144,26 +204,104 @@ public abstract class EventsTest } /** + * 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); } /** - * Same as testStartCancel, but with a startDelayed animator + * 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 + */ + @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 @@ public abstract class EventsTest 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(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(); + mFuture.get(TIMEOUT, TimeUnit.MILLISECONDS); } /** @@ -204,13 +365,91 @@ public abstract class EventsTest 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(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(); + 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 @@ public abstract class EventsTest } } }); - 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 @@ public abstract class EventsTest } } }); - 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 242057cf963d..4db4ea52d527 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 android.util.Log; 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 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { 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 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { 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 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // 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 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // 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 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // 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 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { 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 class NetworkStatsHistoryTest extends AndroidTestCase { 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 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { // 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 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { } } + 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 @@ public class NetworkStatsHistoryTest extends AndroidTestCase { 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/developing/tools/draw9patch.jd b/docs/html/guide/developing/tools/draw9patch.jd index 1d9de4f1f4ca..7cf0e4b1d861 100644 --- a/docs/html/guide/developing/tools/draw9patch.jd +++ b/docs/html/guide/developing/tools/draw9patch.jd @@ -41,7 +41,7 @@ You'll need the PNG image with which you'd like to create a NinePatch.</p> A previously saved 9-patch file (<code>*.9.png</code>) will be loaded as-is, with no drawing area added, because it already exists.</p> -<img src="{@docRoot}images/draw9patch-bad.png" style="float:right" alt="" height="300" width="341" +<img src="{@docRoot}images/draw9patch-bad.png" style="float:right;clear:both" alt="" height="300" width="341" /> <p>Optional controls include:</p> diff --git a/drm/libdrmframework/plugins/common/util/include/SessionMap.h b/drm/libdrmframework/plugins/common/util/include/SessionMap.h index 3dff58c17ef7..e5638942c234 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; + /** + * 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(); + } - if (map.size() > index) { - value = map.valueAt(index); + /** + * 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); + + if (map.size() > index) { + value = map.valueAt(index); + } + return value; } - return value; -} + /** + * 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); + } -/** - * 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) { + Mutex::Autolock lock(mLock); + return isCreatedInternal(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)); -} + SessionMap<TValue> & operator=(const SessionMap<TValue> & objectCopy) { + Mutex::Autolock lock(mLock); -/** - * empty the entire session table. It releases all the memory for session objects. - */ -void destroyMap() { - int size = map.size(); - int i = 0; + destroyMap(); + map = objectCopy.map; + return *this; + } - for (i = 0; i < size; i++) { - deleteValue(map.valueAt(i)); +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; } - map.clear(); -} + /** + * 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(); + } -/** - * 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 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 4ee903ef01d2..57ef7992fd04 100644 --- a/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp +++ b/drm/libdrmframework/plugins/common/util/src/MimeTypeUtil.cpp @@ -22,6 +22,13 @@ namespace android { #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_audio[] = "audio/"; 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 @@ static struct MimeTypeList mimeTypeList[] = { * 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; + 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; + } + pGroup++; } - 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); - break; + /* 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++; } - pMimeItem++; } + } else { + result = String8(mime_type_unsupported); } - LOGI("convertMimeType got mimetype %s, converted into mimetype %s", - pMimeType, result.string()); + 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 9805a4094c06..e359dbd81fce 100644 --- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/Android.mk @@ -17,6 +17,9 @@ LOCAL_PATH := $(call my-dir) 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 31c3c1451e99..e1845454d361 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 @@ extern "C" void destroy(IDrmEngine* plugIn) { } FwdLockEngine::FwdLockEngine() { - LOGV("FwdLockEngine Construction"); + LOG_VERBOSE("FwdLockEngine Construction"); } FwdLockEngine::~FwdLockEngine() { - LOGV("FwdLockEngine Destruction"); + LOG_VERBOSE("FwdLockEngine Destruction"); + + int size = decodeSessionMap.getSize(); - convertSessionMap.destroyMap(); - decodeSessionMap.destroyMap(); + 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 @@ int FwdLockEngine::getConvertedStatus(FwdLockConv_Status_t status) { 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 @@ int FwdLockEngine::getConvertedStatus(FwdLockConv_Status_t status) { 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 @@ DrmConstraints* FwdLockEngine::onGetConstraints(int uniqueId, const String8* pat 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 @@ DrmMetadata* FwdLockEngine::onGetMetadata(int uniqueId, const String8* path) { } 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::onInitialize(int uniqueId) { 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 @@ android::status_t FwdLockEngine::onTerminate(int uniqueId) { 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* FwdLockEngine::onProcessDrmInfo(int uniqueId, const DrmInfo* drmI drmInfoStatus = new DrmInfoStatus((int)DrmInfoStatus::STATUS_OK, 0, NULL, String8("")); - LOGV("FwdLockEngine::onProcessDrmInfo"); + LOG_VERBOSE("FwdLockEngine::onProcessDrmInfo"); return drmInfoStatus; } @@ -193,7 +210,7 @@ status_t FwdLockEngine::onSaveRights( 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* FwdLockEngine::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drm 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 FwdLockEngine::onCheckRightsStatus(int uniqueId, 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 @@ status_t FwdLockEngine::onConsumeRights(int uniqueId, int action, bool reserve) { // No rights consumption - LOGV("FwdLockEngine::onConsumeRights"); + LOG_VERBOSE("FwdLockEngine::onConsumeRights"); return DRM_NO_ERROR; } @@ -249,14 +266,14 @@ bool FwdLockEngine::onValidateAction(int uniqueId, 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 @@ int FwdLockEngine::onGetDrmObjectType(int uniqueId, const String8& mimeType) { String8 mimeStr = String8(mimeType); - LOGV("FwdLockEngine::onGetDrmObjectType"); + LOG_VERBOSE("FwdLockEngine::onGetDrmObjectType"); mimeStr.toLower(); @@ -301,13 +318,13 @@ int FwdLockEngine::onGetDrmObjectType(int uniqueId, 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 @@ status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decrypt 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 @@ status_t FwdLockEngine::onOpenConvertSession(int uniqueId, 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 @@ DrmConvertedStatus* FwdLockEngine::onCloseConvertSession(int uniqueId, 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 FwdLockEngine::onOpenDecryptSession(int uniqueId, 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 @@ status_t FwdLockEngine::onOpenDecryptSession(int uniqueId, 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 @@ status_t FwdLockEngine::onOpenDecryptSession(int uniqueId, ::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::onOpenDecryptSession(int uniqueId, 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 @@ status_t FwdLockEngine::onCloseDecryptSession(int uniqueId, decryptHandle = NULL; } - LOGV("FwdLockEngine::onCloseDecryptSession Exit"); + LOG_VERBOSE("FwdLockEngine::onCloseDecryptSession Exit"); return result; } @@ -533,13 +550,13 @@ status_t FwdLockEngine::onInitializeDecryptUnit(int uniqueId, 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 @@ status_t FwdLockEngine::onDecrypt(int uniqueId, 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 @@ ssize_t FwdLockEngine::onPread(int uniqueId, 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 14ea9e909fd2..299116deb469 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 @@ static int FwdLockConv_DeriveKeys(FwdLockConv_Session_t *pSession) { } /** - * 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 @@ static FwdLockConv_Status_t FwdLockConv_PushChar(FwdLockConv_Session_t *pSession 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 @@ FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_ 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 @@ FwdLockConv_Status_t FwdLockConv_CloseSession(int sessionId, FwdLockConv_Output_ 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 98284e722571..dacf00efaf13 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 @@ static int FwdLockFile_AcquireSession(int fileDesc) { } /** - * 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 @@ int FwdLockFile_CheckDataIntegrity(int fileDesc) { 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 @@ int FwdLockFile_CheckDataIntegrity(int fileDesc) { } 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 @@ int FwdLockFile_CheckHeaderIntegrity(int fileDesc) { } 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/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index b8e9384c1de8..7269a71d91c0 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -84,9 +84,19 @@ public class ImageFormat { public static final int JPEG = 0x100; /** + * Raw bayer format used for images, which is 10 bit precision samples + * stored in 16 bit words. The filter pattern is RGGB. Whether this format + * is supported by the camera hardware can be determined by + * {@link android.hardware.Camera.Parameters#getSupportedPreviewFormats()}. + * + * @hide + */ + public static final int BAYER_RGGB = 0x200; + + /** * Use this function to retrieve the number of bits per pixel of an * ImageFormat. - * + * * @param format * @return the number of bits per pixel of the given format or -1 if the * format doesn't exist or is not supported. @@ -103,6 +113,8 @@ public class ImageFormat { return 12; case NV21: return 12; + case BAYER_RGGB: + return 16; } return -1; } diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h index 48483fda548e..b6614965b0f5 100644 --- a/include/camera/CameraParameters.h +++ b/include/camera/CameraParameters.h @@ -573,6 +573,9 @@ public: static const char PIXEL_FORMAT_RGB565[]; static const char PIXEL_FORMAT_RGBA8888[]; static const char PIXEL_FORMAT_JPEG[]; + // Raw bayer format used for images, which is 10 bit precision samples + // stored in 16 bit words. The filter pattern is RGGB. + static const char PIXEL_FORMAT_BAYER_RGGB[]; // Values for focus mode settings. // Auto-focus mode. Applications should call diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h index c1c4f9498ad3..3d79596f3f46 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 @@ public: 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 @@ private: void *mData; size_t mSize, mRangeOffset, mRangeLength; sp<GraphicBuffer> mGraphicBuffer; + sp<ABuffer> mBuffer; bool mOwnsData; diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp index 251615d5f026..51b96c1fe9dd 100644 --- a/libs/camera/CameraParameters.cpp +++ b/libs/camera/CameraParameters.cpp @@ -151,6 +151,7 @@ const char CameraParameters::PIXEL_FORMAT_YUV420P[] = "yuv420p"; const char CameraParameters::PIXEL_FORMAT_RGB565[] = "rgb565"; const char CameraParameters::PIXEL_FORMAT_RGBA8888[] = "rgba8888"; const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg"; +const char CameraParameters::PIXEL_FORMAT_BAYER_RGGB[] = "bayer-rggb"; // Values for focus mode settings. const char CameraParameters::FOCUS_MODE_AUTO[] = "auto"; diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index 731d1f38e75d..e7a306b78028 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1802,36 +1802,20 @@ public class AudioManager { /** * @hide - * FIXME to be changed to address Neel's comments - * Force a refresh of the remote control client associated with the event receiver. + * Notifies the users of the associated remote control client that the information to display + * has changed. * @param eventReceiver */ - public void refreshRemoteControlDisplay(ComponentName eventReceiver) { + public void notifyRemoteControlInformationChanged(ComponentName eventReceiver) { IAudioService service = getService(); try { - service.refreshRemoteControlDisplay(eventReceiver); + service.notifyRemoteControlInformationChanged(eventReceiver); } catch (RemoteException e) { Log.e(TAG, "Dead object in refreshRemoteControlDisplay"+e); } } /** - * @hide - * FIXME API to be used by implementors of remote controls, not a candidate for SDK - */ - public void registerRemoteControlObserver() { - - } - - /** - * @hide - * FIXME API to be used by implementors of remote controls, not a candidate for SDK - */ - public void unregisterRemoteControlObserver() { - - } - - /** * @hide * Reload audio settings. This method is called by Settings backup * agent when audio settings are restored and causes the AudioService diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index cd55b0e9c2f1..bf1585dceed3 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -3200,8 +3200,8 @@ public class AudioService extends IAudioService.Stub { } } - /** see AudioManager.refreshRemoteControlDisplay(ComponentName er) */ - public void refreshRemoteControlDisplay(ComponentName eventReceiver) { + /** see AudioManager.notifyRemoteControlInformationChanged(ComponentName er) */ + public void notifyRemoteControlInformationChanged(ComponentName eventReceiver) { synchronized(mAudioFocusLock) { synchronized(mRCStack) { // only refresh if the eventReceiver is at the top of the stack diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl index 1a05f15248b7..9afe55326e68 100644 --- a/media/java/android/media/IAudioService.aidl +++ b/media/java/android/media/IAudioService.aidl @@ -93,7 +93,7 @@ interface IAudioService { void registerRemoteControlClient(in ComponentName eventReceiver, in IRemoteControlClient rcClient, in String callingPackageName); - void refreshRemoteControlDisplay(in ComponentName eventReceiver); + void notifyRemoteControlInformationChanged(in ComponentName eventReceiver); void startBluetoothSco(IBinder cb); diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp index a8fadf2c3a8f..0b14f1ecfbf6 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 @@ MediaBuffer::MediaBuffer(const sp<GraphicBuffer>& graphicBuffer) 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 07a9eb892c5d..887fe7c4e4e6 100644 --- a/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp +++ b/media/libstagefright/chromium_http/ChromiumHTTPDataSource.cpp @@ -47,6 +47,8 @@ ChromiumHTTPDataSource::~ChromiumHTTPDataSource() { delete mDelegate; mDelegate = NULL; + clearDRMState_l(); + if (mDrmManagerClient != NULL) { delete mDrmManagerClient; mDrmManagerClient = NULL; @@ -116,8 +118,6 @@ void ChromiumHTTPDataSource::onConnectionFailed(status_t err) { // mURI.clear(); mIOResult = err; - - clearDRMState_l(); } void ChromiumHTTPDataSource::disconnect() { @@ -251,8 +251,6 @@ void ChromiumHTTPDataSource::onDisconnectComplete() { // mURI.clear(); mCondition.broadcast(); - - clearDRMState_l(); } sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization() { diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp index eb10ab76b582..26c3eda8b058 100644 --- a/media/libstagefright/chromium_http/support.cpp +++ b/media/libstagefright/chromium_http/support.cpp @@ -183,19 +183,19 @@ void SfDelegate::setOwner(ChromiumHTTPDataSource *owner) { void SfDelegate::OnReceivedRedirect( net::URLRequest *request, const GURL &new_url, bool *defer_redirect) { - MY_LOGI("OnReceivedRedirect"); + MY_LOGV("OnReceivedRedirect"); } void SfDelegate::OnAuthRequired( net::URLRequest *request, net::AuthChallengeInfo *auth_info) { - MY_LOGI("OnAuthRequired"); + MY_LOGV("OnAuthRequired"); inherited::OnAuthRequired(request, auth_info); } void SfDelegate::OnCertificateRequested( net::URLRequest *request, net::SSLCertRequestInfo *cert_request_info) { - MY_LOGI("OnCertificateRequested"); + MY_LOGV("OnCertificateRequested"); inherited::OnCertificateRequested(request, cert_request_info); } @@ -208,7 +208,7 @@ void SfDelegate::OnSSLCertificateError( } void SfDelegate::OnGetCookies(net::URLRequest *request, bool blocked_by_policy) { - MY_LOGI("OnGetCookies"); + MY_LOGV("OnGetCookies"); } void SfDelegate::OnSetCookie( @@ -216,7 +216,7 @@ void SfDelegate::OnSetCookie( const std::string &cookie_line, const net::CookieOptions &options, bool blocked_by_policy) { - MY_LOGI("OnSetCookie"); + MY_LOGV("OnSetCookie"); } void SfDelegate::OnResponseStarted(net::URLRequest *request) { diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp index 2578d2dbd2a5..f67cdace819c 100644 --- a/media/libstagefright/httplive/LiveSession.cpp +++ b/media/libstagefright/httplive/LiveSession.cpp @@ -617,23 +617,32 @@ rinse_repeat: goto rinse_repeat; } - if (!mPlaylist->isComplete() - && mSeqNumber > lastSeqNumberInPlaylist - && mNumRetries < kMaxNumRetries) { + if (!mPlaylist->isComplete() && mNumRetries < kMaxNumRetries) { ++mNumRetries; - mLastPlaylistFetchTimeUs = -1; - postMonitorQueue(3000000ll); - return; - } + if (mSeqNumber > lastSeqNumberInPlaylist) { + mLastPlaylistFetchTimeUs = -1; + postMonitorQueue(3000000ll); + return; + } - LOGE("Cannot find sequence number %d in playlist " - "(contains %d - %d)", - mSeqNumber, firstSeqNumberInPlaylist, - firstSeqNumberInPlaylist + mPlaylist->size() - 1); + // we've missed the boat, let's start from the lowest sequence + // number available and signal a discontinuity. - mDataSource->queueEOS(ERROR_END_OF_STREAM); - return; + LOGI("We've missed the boat, restarting playback."); + mSeqNumber = lastSeqNumberInPlaylist; + explicitDiscontinuity = true; + + // fall through + } else { + LOGE("Cannot find sequence number %d in playlist " + "(contains %d - %d)", + mSeqNumber, firstSeqNumberInPlaylist, + firstSeqNumberInPlaylist + mPlaylist->size() - 1); + + mDataSource->queueEOS(ERROR_END_OF_STREAM); + return; + } } mNumRetries = 0; diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index 59de17eb7bf5..2e66a2cf1d5f 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -105,12 +105,10 @@ status_t AnotherPacketSource::read( int64_t timeUs; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); - MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size()); - mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs); + MediaBuffer *mediaBuffer = new MediaBuffer(buffer); - // hexdump(buffer->data(), buffer->size()); + mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs); - 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 a02591f70941..4ecb92f02c5f 100644 --- a/media/libstagefright/rtsp/APacketSource.cpp +++ b/media/libstagefright/rtsp/APacketSource.cpp @@ -628,14 +628,12 @@ status_t APacketSource::read( 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/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 7bb8277ffb48..25eab26af403 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1873,7 +1873,7 @@ public class PhoneStatusBar extends StatusBar { } void updateExpandedSize() { - if (mExpandedDialog != null) { + if (mExpandedDialog != null && mExpandedParams != null) { mExpandedParams.width = mDisplaySize.x; mExpandedParams.height = getExpandedHeight(mDisplaySize.y); if (!mExpandedVisible) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java index f32c6021386f..97b629870c1f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java @@ -61,6 +61,7 @@ public class NetworkController extends BroadcastReceiver { // debug static final String TAG = "StatusBar.NetworkController"; static final boolean DEBUG = false; + static final boolean CHATTY = true; // additional diagnostics, but not logspew // telephony boolean mHspaDataDistinguishable; @@ -286,7 +287,7 @@ public class NetworkController extends BroadcastReceiver { @Override public void onDataConnectionStateChanged(int state, int networkType) { - if (DEBUG) { + if (DEBUG || CHATTY) { Slog.d(TAG, "onDataConnectionStateChanged: state=" + state + " type=" + networkType); } @@ -682,10 +683,19 @@ public class NetworkController extends BroadcastReceiver { // ===== Full or limited Internet connectivity ================================== private void updateConnectivity(Intent intent) { + if (CHATTY) { + Slog.d(TAG, "updateConnectivity: intent=" + intent); + } + NetworkInfo info = (NetworkInfo)(intent.getParcelableExtra( ConnectivityManager.EXTRA_NETWORK_INFO)); int connectionStatus = intent.getIntExtra(ConnectivityManager.EXTRA_INET_CONDITION, 0); + if (CHATTY) { + Slog.d(TAG, "updateConnectivity: networkInfo=" + info); + Slog.d(TAG, "updateConnectivity: connectionStatus=" + connectionStatus); + } + int inetCondition = (connectionStatus > INET_CONDITION_THRESHOLD ? 1 : 0); switch (info.getType()) { 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 dc7e3137386c..c7b6f2094ac8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -1606,6 +1606,10 @@ public class TabletStatusBar extends StatusBar implements } return; + } else if (0 != (mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS)) { + // if icons are disabled but we're not in DND mode, this is probably Setup and we should + // just leave the area totally empty + return; } int N = mNotificationData.size(); diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml index bd5f739945b7..8e062b7870d2 100644 --- a/packages/VpnDialogs/AndroidManifest.xml +++ b/packages/VpnDialogs/AndroidManifest.xml @@ -5,7 +5,6 @@ <application android:label="VpnDialogs" android:allowBackup="false" > <activity android:name=".ConfirmDialog" - android:permission="android.permission.VPN" android:theme="@style/transparent"> <intent-filter> <action android:name="android.intent.action.MAIN"/> diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java index 40c0a02e7912..d668e98f0f27 100644 --- a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java +++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java @@ -79,7 +79,7 @@ public class ManageDialog extends Activity implements Handler.Callback, mDataTransmitted = (TextView) view.findViewById(R.id.data_transmitted); mDataReceived = (TextView) view.findViewById(R.id.data_received); - if (mConfig.packagz.equals(VpnConfig.LEGACY_VPN)) { + if (mConfig.user.equals(VpnConfig.LEGACY_VPN)) { mDialog = new AlertDialog.Builder(this) .setIcon(android.R.drawable.ic_dialog_info) .setTitle(R.string.legacy_title) @@ -89,7 +89,7 @@ public class ManageDialog extends Activity implements Handler.Callback, .create(); } else { PackageManager pm = getPackageManager(); - ApplicationInfo app = pm.getApplicationInfo(mConfig.packagz, 0); + ApplicationInfo app = pm.getApplicationInfo(mConfig.user, 0); mDialog = new AlertDialog.Builder(this) .setIcon(app.loadIcon(pm)) .setTitle(app.loadLabel(pm)) @@ -131,7 +131,7 @@ public class ManageDialog extends Activity implements Handler.Callback, if (which == AlertDialog.BUTTON_POSITIVE) { mConfig.configureIntent.send(); } else if (which == AlertDialog.BUTTON_NEUTRAL) { - mService.prepareVpn(mConfig.packagz, VpnConfig.LEGACY_VPN); + mService.prepareVpn(mConfig.user, VpnConfig.LEGACY_VPN); } } catch (Exception e) { Log.e(TAG, "onClick", e); diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index bdc37ef4a570..a2dbb788623a 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -3018,7 +3018,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - Runnable mScreenSaverActivator = new Runnable() { + Runnable mScreenSaverActivator = null; + /*new Runnable() { public void run() { synchronized (this) { if (!(mScreenSaverEnabled && mScreenOn)) { @@ -3049,9 +3050,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } }; + */ // Must call while holding mLock private void updateScreenSaverTimeoutLocked() { + if (mScreenSaverActivator == null) return; + synchronized (mScreenSaverActivator) { mHandler.removeCallbacks(mScreenSaverActivator); if (mScreenSaverEnabled && mScreenOn && mScreenSaverTimeout > 0) { diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index a59b6c07f356..30de385f305a 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -1231,6 +1231,13 @@ class NetworkManagementService extends INetworkManagementService.Stub { } } + @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/SystemServer.java b/services/java/com/android/server/SystemServer.java index f15eca67d70d..4a0dcdfd3b68 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -27,6 +27,7 @@ import android.content.Intent; import android.content.pm.IPackageManager; import android.content.res.Configuration; import android.media.AudioService; +import android.net.wifi.p2p.WifiP2pService; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; @@ -37,7 +38,6 @@ import android.provider.Settings; import android.server.BluetoothA2dpService; import android.server.BluetoothService; import android.server.search.SearchManagerService; -import android.server.WifiP2pService; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Slog; diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index ecbad09909d0..b69cc31c1d26 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -54,7 +54,6 @@ import java.util.Arrays; public class Vpn extends INetworkManagementEventObserver.Stub { private final static String TAG = "Vpn"; - private final static String VPN = android.Manifest.permission.VPN; private final Context mContext; private final VpnCallback mCallback; @@ -69,18 +68,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } /** - * Protect a socket from routing changes by binding it to the given - * interface. The socket is NOT closed by this method. - * - * @param socket The socket to be bound. - * @param name The name of the interface. - */ - public void protect(ParcelFileDescriptor socket, String interfaze) { - mContext.enforceCallingPermission(VPN, "protect"); - jniProtect(socket.getFd(), interfaze); - } - - /** * Prepare for a VPN application. This method is designed to solve * race conditions. It first compares the current prepared package * with {@code oldPackage}. If they are the same, the prepared @@ -115,13 +102,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub { throw new SecurityException("Unauthorized Caller"); } - // Check the permission of the given package. - PackageManager pm = mContext.getPackageManager(); - if (!newPackage.equals(VpnConfig.LEGACY_VPN) && - pm.checkPermission(VPN, newPackage) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException(newPackage + " does not have " + VPN); - } - // Reset the interface and hide the notification. if (mInterface != null) { jniReset(mInterface); @@ -130,12 +110,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mInterface = null; } - // Send out the broadcast or stop LegacyVpnRunner. + // Revoke the connection or stop LegacyVpnRunner. if (!mPackage.equals(VpnConfig.LEGACY_VPN)) { - Intent intent = new Intent(VpnConfig.ACTION_VPN_REVOKED); - intent.setPackage(mPackage); - intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); - mContext.sendBroadcast(intent); + // TODO } else if (mLegacyVpnRunner != null) { mLegacyVpnRunner.exit(); mLegacyVpnRunner = null; @@ -147,6 +124,22 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } /** + * Protect a socket from routing changes by binding it to the given + * interface. The socket is NOT closed by this method. + * + * @param socket The socket to be bound. + * @param name The name of the interface. + */ + public void protect(ParcelFileDescriptor socket, String interfaze) throws Exception { + PackageManager pm = mContext.getPackageManager(); + ApplicationInfo app = pm.getApplicationInfo(mPackage, 0); + if (Binder.getCallingUid() != app.uid) { + throw new SecurityException("Unauthorized Caller"); + } + jniProtect(socket.getFd(), interfaze); + } + + /** * Establish a VPN network and return the file descriptor of the VPN * interface. This methods returns {@code null} if the application is * revoked or not prepared. @@ -155,9 +148,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub { * @return The file descriptor of the VPN interface. */ public synchronized ParcelFileDescriptor establish(VpnConfig config) { - // Check the permission of the caller. - mContext.enforceCallingPermission(VPN, "establish"); - // Check if the caller is already prepared. PackageManager pm = mContext.getPackageManager(); ApplicationInfo app = null; @@ -170,6 +160,9 @@ public class Vpn extends INetworkManagementEventObserver.Stub { return null; } + // Check if the service is properly declared. + // TODO + // Load the label. String label = app.loadLabel(pm).toString(); @@ -198,6 +191,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { if (config.routes != null) { jniSetRoutes(interfaze, config.routes); } + // TODO: bind the service if (mInterface != null && !mInterface.equals(interfaze)) { jniReset(mInterface); } @@ -211,23 +205,25 @@ public class Vpn extends INetworkManagementEventObserver.Stub { throw e; } - // Override DNS servers and search domains. - mCallback.override(config.dnsServers, config.searchDomains); - // Fill more values. - config.packagz = mPackage; + config.user = mPackage; config.interfaze = mInterface; - // Show the notification! + // Override DNS servers and show the notification. + long identity = Binder.clearCallingIdentity(); + mCallback.override(config.dnsServers, config.searchDomains); showNotification(config, label, bitmap); + Binder.restoreCallingIdentity(identity); return tun; } // INetworkManagementEventObserver.Stub + @Override public void interfaceAdded(String interfaze) { } // INetworkManagementEventObserver.Stub + @Override public synchronized void interfaceStatusChanged(String interfaze, boolean up) { if (!up && mLegacyVpnRunner != null) { mLegacyVpnRunner.check(interfaze); @@ -235,22 +231,28 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } // INetworkManagementEventObserver.Stub - public synchronized void interfaceLinkStateChanged(String interfaze, boolean up) { - if (!up && mLegacyVpnRunner != null) { - mLegacyVpnRunner.check(interfaze); - } + @Override + public void interfaceLinkStateChanged(String interfaze, boolean up) { + interfaceStatusChanged(interfaze, up); } // INetworkManagementEventObserver.Stub + @Override public synchronized void interfaceRemoved(String interfaze) { if (interfaze.equals(mInterface) && jniCheck(interfaze) == 0) { + long identity = Binder.clearCallingIdentity(); mCallback.restore(); hideNotification(); + Binder.restoreCallingIdentity(identity); mInterface = null; + // TODO: unbind the service } } - public void limitReached(String limitName, String iface) {} + // INetworkManagementEventObserver.Stub + @Override + public void limitReached(String limit, String interfaze) { + } private void showNotification(VpnConfig config, String label, Bitmap icon) { NotificationManager nm = (NotificationManager) @@ -263,7 +265,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mContext.getString(R.string.vpn_text_long, config.session); config.startTime = SystemClock.elapsedRealtime(); - long identity = Binder.clearCallingIdentity(); Notification notification = new Notification.Builder(mContext) .setSmallIcon(R.drawable.vpn_connected) .setLargeIcon(icon) @@ -274,7 +275,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub { .setOngoing(true) .getNotification(); nm.notify(R.drawable.vpn_connected, notification); - Binder.restoreCallingIdentity(identity); } } @@ -283,9 +283,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mContext.getSystemService(Context.NOTIFICATION_SERVICE); if (nm != null) { - long identity = Binder.clearCallingIdentity(); nm.cancel(R.drawable.vpn_connected); - Binder.restoreCallingIdentity(identity); } } @@ -355,8 +353,8 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mOuterInterface = mConfig.interfaze; // Legacy VPN is not a real package, so we use it to carry the key. - mInfo.key = mConfig.packagz; - mConfig.packagz = VpnConfig.LEGACY_VPN; + mInfo.key = mConfig.user; + mConfig.user = VpnConfig.LEGACY_VPN; } public void check(String interfaze) { diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index b79e31fad9c0..0ce5499a1e0b 100755 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -22,6 +22,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.Cursor; import android.location.Criteria; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; @@ -32,6 +33,7 @@ import android.location.LocationManager; import android.location.LocationProvider; import android.net.ConnectivityManager; import android.net.NetworkInfo; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -45,6 +47,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.WorkSource; import android.provider.Settings; +import android.provider.Telephony.Carriers; import android.provider.Telephony.Sms.Intents; import android.telephony.SmsMessage; import android.telephony.TelephonyManager; @@ -489,8 +492,17 @@ public class GpsLocationProvider implements LocationProviderInterface { } if (info != null) { + boolean dataEnabled = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.MOBILE_DATA, 1) == 1; + boolean networkAvailable = info.isAvailable() && dataEnabled; + String defaultApn = getSelectedApn(); + if (defaultApn == null) { + defaultApn = "dummy-apn"; + } + native_update_network_state(info.isConnected(), info.getType(), - info.isRoaming(), info.getExtraInfo()); + info.isRoaming(), networkAvailable, + info.getExtraInfo(), defaultApn); } if (info != null && info.getType() == ConnectivityManager.TYPE_MOBILE_SUPL @@ -1597,6 +1609,25 @@ public class GpsLocationProvider implements LocationProviderInterface { } } + private String getSelectedApn() { + Uri uri = Uri.parse("content://telephony/carriers/preferapn"); + String apn = null; + + Cursor cursor = mContext.getContentResolver().query(uri, new String[] {"apn"}, + null, null, Carriers.DEFAULT_SORT_ORDER); + + if (null != cursor) { + try { + if (cursor.moveToFirst()) { + apn = cursor.getString(0); + } + } finally { + cursor.close(); + } + } + return apn; + } + // for GPS SV statistics private static final int MAX_SVS = 32; private static final int EPHEMERIS_MASK = 0; @@ -1655,5 +1686,5 @@ public class GpsLocationProvider implements LocationProviderInterface { private native void native_agps_set_id(int type, String setid); private native void native_update_network_state(boolean connected, int type, - boolean roaming, String extraInfo); + boolean roaming, boolean available, String extraInfo, String defaultAPN); } diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 24188cab76b6..deca7a91cfa4 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.RemoteException; 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } @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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { */ @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; - - long uidEnd = System.currentTimeMillis(); - long uidStart = uidEnd - mSettings.getUidMaxHistory(); - long uidRx = 500 * MB_IN_BYTES; - long uidTx = 100 * MB_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; + + 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { 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 @@ public class NetworkStatsService extends INetworkStatsService.Stub { * 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/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp index c29be3a1591c..c823da56708c 100755 --- a/services/jni/com_android_server_location_GpsLocationProvider.cpp +++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp @@ -543,7 +543,7 @@ static jstring android_location_GpsLocationProvider_get_internal_state(JNIEnv* e } static void android_location_GpsLocationProvider_update_network_state(JNIEnv* env, jobject obj, - jboolean connected, int type, jboolean roaming, jstring extraInfo) + jboolean connected, int type, jboolean roaming, jboolean available, jstring extraInfo, jstring apn) { if (sAGpsRilInterface && sAGpsRilInterface->update_network_state) { @@ -554,6 +554,14 @@ static void android_location_GpsLocationProvider_update_network_state(JNIEnv* en } else { sAGpsRilInterface->update_network_state(connected, type, roaming, NULL); } + + // update_network_availability callback was not included in original AGpsRilInterface + if (sAGpsRilInterface->size >= sizeof(AGpsRilInterface) + && sAGpsRilInterface->update_network_availability) { + const char *c_apn = env->GetStringUTFChars(apn, NULL); + sAGpsRilInterface->update_network_availability(available, c_apn); + env->ReleaseStringUTFChars(apn, c_apn); + } } } @@ -582,7 +590,7 @@ static JNINativeMethod sMethods[] = { {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response}, {"native_agps_ni_message", "([BI)V", (void *)android_location_GpsLocationProvider_agps_send_ni_message}, {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state}, - {"native_update_network_state", "(ZIZLjava/lang/String;)V", (void*)android_location_GpsLocationProvider_update_network_state }, + {"native_update_network_state", "(ZIZZLjava/lang/String;Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_update_network_state }, }; int register_android_server_location_GpsLocationProvider(JNIEnv* env) diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 91fef22fbd21..09f8ff39c97e 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.ACTION_UID_REMOVED; 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.EasyMock; 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 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { 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 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { } } + 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 cf69fd546552..8eb9cc305397 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.ConnectivityManager.TYPE_WIMAX; 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 @@ public class NetworkStatsServiceTest extends AndroidTestCase { 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 @@ public class NetworkStatsServiceTest extends AndroidTestCase { 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 @@ public class NetworkStatsServiceTest extends AndroidTestCase { 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/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java index 625b40d7f279..0928ec572540 100644 --- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java +++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java @@ -417,6 +417,22 @@ public final class BridgeResources extends Resources { } @Override + public boolean getBoolean(int id) throws NotFoundException { + Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag); + + if (value != null && value.getSecond().getValue() != null) { + String v = value.getSecond().getValue(); + return Boolean.parseBoolean(v); + } + + // id was not found or not resolved. Throw a NotFoundException. + throwException(id); + + // this is not used since the method above always throws + return false; + } + + @Override public String getResourceEntryName(int resid) throws NotFoundException { throw new UnsupportedOperationException(); } diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index de49eb4f7011..331d5c0ad8db 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -57,6 +57,7 @@ import android.net.NetworkInfo.DetailedState; import android.net.NetworkUtils; import android.net.wifi.WpsResult.Status; import android.net.wifi.p2p.WifiP2pManager; +import android.net.wifi.p2p.WifiP2pService; import android.net.wifi.StateChangeResult; import android.os.Binder; import android.os.IBinder; @@ -69,7 +70,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.WorkSource; -import android.server.WifiP2pService; import android.provider.Settings; import android.util.EventLog; import android.util.Log; diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java index be9dfcf81ef8..168c68b41df1 100644 --- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java +++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java @@ -44,9 +44,11 @@ import com.android.internal.util.StateMachine; 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 @@ public class WifiWatchdogStateMachine extends StateMachine { 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 @@ public class WifiWatchdogStateMachine extends StateMachine { 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 @@ public class WifiWatchdogStateMachine extends StateMachine { 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 @@ public class WifiWatchdogStateMachine extends StateMachine { 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 @@ public class WifiWatchdogStateMachine extends StateMachine { } 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..."); - return HANDLED; - } - long pingResponseTime = mDnsPinger.pingDns(mDnsPinger.getDns(), - mDnsPingTimeoutMs); + 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; + } + ids.remove(pingID); dnsCheckTries++; if (pingResponseTime >= 0) dnsCheckSuccesses++; @@ -730,11 +739,15 @@ public class WifiWatchdogStateMachine extends StateMachine { 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 @@ public class WifiWatchdogStateMachine extends StateMachine { } return true; } - - private void sendCheckStepMessage(long delay) { - sendMessageDelayed(obtainMessage(MESSAGE_CHECK_STEP, mNetEventCounter, 0), delay); - } - } class OnlineWatchState extends State { @@ -779,12 +787,15 @@ public class WifiWatchdogStateMachine extends StateMachine { 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 @@ public class WifiWatchdogStateMachine extends StateMachine { 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 @@ public class WifiWatchdogStateMachine extends StateMachine { return NOT_HANDLED; } + @Override + public void exit() { + mDnsPinger.cancelPings(); + } + /** * Times a dns check with an interval based on {@link #signalUnstable} */ diff --git a/wifi/java/android/net/wifi/p2p/WifiP2pService.java b/wifi/java/android/net/wifi/p2p/WifiP2pService.java index 4988f0b8573e..28afd44ed104 100644 --- a/wifi/java/android/net/wifi/p2p/WifiP2pService.java +++ b/wifi/java/android/net/wifi/p2p/WifiP2pService.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package android.server; +package android.net.wifi.p2p; import android.app.AlertDialog; import android.content.BroadcastReceiver; @@ -31,20 +31,13 @@ import android.net.wifi.WifiNative; import android.net.wifi.WifiStateMachine; import android.net.wifi.WpsConfiguration; import android.net.wifi.WpsConfiguration.Setup; -import android.net.wifi.p2p.IWifiP2pManager; -import android.net.wifi.p2p.WifiP2pConfig; -import android.net.wifi.p2p.WifiP2pDevice; import android.net.wifi.p2p.WifiP2pDevice.Status; -import android.net.wifi.p2p.WifiP2pDeviceList; -import android.net.wifi.p2p.WifiP2pGroup; -import android.net.wifi.p2p.WifiP2pManager; import android.os.Binder; import android.os.IBinder; import android.os.Handler; -import android.os.Messenger; import android.os.HandlerThread; -import android.os.IBinder; import android.os.Message; +import android.os.Messenger; import android.util.Slog; import android.view.LayoutInflater; import android.view.View; @@ -55,9 +48,9 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.Collection; +import com.android.internal.R; import com.android.internal.util.AsyncChannel; import com.android.internal.util.Protocol; -import com.android.internal.R; import com.android.internal.util.State; import com.android.internal.util.StateMachine; |