Merge "Initial upscaled progress spinners from Gingerbread in xhdpi"
diff --git a/api/current.txt b/api/current.txt
index f6bfd73..f21a4f3 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -975,6 +975,7 @@
     field public static final int textColorTertiary = 16843282; // 0x1010212
     field public static final int textColorTertiaryInverse = 16843283; // 0x1010213
     field public static final int textCursorDrawable = 16843618; // 0x1010362
+    field public static final int textDirection = 16843677; // 0x101039d
     field public static final int textEditNoPasteWindowLayout = 16843541; // 0x1010315
     field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314
     field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f
@@ -5896,6 +5897,7 @@
     field public long codeSize;
     field public long dataSize;
     field public long externalCacheSize;
+    field public long externalCodeSize;
     field public long externalDataSize;
     field public long externalMediaSize;
     field public long externalObbSize;
@@ -9397,6 +9399,7 @@
     method public void onUpdateExtractingViews(android.view.inputmethod.EditorInfo);
     method public void onUpdateExtractingVisibility(android.view.inputmethod.EditorInfo);
     method public void onUpdateSelection(int, int, int, int, int, int);
+    method public void onViewClicked(boolean);
     method public void onWindowHidden();
     method public void onWindowShown();
     method public void requestHideSelf(int);
@@ -9440,6 +9443,7 @@
     method public void updateCursor(android.graphics.Rect);
     method public void updateExtractedText(int, android.view.inputmethod.ExtractedText);
     method public void updateSelection(int, int, int, int, int, int);
+    method public void viewClicked(boolean);
   }
 
   public static final class InputMethodService.Insets {
@@ -11240,7 +11244,8 @@
     method public static long getUidUdpRxPackets(int);
     method public static long getUidUdpTxBytes(int);
     method public static long getUidUdpTxPackets(int);
-    method public static void setThreadStatsTag(java.lang.String);
+    method public static void setThreadStatsTag(int);
+    method public static deprecated void setThreadStatsTag(java.lang.String);
     method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
     method public static void untagSocket(java.net.Socket) throws java.net.SocketException;
     field public static final int UNSUPPORTED = -1; // 0xffffffff
@@ -11446,11 +11451,14 @@
   }
 
   public class SslError {
-    ctor public SslError(int, android.net.http.SslCertificate);
-    ctor public SslError(int, java.security.cert.X509Certificate);
+    ctor public deprecated SslError(int, android.net.http.SslCertificate);
+    ctor public deprecated SslError(int, java.security.cert.X509Certificate);
+    ctor public SslError(int, android.net.http.SslCertificate, java.lang.String);
+    ctor public SslError(int, java.security.cert.X509Certificate, java.lang.String);
     method public boolean addError(int);
     method public android.net.http.SslCertificate getCertificate();
     method public int getPrimaryError();
+    method public java.lang.String getUrl();
     method public boolean hasError(int);
     field public static final int SSL_EXPIRED = 1; // 0x1
     field public static final int SSL_IDMISMATCH = 2; // 0x2
@@ -21426,7 +21434,6 @@
     field public static final int AXIS_Y = 1; // 0x1
     field public static final int AXIS_Z = 11; // 0xb
     field public static final int BUTTON_BACK = 8; // 0x8
-    field public static final int BUTTON_ERASER = 32; // 0x20
     field public static final int BUTTON_FORWARD = 16; // 0x10
     field public static final int BUTTON_PRIMARY = 1; // 0x1
     field public static final int BUTTON_SECONDARY = 2; // 0x2
@@ -21875,6 +21882,7 @@
     method public boolean isHovered();
     method public boolean isInEditMode();
     method public boolean isInTouchMode();
+    method protected static boolean isLayoutDirectionRtl(java.util.Locale);
     method public boolean isLayoutRequested();
     method public boolean isLongClickable();
     method public boolean isOpaque();
@@ -21960,8 +21968,10 @@
     method public void requestLayout();
     method public boolean requestRectangleOnScreen(android.graphics.Rect);
     method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean);
+    method protected void resetResolvedTextDirection();
     method public static int resolveSize(int, int);
     method public static int resolveSizeAndState(int, int, int);
+    method protected void resolveTextDirection();
     method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>);
     method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>);
     method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long);
@@ -22011,6 +22021,7 @@
     method public void setOnDragListener(android.view.View.OnDragListener);
     method public void setOnFocusChangeListener(android.view.View.OnFocusChangeListener);
     method public void setOnGenericMotionListener(android.view.View.OnGenericMotionListener);
+    method public void setOnHoverListener(android.view.View.OnHoverListener);
     method public void setOnKeyListener(android.view.View.OnKeyListener);
     method public void setOnLongClickListener(android.view.View.OnLongClickListener);
     method public void setOnSystemUiVisibilityChangeListener(android.view.View.OnSystemUiVisibilityChangeListener);
@@ -22060,6 +22071,7 @@
     method public boolean willNotCacheDrawing();
     method public boolean willNotDraw();
     field public static android.util.Property ALPHA;
+    field protected static int DEFAULT_TEXT_DIRECTION;
     field public static final int DRAWING_CACHE_QUALITY_AUTO = 0; // 0x0
     field public static final int DRAWING_CACHE_QUALITY_HIGH = 1048576; // 0x100000
     field public static final int DRAWING_CACHE_QUALITY_LOW = 524288; // 0x80000
@@ -23507,6 +23519,7 @@
     method public void updateCursor(android.view.View, int, int, int, int);
     method public void updateExtractedText(android.view.View, int, android.view.inputmethod.ExtractedText);
     method public void updateSelection(android.view.View, int, int, int, int);
+    method public void viewClicked(android.view.View);
     field public static final int HIDE_IMPLICIT_ONLY = 1; // 0x1
     field public static final int HIDE_NOT_ALWAYS = 2; // 0x2
     field public static final int RESULT_HIDDEN = 3; // 0x3
@@ -23527,6 +23540,7 @@
     method public abstract void updateCursor(android.graphics.Rect);
     method public abstract void updateExtractedText(int, android.view.inputmethod.ExtractedText);
     method public abstract void updateSelection(int, int, int, int, int, int);
+    method public abstract void viewClicked(boolean);
   }
 
   public static abstract interface InputMethodSession.EventCallback {
diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
index 0acba8b..ccd668d 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -213,9 +213,10 @@
     // create the native surface
     sp<SurfaceControl> control = session()->createSurface(
             0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);
-    session()->openTransaction();
+
+    SurfaceComposerClient::openGlobalTransaction();
     control->setLayer(0x40000000);
-    session()->closeTransaction();
+    SurfaceComposerClient::closeGlobalTransaction();
 
     sp<Surface> s = control->getSurface();
 
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index d45ac192..26b9113 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -288,8 +288,9 @@
 }
 
 int get_size(const char *pkgname, const char *apkpath,
-             const char *fwdlock_apkpath,
-             int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize)
+             const char *fwdlock_apkpath, const char *asecpath,
+             int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize,
+             int64_t* _asecsize)
 {
     DIR *d;
     int dfd;
@@ -300,6 +301,7 @@
     int64_t codesize = 0;
     int64_t datasize = 0;
     int64_t cachesize = 0;
+    int64_t asecsize = 0;
 
         /* count the source apk as code -- but only if it's not
          * on the /system partition and its not on the sdcard.
@@ -324,6 +326,14 @@
         }
     }
 
+        /* compute asec size if it is given
+         */
+    if (asecpath != NULL && asecpath[0] != '!') {
+        if (stat(asecpath, &s) == 0) {
+            asecsize += stat_size(&s);
+        }
+    }
+
     if (create_pkg_path(path, pkgname, PKG_DIR_POSTFIX, 0)) {
         goto done;
     }
@@ -370,6 +380,7 @@
     *_codesize = codesize;
     *_datasize = datasize;
     *_cachesize = cachesize;
+    *_asecsize = asecsize;
     return 0;
 }
 
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index c062d36..feb6b92 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -77,16 +77,18 @@
     int64_t codesize = 0;
     int64_t datasize = 0;
     int64_t cachesize = 0;
+    int64_t asecsize = 0;
     int res = 0;
 
         /* pkgdir, apkpath */
-    res = get_size(arg[0], arg[1], arg[2], &codesize, &datasize, &cachesize);
+    res = get_size(arg[0], arg[1], arg[2], arg[3], &codesize, &datasize, &cachesize, &asecsize);
 
     /*
      * Each int64_t can take up 22 characters printed out. Make sure it
      * doesn't go over REPLY_MAX in the future.
      */
-    snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64, codesize, datasize, cachesize);
+    snprintf(reply, REPLY_MAX, "%" PRId64 " %" PRId64 " %" PRId64 " %" PRId64,
+            codesize, datasize, cachesize, asecsize);
     return res;
 }
 
@@ -137,7 +139,7 @@
     { "freecache",            1, do_free_cache },
     { "rmcache",              1, do_rm_cache },
     { "protect",              2, do_protect },
-    { "getsize",              3, do_get_size },
+    { "getsize",              4, do_get_size },
     { "rmuserdata",           2, do_rm_user_data },
     { "movefiles",            0, do_movefiles },
     { "linklib",              2, do_linklib },
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index e5f6739..c5872b8 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -143,7 +143,8 @@
 int rm_dex(const char *path);
 int protect(char *pkgname, gid_t gid);
 int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath,
-             int64_t *codesize, int64_t *datasize, int64_t *cachesize);
+             const char *asecpath, int64_t *codesize, int64_t *datasize, int64_t *cachesize,
+             int64_t *asecsize);
 int free_cache(int64_t free_size);
 int dexopt(const char *apk_path, uid_t uid, int is_public);
 int movefiles();
diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp
index 289665f..6fa66cf 100644
--- a/cmds/stagefright/sf2.cpp
+++ b/cmds/stagefright/sf2.cpp
@@ -29,6 +29,7 @@
 #include <media/stagefright/MediaExtractor.h>
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/NativeWindowWrapper.h>
 #include <media/stagefright/Utils.h>
 
 #include <surfaceflinger/ISurfaceComposer.h>
@@ -39,10 +40,12 @@
 using namespace android;
 
 struct Controller : public AHandler {
-    Controller(const char *uri, bool decodeAudio, const sp<Surface> &surface)
+    Controller(const char *uri, bool decodeAudio,
+               const sp<Surface> &surface, bool renderToSurface)
         : mURI(uri),
           mDecodeAudio(decodeAudio),
           mSurface(surface),
+          mRenderToSurface(renderToSurface),
           mCodec(new ACodec) {
         CHECK(!mDecodeAudio || mSurface == NULL);
     }
@@ -97,7 +100,8 @@
                 sp<AMessage> format = makeFormat(mSource->getFormat());
 
                 if (mSurface != NULL) {
-                    format->setObject("surface", mSurface);
+                    format->setObject(
+                            "native-window", new NativeWindowWrapper(mSurface));
                 }
 
                 mCodec->initiateSetup(format);
@@ -220,6 +224,7 @@
     AString mURI;
     bool mDecodeAudio;
     sp<Surface> mSurface;
+    bool mRenderToSurface;
     sp<ACodec> mCodec;
     sp<MediaSource> mSource;
 
@@ -451,7 +456,7 @@
                 inBuffer->release();
                 inBuffer = NULL;
 
-                // break;  // Don't coalesce
+                break;  // Don't coalesce
             }
 
             LOGV("coalesced %d input buffers", n);
@@ -479,6 +484,10 @@
         sp<AMessage> reply;
         CHECK(msg->findMessage("reply", &reply));
 
+        if (mRenderToSurface) {
+            reply->setInt32("render", 1);
+        }
+
         reply->post();
     }
 
@@ -491,7 +500,8 @@
     fprintf(stderr, "       -a(udio)\n");
 
     fprintf(stderr,
-            "       -s(surface) Allocate output buffers on a surface.\n");
+            "       -S(urface) Allocate output buffers on a surface.\n"
+            "       -R(ender)  Render surface-allocated buffers.\n");
 }
 
 int main(int argc, char **argv) {
@@ -499,18 +509,23 @@
 
     bool decodeAudio = false;
     bool useSurface = false;
+    bool renderToSurface = false;
 
     int res;
-    while ((res = getopt(argc, argv, "has")) >= 0) {
+    while ((res = getopt(argc, argv, "haSR")) >= 0) {
         switch (res) {
             case 'a':
                 decodeAudio = true;
                 break;
 
-            case 's':
+            case 'S':
                 useSurface = true;
                 break;
 
+            case 'R':
+                renderToSurface = true;
+                break;
+
             case '?':
             case 'h':
             default:
@@ -553,16 +568,18 @@
         CHECK(control != NULL);
         CHECK(control->isValid());
 
-        CHECK_EQ(composerClient->openTransaction(), (status_t)OK);
+        SurfaceComposerClient::openGlobalTransaction();
         CHECK_EQ(control->setLayer(30000), (status_t)OK);
         CHECK_EQ(control->show(), (status_t)OK);
-        CHECK_EQ(composerClient->closeTransaction(), (status_t)OK);
+        SurfaceComposerClient::closeGlobalTransaction();
 
         surface = control->getSurface();
         CHECK(surface != NULL);
     }
 
-    sp<Controller> controller = new Controller(argv[0], decodeAudio, surface);
+    sp<Controller> controller =
+        new Controller(argv[0], decodeAudio, surface, renderToSurface);
+
     looper->registerHandler(controller);
 
     controller->startAsync();
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 656f5fd..1a5b7f3 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -865,10 +865,10 @@
             CHECK(control != NULL);
             CHECK(control->isValid());
 
-            CHECK_EQ(composerClient->openTransaction(), (status_t)OK);
+            SurfaceComposerClient::openGlobalTransaction();
             CHECK_EQ(control->setLayer(30000), (status_t)OK);
             CHECK_EQ(control->show(), (status_t)OK);
-            CHECK_EQ(composerClient->closeTransaction(), (status_t)OK);
+            SurfaceComposerClient::closeGlobalTransaction();
 
             gSurface = control->getSurface();
             CHECK(gSurface != NULL);
diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp
index f780afb..ee91c29 100644
--- a/cmds/stagefright/stream.cpp
+++ b/cmds/stagefright/stream.cpp
@@ -20,6 +20,11 @@
 #include <media/mediaplayer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MPEG2TSWriter.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
 
 #include <binder/IServiceManager.h>
 #include <media/IMediaPlayerService.h>
@@ -31,7 +36,7 @@
 using namespace android;
 
 struct MyStreamSource : public BnStreamSource {
-    // Caller retains ownership of fd.
+    // Object assumes ownership of fd.
     MyStreamSource(int fd);
 
     virtual void setListener(const sp<IStreamListener> &listener);
@@ -64,6 +69,8 @@
 }
 
 MyStreamSource::~MyStreamSource() {
+    close(mFd);
+    mFd = -1;
 }
 
 void MyStreamSource::setListener(const sp<IStreamListener> &listener) {
@@ -99,6 +106,143 @@
         mListener->queueBuffer(index, n);
     }
 }
+////////////////////////////////////////////////////////////////////////////////
+
+struct MyConvertingStreamSource : public BnStreamSource {
+    MyConvertingStreamSource(const char *filename);
+
+    virtual void setListener(const sp<IStreamListener> &listener);
+    virtual void setBuffers(const Vector<sp<IMemory> > &buffers);
+
+    virtual void onBufferAvailable(size_t index);
+
+protected:
+    virtual ~MyConvertingStreamSource();
+
+private:
+    Mutex mLock;
+    Condition mCondition;
+
+    sp<IStreamListener> mListener;
+    Vector<sp<IMemory> > mBuffers;
+
+    sp<MPEG2TSWriter> mWriter;
+
+    ssize_t mCurrentBufferIndex;
+    size_t mCurrentBufferOffset;
+
+    List<size_t> mBufferQueue;
+
+    static ssize_t WriteDataWrapper(void *me, const void *data, size_t size);
+    ssize_t writeData(const void *data, size_t size);
+
+    DISALLOW_EVIL_CONSTRUCTORS(MyConvertingStreamSource);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+MyConvertingStreamSource::MyConvertingStreamSource(const char *filename)
+    : mCurrentBufferIndex(-1),
+      mCurrentBufferOffset(0) {
+    sp<DataSource> dataSource = DataSource::CreateFromURI(filename);
+    CHECK(dataSource != NULL);
+
+    sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+    CHECK(extractor != NULL);
+
+    mWriter = new MPEG2TSWriter(
+            this, &MyConvertingStreamSource::WriteDataWrapper);
+
+    for (size_t i = 0; i < extractor->countTracks(); ++i) {
+        const sp<MetaData> &meta = extractor->getTrackMetaData(i);
+
+        const char *mime;
+        CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+        if (strncasecmp("video/", mime, 6) && strncasecmp("audio/", mime, 6)) {
+            continue;
+        }
+
+        CHECK_EQ(mWriter->addSource(extractor->getTrack(i)), (status_t)OK);
+    }
+
+    CHECK_EQ(mWriter->start(), (status_t)OK);
+}
+
+MyConvertingStreamSource::~MyConvertingStreamSource() {
+}
+
+void MyConvertingStreamSource::setListener(
+        const sp<IStreamListener> &listener) {
+    mListener = listener;
+}
+
+void MyConvertingStreamSource::setBuffers(
+        const Vector<sp<IMemory> > &buffers) {
+    mBuffers = buffers;
+}
+
+ssize_t MyConvertingStreamSource::WriteDataWrapper(
+        void *me, const void *data, size_t size) {
+    return static_cast<MyConvertingStreamSource *>(me)->writeData(data, size);
+}
+
+ssize_t MyConvertingStreamSource::writeData(const void *data, size_t size) {
+    size_t totalWritten = 0;
+
+    while (size > 0) {
+        Mutex::Autolock autoLock(mLock);
+
+        if (mCurrentBufferIndex < 0) {
+            while (mBufferQueue.empty()) {
+                mCondition.wait(mLock);
+            }
+
+            mCurrentBufferIndex = *mBufferQueue.begin();
+            mCurrentBufferOffset = 0;
+
+            mBufferQueue.erase(mBufferQueue.begin());
+        }
+
+        sp<IMemory> mem = mBuffers.itemAt(mCurrentBufferIndex);
+
+        size_t copy = size;
+        if (copy + mCurrentBufferOffset > mem->size()) {
+            copy = mem->size() - mCurrentBufferOffset;
+        }
+
+        memcpy((uint8_t *)mem->pointer() + mCurrentBufferOffset, data, copy);
+        mCurrentBufferOffset += copy;
+
+        if (mCurrentBufferOffset == mem->size()) {
+            mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset);
+            mCurrentBufferIndex = -1;
+        }
+
+        data = (const uint8_t *)data + copy;
+        size -= copy;
+
+        totalWritten += copy;
+    }
+
+    return (ssize_t)totalWritten;
+}
+
+void MyConvertingStreamSource::onBufferAvailable(size_t index) {
+    Mutex::Autolock autoLock(mLock);
+
+    mBufferQueue.push_back(index);
+    mCondition.signal();
+
+    if (mWriter->reachedEOS()) {
+        if (mCurrentBufferIndex >= 0) {
+            mListener->queueBuffer(mCurrentBufferIndex, mCurrentBufferOffset);
+            mCurrentBufferIndex = -1;
+        }
+
+        mListener->issueCommand(IStreamListener::EOS, false /* synchronous */);
+    }
+}
 
 ////////////////////////////////////////////////////////////////////////////////
 
@@ -139,6 +283,8 @@
 int main(int argc, char **argv) {
     android::ProcessState::self()->startThreadPool();
 
+    DataSource::RegisterDefaultSniffers();
+
     if (argc != 2) {
         fprintf(stderr, "Usage: %s filename\n", argv[0]);
         return 1;
@@ -159,10 +305,10 @@
     CHECK(control != NULL);
     CHECK(control->isValid());
 
-    CHECK_EQ(composerClient->openTransaction(), (status_t)OK);
+    SurfaceComposerClient::openGlobalTransaction();
     CHECK_EQ(control->setLayer(30000), (status_t)OK);
     CHECK_EQ(control->show(), (status_t)OK);
-    CHECK_EQ(composerClient->closeTransaction(), (status_t)OK);
+    SurfaceComposerClient::closeGlobalTransaction();
 
     sp<Surface> surface = control->getSurface();
     CHECK(surface != NULL);
@@ -173,17 +319,28 @@
 
     CHECK(service.get() != NULL);
 
-    int fd = open(argv[1], O_RDONLY);
-
-    if (fd < 0) {
-        fprintf(stderr, "Failed to open file '%s'.", argv[1]);
-        return 1;
-    }
-
     sp<MyClient> client = new MyClient;
 
+    sp<IStreamSource> source;
+
+    size_t len = strlen(argv[1]);
+    if (len >= 3 && !strcasecmp(".ts", &argv[1][len - 3])) {
+        int fd = open(argv[1], O_RDONLY);
+
+        if (fd < 0) {
+            fprintf(stderr, "Failed to open file '%s'.", argv[1]);
+            return 1;
+        }
+
+        source = new MyStreamSource(fd);
+    } else {
+        printf("Converting file to transport stream for streaming...\n");
+
+        source = new MyConvertingStreamSource(argv[1]);
+    }
+
     sp<IMediaPlayer> player =
-        service->create(getpid(), client, new MyStreamSource(fd), 0);
+        service->create(getpid(), client, source, 0);
 
     if (player != NULL) {
         player->setVideoSurface(surface);
@@ -196,9 +353,6 @@
         fprintf(stderr, "failed to instantiate player.\n");
     }
 
-    close(fd);
-    fd = -1;
-
     composerClient->dispose();
 
     return 0;
diff --git a/core/java/android/content/pm/PackageStats.java b/core/java/android/content/pm/PackageStats.java
index 11068e5..1205da7 100755
--- a/core/java/android/content/pm/PackageStats.java
+++ b/core/java/android/content/pm/PackageStats.java
@@ -40,6 +40,12 @@
     public long cacheSize;
 
     /**
+     * Size of the secure container on external storage holding the
+     * application's code.
+     */
+    public long externalCodeSize;
+
+    /**
      * Size of the external data used by the application (e.g.,
      * <sdcard>/Android/data/<app>)
      */
@@ -80,6 +86,8 @@
         sb.append(dataSize);
         sb.append(",cacheSize=");
         sb.append(cacheSize);
+        sb.append(",externalCodeSize=");
+        sb.append(externalCodeSize);
         sb.append(",externalDataSize=");
         sb.append(externalDataSize);
         sb.append(",externalCacheSize=");
@@ -100,6 +108,7 @@
         codeSize = source.readLong();
         dataSize = source.readLong();
         cacheSize = source.readLong();
+        externalCodeSize = source.readLong();
         externalDataSize = source.readLong();
         externalCacheSize = source.readLong();
         externalMediaSize = source.readLong();
@@ -111,6 +120,7 @@
         codeSize = pStats.codeSize;
         dataSize = pStats.dataSize;
         cacheSize = pStats.cacheSize;
+        externalCodeSize = pStats.externalCodeSize;
         externalDataSize = pStats.externalDataSize;
         externalCacheSize = pStats.externalCacheSize;
         externalMediaSize = pStats.externalMediaSize;
@@ -126,6 +136,7 @@
         dest.writeLong(codeSize);
         dest.writeLong(dataSize);
         dest.writeLong(cacheSize);
+        dest.writeLong(externalCodeSize);
         dest.writeLong(externalDataSize);
         dest.writeLong(externalCacheSize);
         dest.writeLong(externalMediaSize);
diff --git a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
index df8cf9a..e10f218 100644
--- a/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
+++ b/core/java/android/inputmethodservice/IInputMethodSessionWrapper.java
@@ -47,6 +47,7 @@
     private static final int DO_APP_PRIVATE_COMMAND = 100;
     private static final int DO_TOGGLE_SOFT_INPUT = 105;
     private static final int DO_FINISH_SESSION = 110;
+    private static final int DO_VIEW_CLICKED = 115;
 
     HandlerCaller mCaller;
     InputMethodSession mInputMethodSession;
@@ -133,6 +134,10 @@
                 mInputMethodSession = null;
                 return;
             }
+            case DO_VIEW_CLICKED: {
+                mInputMethodSession.viewClicked(msg.arg1 == 1);
+                return;
+            }
         }
         Log.w(TAG, "Unhandled message code: " + msg.what);
     }
@@ -167,7 +172,11 @@
                 oldSelStart, oldSelEnd, newSelStart, newSelEnd,
                 candidatesStart, candidatesEnd));
     }
-    
+
+    public void viewClicked(boolean focusChanged) {
+        mCaller.executeOrSendMessage(mCaller.obtainMessageI(DO_VIEW_CLICKED, focusChanged ? 1 : 0));
+    }
+
     public void updateCursor(Rect newCursor) {
         mCaller.executeOrSendMessage(mCaller.obtainMessageO(DO_UPDATE_CURSOR,
                 newCursor));
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index feb246e..9481a88 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -488,7 +488,15 @@
             InputMethodService.this.onUpdateSelection(oldSelStart, oldSelEnd,
                     newSelStart, newSelEnd, candidatesStart, candidatesEnd);
         }
-        
+
+        @Override
+        public void viewClicked(boolean focusChanged) {
+            if (!isEnabled()) {
+                return;
+            }
+            InputMethodService.this.onViewClicked(focusChanged);
+        }
+
         /**
          * Call {@link InputMethodService#onUpdateCursor
          * InputMethodService.onUpdateCursor()}.
@@ -1609,6 +1617,16 @@
     }
 
     /**
+     * Called when the user tapped or clicked a text view.
+     * IMEs can't rely on this method being called because this was not part of the original IME
+     * protocol, so applications with custom text editing written before this method appeared will
+     * not call to inform the IME of this interaction.
+     * @param focusChanged true if the user changed the focused view by this click.
+     */
+    public void onViewClicked(boolean focusChanged) {
+    }
+
+    /**
      * Called when the application has reported a new location of its text
      * cursor.  This is only called if explicitly requested by the input method.
      * The default implementation does nothing.
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 91af16d..593b2b7 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -16,14 +16,21 @@
 
 package android.net;
 
+import static android.content.pm.PackageManager.GET_SIGNATURES;
 import static android.text.format.Time.MONTH_DAY;
 
 import android.content.Context;
 import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.Signature;
 import android.os.RemoteException;
 import android.text.format.Time;
 
+import com.google.android.collect.Sets;
+
 import java.io.PrintWriter;
+import java.util.HashSet;
 
 /**
  * Manager for creating and modifying network policy rules.
@@ -42,6 +49,8 @@
     /** Reject traffic on metered networks. */
     public static final int RULE_REJECT_METERED = 0x1;
 
+    private static final boolean ALLOW_PLATFORM_APP_POLICY = true;
+
     /**
      * {@link Intent} action launched when user selects {@link NetworkPolicy}
      * warning notification.
@@ -210,8 +219,37 @@
      * usually to protect critical system services.
      */
     public static boolean isUidValidForPolicy(Context context, int uid) {
-        return (uid >= android.os.Process.FIRST_APPLICATION_UID
-                && uid <= android.os.Process.LAST_APPLICATION_UID);
+        // first, quick-reject non-applications
+        if (uid < android.os.Process.FIRST_APPLICATION_UID
+                || uid > android.os.Process.LAST_APPLICATION_UID) {
+            return false;
+        }
+
+        if (!ALLOW_PLATFORM_APP_POLICY) {
+            final PackageManager pm = context.getPackageManager();
+            final HashSet<Signature> systemSignature;
+            try {
+                systemSignature = Sets.newHashSet(
+                        pm.getPackageInfo("android", GET_SIGNATURES).signatures);
+            } catch (NameNotFoundException e) {
+                throw new RuntimeException("problem finding system signature", e);
+            }
+
+            try {
+                // reject apps signed with platform cert
+                for (String packageName : pm.getPackagesForUid(uid)) {
+                    final HashSet<Signature> packageSignature = Sets.newHashSet(
+                            pm.getPackageInfo(packageName, GET_SIGNATURES).signatures);
+                    if (packageSignature.containsAll(systemSignature)) {
+                        return false;
+                    }
+                }
+            } catch (NameNotFoundException e) {
+            }
+        }
+
+        // nothing found above; we can apply policy to UID
+        return true;
     }
 
     /** {@hide} */
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index cb47193..2b59dba 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -16,14 +16,18 @@
 
 package android.net;
 
+import android.app.DownloadManager;
+import android.app.backup.BackupManager;
 import android.content.Context;
+import android.media.MediaPlayer;
 import android.os.IBinder;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 
-import dalvik.system.BlockGuard;
+import com.android.server.NetworkManagementSocketTagger;
 
+import dalvik.system.SocketTagger;
 import java.net.Socket;
 import java.net.SocketException;
 
@@ -50,6 +54,27 @@
     public static final int UID_REMOVED = -4;
 
     /**
+     * Default tag value for {@link DownloadManager} traffic.
+     *
+     * @hide
+     */
+    public static final int TAG_SYSTEM_DOWNLOAD = 0xFFFF0001;
+
+    /**
+     * Default tag value for {@link MediaPlayer} traffic.
+     *
+     * @hide
+     */
+    public static final int TAG_SYSTEM_MEDIA = 0xFFFF0002;
+
+    /**
+     * Default tag value for {@link BackupManager} traffic.
+     *
+     * @hide
+     */
+    public static final int TAG_SYSTEM_BACKUP = 0xFFFF0003;
+
+    /**
      * Snapshot of {@link NetworkStats} when the currently active profiling
      * session started, or {@code null} if no session active.
      *
@@ -67,12 +92,20 @@
      * Changes only take effect during subsequent calls to
      * {@link #tagSocket(Socket)}.
      */
+    public static void setThreadStatsTag(int tag) {
+        NetworkManagementSocketTagger.setThreadSocketStatsTag(tag);
+    }
+
+    /**
+     * @deprecated unsupported, will eventually be removed
+     */
+    @Deprecated
     public static void setThreadStatsTag(String tag) {
-        BlockGuard.setThreadSocketStatsTag(tag);
+        setThreadStatsTag(tag.hashCode());
     }
 
     public static void clearThreadStatsTag() {
-        BlockGuard.setThreadSocketStatsTag(null);
+        NetworkManagementSocketTagger.setThreadSocketStatsTag(-1);
     }
 
     /**
@@ -89,12 +122,12 @@
      * {@hide}
      */
     public static void setThreadStatsUid(int uid) {
-        BlockGuard.setThreadSocketStatsUid(uid);
+        NetworkManagementSocketTagger.setThreadSocketStatsUid(uid);
     }
 
     /** {@hide} */
     public static void clearThreadStatsUid() {
-        BlockGuard.setThreadSocketStatsUid(-1);
+        NetworkManagementSocketTagger.setThreadSocketStatsUid(-1);
     }
 
     /**
@@ -103,18 +136,18 @@
      * parameters. When finished, call {@link #untagSocket(Socket)} to remove
      * statistics parameters.
      *
-     * @see #setThreadStatsTag(String)
+     * @see #setThreadStatsTag(int)
      * @see #setThreadStatsUid(int)
      */
     public static void tagSocket(Socket socket) throws SocketException {
-        BlockGuard.tagSocketFd(socket.getFileDescriptor$());
+        SocketTagger.get().tag(socket);
     }
 
     /**
      * Remove any statistics parameters from the given {@link Socket}.
      */
     public static void untagSocket(Socket socket) throws SocketException {
-        BlockGuard.untagSocketFd(socket.getFileDescriptor$());
+        SocketTagger.get().untag(socket);
     }
 
     /**
diff --git a/core/java/android/net/http/SslError.java b/core/java/android/net/http/SslError.java
index e1b9deb..1e1cb49 100644
--- a/core/java/android/net/http/SslError.java
+++ b/core/java/android/net/http/SslError.java
@@ -59,36 +59,97 @@
     /**
      * The SSL certificate associated with the error set
      */
-    SslCertificate mCertificate;
+    final SslCertificate mCertificate;
+
+    /**
+     * The URL associated with the error set.
+     */
+    final String mUrl;
 
     /**
      * Creates a new SSL error set object
      * @param error The SSL error
      * @param certificate The associated SSL certificate
+     * @deprecated Use {@link #SslError(int, SslCertificate, String)}
      */
+    @Deprecated
     public SslError(int error, SslCertificate certificate) {
         addError(error);
+        if (certificate == null) {
+            throw new NullPointerException("certificate is null.");
+        }
         mCertificate = certificate;
+        mUrl = "";
     }
 
     /**
      * Creates a new SSL error set object
      * @param error The SSL error
      * @param certificate The associated SSL certificate
+     * @deprecated Use {@link #SslError(int, X509Certificate, String)}
      */
+    @Deprecated
     public SslError(int error, X509Certificate certificate) {
         addError(error);
+        if (certificate == null) {
+            throw new NullPointerException("certificate is null.");
+        }
         mCertificate = new SslCertificate(certificate);
+        mUrl = "";
     }
 
     /**
-     * @return The SSL certificate associated with the error set
+     * Creates a new SSL error set object
+     * @param error The SSL error
+     * @param certificate The associated SSL certificate
+     * @param url The associated URL.
+     */
+    public SslError(int error, SslCertificate certificate, String url) {
+        addError(error);
+        if (certificate == null) {
+            throw new NullPointerException("certificate is null.");
+        }
+        mCertificate = certificate;
+        if (url == null) {
+            throw new NullPointerException("url is null.");
+        }
+        mUrl = url;
+    }
+
+    /**
+     * Creates a new SSL error set object
+     * @param error The SSL error
+     * @param certificate The associated SSL certificate
+     * @param url The associated URL.
+     */
+    public SslError(int error, X509Certificate certificate, String url) {
+        addError(error);
+        if (certificate == null) {
+            throw new NullPointerException("certificate is null.");
+        }
+        mCertificate = new SslCertificate(certificate);
+        if (url == null) {
+            throw new NullPointerException("url is null.");
+        }
+        mUrl = url;
+    }
+
+    /**
+     * @return The SSL certificate associated with the error set, non-null.
      */
     public SslCertificate getCertificate() {
         return mCertificate;
     }
 
     /**
+     * @return The URL associated with the error set, non-null.
+     * "" if one of the deprecated constructors is used.
+     */
+    public String getUrl() {
+        return mUrl;
+    }
+
+    /**
      * Adds the SSL error to the error set
      * @param error The SSL error to add
      * @return True iff the error being added is a known SSL error
@@ -137,6 +198,7 @@
      */
     public String toString() {
         return "primary error: " + getPrimaryError() +
-            " certificate: " + getCertificate();
+            " certificate: " + getCertificate() +
+            "  on URL: " + getUrl();
     }
 }
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index 1b09242..c9b6121 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -210,6 +210,21 @@
     NetworkStats getNetworkStatsUidDetail(int uid);
 
     /**
+     * Set quota for an interface.
+     */
+    void setInterfaceQuota(String iface, long quota);
+
+    /**
+     * Remove quota for an interface.
+     */
+    void removeInterfaceQuota(String iface);
+
+    /**
+     * Control network activity of a UID over interfaces with a quota limit.
+     */
+    void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces);
+
+    /**
      * Configures bandwidth throttling on an interface.
      */
     void setInterfaceThrottle(String iface, int rxKbps, int txKbps);
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index c2dc8ae..9302060 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -655,6 +655,26 @@
                 }
                 return _result;
             }
+
+            /*
+             * Returns the filesystem path of a mounted secure container.
+             */
+            public String getSecureContainerFilesystemPath(String id) throws RemoteException {
+                Parcel _data = Parcel.obtain();
+                Parcel _reply = Parcel.obtain();
+                String _result;
+                try {
+                    _data.writeInterfaceToken(DESCRIPTOR);
+                    _data.writeString(id);
+                    mRemote.transact(Stub.TRANSACTION_getSecureContainerFilesystemPath, _data, _reply, 0);
+                    _reply.readException();
+                    _result = _reply.readString();
+                } finally {
+                    _reply.recycle();
+                    _data.recycle();
+                }
+                return _result;
+            }
         }
 
         private static final String DESCRIPTOR = "IMountService";
@@ -719,6 +739,8 @@
 
         static final int TRANSACTION_getVolumeList = IBinder.FIRST_CALL_TRANSACTION + 29;
 
+        static final int TRANSACTION_getSecureContainerFilesystemPath = IBinder.FIRST_CALL_TRANSACTION + 30;
+
         /**
          * Cast an IBinder object into an IMountService interface, generating a
          * proxy if needed.
@@ -1031,6 +1053,15 @@
                     reply.writeParcelableArray(result, 0);
                     return true;
                 }
+                case TRANSACTION_getSecureContainerFilesystemPath: {
+                    data.enforceInterface(DESCRIPTOR);
+                    String id;
+                    id = data.readString();
+                    String path = getSecureContainerFilesystemPath(id);
+                    reply.writeNoException();
+                    reply.writeString(path);
+                    return true;
+                }
             }
             return super.onTransact(code, data, reply, flags);
         }
@@ -1210,4 +1241,6 @@
      * Returns list of all mountable volumes.
      */
     public Parcelable[] getVolumeList() throws RemoteException;
+
+    public String getSecureContainerFilesystemPath(String id) throws RemoteException;
 }
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 02faf49..39c6f57 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -24,6 +24,8 @@
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.DataUsageFeedback;
 import android.text.TextUtils;
 
 /**
@@ -55,6 +57,29 @@
                 Uri.parse("content://call_log/calls/filter");
 
         /**
+         * An optional URI parameter which instructs the provider to allow the operation to be
+         * applied to voicemail records as well.
+         * <p>
+         * TYPE: Boolean
+         * <p>
+         * Using this parameter with a value of {@code true} will result in a security error if the
+         * calling package does not have appropriate permissions to access voicemails.
+         *
+         * @hide
+         */
+        public static final String ALLOW_VOICEMAILS_PARAM_KEY = "allow_voicemails";
+
+        /**
+         * Content uri with {@link #ALLOW_VOICEMAILS_PARAM_KEY} set. This can directly be used to
+         * access call log entries that includes voicemail records.
+         *
+         * @hide
+         */
+        public static final Uri CONTENT_URI_WITH_VOICEMAIL = CONTENT_URI.buildUpon()
+                .appendQueryParameter(ALLOW_VOICEMAILS_PARAM_KEY, "true")
+                .build();
+
+        /**
          * The default sort order for this table
          */
         public static final String DEFAULT_SORT_ORDER = "date DESC";
@@ -204,7 +229,44 @@
             }
 
             if ((ci != null) && (ci.person_id > 0)) {
-                ContactsContract.Contacts.markAsContacted(resolver, ci.person_id);
+                // Update usage information for the number associated with the contact ID.
+                // We need to use both the number and the ID for obtaining a data ID since other
+                // contacts may have the same number.
+
+                final Cursor cursor;
+
+                // We should prefer normalized one (probably coming from
+                // Phone.NORMALIZED_NUMBER column) first. If it isn't available try others.
+                if (ci.normalizedNumber != null) {
+                    final String normalizedPhoneNumber = ci.normalizedNumber;
+                    cursor = resolver.query(Phone.CONTENT_URI,
+                            new String[] { Phone._ID },
+                            Phone.CONTACT_ID + " =? AND " + Phone.NORMALIZED_NUMBER + " =?",
+                            new String[] { String.valueOf(ci.person_id), normalizedPhoneNumber},
+                            null);
+                } else {
+                    final String phoneNumber = ci.phoneNumber != null ? ci.phoneNumber : number;
+                    cursor = resolver.query(Phone.CONTENT_URI,
+                            new String[] { Phone._ID },
+                            Phone.CONTACT_ID + " =? AND " + Phone.NUMBER + " =?",
+                            new String[] { String.valueOf(ci.person_id), phoneNumber},
+                            null);
+                }
+
+                if (cursor != null) {
+                    try {
+                        if (cursor.getCount() > 0 && cursor.moveToFirst()) {
+                            final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon()
+                                    .appendPath(cursor.getString(0))
+                                    .appendQueryParameter(DataUsageFeedback.USAGE_TYPE,
+                                                DataUsageFeedback.USAGE_TYPE_CALL)
+                                    .build();
+                            resolver.update(feedbackUri, new ContentValues(), null, null);
+                        }
+                    } finally {
+                        cursor.close();
+                    }
+                }
             }
 
             Uri result = resolver.insert(CONTENT_URI, values);
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index cb96bfd2..ad71061 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -174,6 +174,15 @@
      */
     public static final String PRIMARY_ACCOUNT_TYPE = "type_for_primary_account";
 
+    /**
+     * A boolean parameter for {@link Contacts#CONTENT_STREQUENT_URI} and
+     * {@link Contacts#CONTENT_STREQUENT_FILTER_URI}, which requires the ContactsProvider to
+     * return only phone-related results. For example, frequently contacted person list should
+     * include persons contacted via phone (not email, sms, etc.)
+     *
+     * @hide
+     */
+    public static final String STREQUENT_PHONE_ONLY = "strequent_phone_only";
 
     /**
      * @hide
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
old mode 100755
new mode 100644
diff --git a/core/java/android/view/InputEventConsistencyVerifier.java b/core/java/android/view/InputEventConsistencyVerifier.java
index 49f3bbe..9b081b2 100644
--- a/core/java/android/view/InputEventConsistencyVerifier.java
+++ b/core/java/android/view/InputEventConsistencyVerifier.java
@@ -239,7 +239,7 @@
                     break;
             }
         } finally {
-            finishEvent(false);
+            finishEvent();
         }
     }
 
@@ -302,7 +302,7 @@
                 problem("Source was not SOURCE_CLASS_TRACKBALL.");
             }
         } finally {
-            finishEvent(false);
+            finishEvent();
         }
     }
 
@@ -328,7 +328,9 @@
             mTouchEventStreamUnhandled = false;
             mTouchEventStreamPointers = 0;
         }
-        final boolean wasTainted = mTouchEventStreamIsTainted;
+        if (mTouchEventStreamIsTainted) {
+            event.setTainted(true);
+        }
 
         try {
             ensureMetaStateIsNormalized(event.getMetaState());
@@ -441,7 +443,7 @@
                 problem("Source was not SOURCE_CLASS_POINTER.");
             }
         } finally {
-            finishEvent(wasTainted);
+            finishEvent();
         }
     }
 
@@ -499,7 +501,7 @@
                 }
             }
         } finally {
-            finishEvent(false);
+            finishEvent();
         }
     }
 
@@ -591,9 +593,9 @@
         return true;
     }
 
-    private void finishEvent(boolean tainted) {
+    private void finishEvent() {
         if (mViolationMessage != null && mViolationMessage.length() != 0) {
-            if (!tainted) {
+            if (!mCurrentEvent.isTainted()) {
                 // Write a log message only if the event was not already tainted.
                 mViolationMessage.append("\n  in ").append(mCaller);
                 mViolationMessage.append("\n  ");
@@ -614,17 +616,14 @@
                 }
 
                 Log.d(mLogTag, mViolationMessage.toString());
-                tainted = true;
+
+                // Taint the event so that we do not generate additional violations from it
+                // further downstream.
+                mCurrentEvent.setTainted(true);
             }
             mViolationMessage.setLength(0);
         }
 
-        if (tainted) {
-            // Taint the event so that we do not generate additional violations from it
-            // further downstream.
-            mCurrentEvent.setTainted(true);
-        }
-
         if (RECENT_EVENTS_TO_LOG != 0) {
             if (mRecentEvents == null) {
                 mRecentEvents = new InputEvent[RECENT_EVENTS_TO_LOG];
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
index 3436cd1..88f59d4 100644
--- a/core/java/android/view/MotionEvent.java
+++ b/core/java/android/view/MotionEvent.java
@@ -1130,14 +1130,14 @@
     public static final int BUTTON_PRIMARY = 1 << 0;
 
     /**
-     * Button constant: Secondary button (right mouse button, stylus barrel).
+     * Button constant: Secondary button (right mouse button, stylus first button).
      *
      * @see #getButtonState
      */
     public static final int BUTTON_SECONDARY = 1 << 1;
 
     /**
-     * Button constant: Tertiary button (middle mouse button).
+     * Button constant: Tertiary button (middle mouse button, stylus second button).
      *
      * @see #getButtonState
      */
@@ -1165,13 +1165,6 @@
      */
     public static final int BUTTON_FORWARD = 1 << 4;
 
-    /**
-     * Button constant: Eraser button pressed (stylus end).
-     *
-     * @see #getButtonState
-     */
-    public static final int BUTTON_ERASER = 1 << 5;
-
     // NOTE: If you add a new axis here you must also add it to:
     //  native/include/android/input.h
 
@@ -1183,7 +1176,7 @@
         "BUTTON_TERTIARY",
         "BUTTON_BACK",
         "BUTTON_FORWARD",
-        "BUTTON_ERASER",
+        "0x00000020",
         "0x00000040",
         "0x00000080",
         "0x00000100",
@@ -2176,7 +2169,6 @@
      * @see #BUTTON_TERTIARY
      * @see #BUTTON_FORWARD
      * @see #BUTTON_BACK
-     * @see #BUTTON_ERASER
      */
     public final int getButtonState() {
         return nativeGetButtonState(mNativePtr);
@@ -2893,7 +2885,7 @@
                     toolTypeToString(getToolType(i)));
         }
 
-        msg.append(", buttonState=").append(KeyEvent.metaStateToString(getButtonState()));
+        msg.append(", buttonState=").append(MotionEvent.buttonStateToString(getButtonState()));
         msg.append(", metaState=").append(KeyEvent.metaStateToString(getMetaState()));
         msg.append(", flags=0x").append(Integer.toHexString(getFlags()));
         msg.append(", edgeFlags=0x").append(Integer.toHexString(getEdgeFlags()));
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 5743134..bf7f359 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2493,6 +2493,84 @@
     private boolean mSendingHoverAccessibilityEvents;
 
     /**
+     * Undefined text direction (used by resolution algorithm).
+     * @hide
+     */
+    public static final int TEXT_DIRECTION_UNDEFINED = -1;
+
+    /**
+     * Text direction is inherited thru {@link ViewGroup}
+     * @hide
+     */
+    public static final int TEXT_DIRECTION_INHERIT = 0;
+
+    /**
+     * Text direction is using "first strong algorithm". The first strong directional character
+     * determines the paragraph direction. If there is no strong directional character, the
+     * paragraph direction is the view’s resolved ayout direction.
+     *
+     * @hide
+     */
+    public static final int TEXT_DIRECTION_FIRST_STRONG = 1;
+
+    /**
+     * Text direction is using "any-RTL" algorithm. The paragraph direction is RTL if it contains
+     * any strong RTL character, otherwise it is LTR if it contains any strong LTR characters.
+     * If there are neither, the paragraph direction is the view’s resolved layout direction.
+     *
+     * @hide
+     */
+    public static final int TEXT_DIRECTION_ANY_RTL = 2;
+
+    /**
+     * Text direction is forced to LTR.
+     *
+     * @hide
+     */
+    public static final int TEXT_DIRECTION_LTR = 3;
+
+    /**
+     * Text direction is forced to RTL.
+     *
+     * @hide
+     */
+    public static final int TEXT_DIRECTION_RTL = 4;
+
+    /**
+     * Default text direction is inherited
+     */
+    protected static int DEFAULT_TEXT_DIRECTION = TEXT_DIRECTION_INHERIT;
+
+    /**
+     * The text direction that has been defined by {@link #setTextDirection(int)}.
+     *
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty(category = "text", mapping = {
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG, to = "FIRST_STRONG"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL")
+    })
+    protected int mTextDirection = DEFAULT_TEXT_DIRECTION;
+
+    /**
+     * The resolved text direction. If resolution has not yet been done or has been reset, it will
+     * be equal to {@link #TEXT_DIRECTION_UNDEFINED}. Otherwise it will be either {@link #TEXT_DIRECTION_LTR}
+     * or {@link #TEXT_DIRECTION_RTL}.
+     *
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty(category = "text", mapping = {
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_UNDEFINED, to = "UNDEFINED"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
+            @ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL")
+    })
+    protected int mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
+
+    /**
      * Consistency verifier for debugging purposes.
      * @hide
      */
@@ -2865,6 +2943,9 @@
                 case R.styleable.View_layerType:
                     setLayerType(a.getInt(attr, LAYER_TYPE_NONE), null);
                     break;
+                case R.styleable.View_textDirection:
+                    mTextDirection = a.getInt(attr, DEFAULT_TEXT_DIRECTION);
+                    break;
             }
         }
 
@@ -3390,6 +3471,14 @@
     }
 
     /**
+     * Register a callback to be invoked when a hover event is sent to this view.
+     * @param l the hover listener to attach to this view
+     */
+    public void setOnHoverListener(OnHoverListener l) {
+        mOnHoverListener = l;
+    }
+
+    /**
      * Register a drag event listener callback object for this View. The parameter is
      * an implementation of {@link android.view.View.OnDragListener}. To send a drag event to a
      * View, the system calls the
@@ -8943,6 +9032,8 @@
         resetLayoutDirectionResolution();
         resolveLayoutDirectionIfNeeded();
         resolvePadding();
+        resetResolvedTextDirection();
+        resolveTextDirection();
         if (isFocused()) {
             InputMethodManager imm = InputMethodManager.peekInstance();
             imm.focusIn(this);
@@ -9043,7 +9134,7 @@
      * @param locale Locale to check
      * @return true if a Locale is corresponding to a RTL script.
      */
-    private static boolean isLayoutDirectionRtl(Locale locale) {
+    protected static boolean isLayoutDirectionRtl(Locale locale) {
         return (LocaleUtil.TEXT_LAYOUT_DIRECTION_RTL_DO_NOT_USE ==
                 LocaleUtil.getLayoutDirectionFromLocale(locale));
     }
@@ -12890,6 +12981,89 @@
         return getVerticalScrollFactor();
     }
 
+    /**
+     * Return the value specifying the text direction or policy that was set with
+     * {@link #setTextDirection(int)}.
+     *
+     * @return the defined text direction. It can be one of:
+     *
+     * {@link #TEXT_DIRECTION_INHERIT},
+     * {@link #TEXT_DIRECTION_FIRST_STRONG}
+     * {@link #TEXT_DIRECTION_ANY_RTL},
+     * {@link #TEXT_DIRECTION_LTR},
+     * {@link #TEXT_DIRECTION_RTL},
+     *
+     * @hide
+     */
+    public int getTextDirection() {
+        return mTextDirection;
+    }
+
+    /**
+     * Set the text direction.
+     *
+     * @param textDirection the direction to set. Should be one of:
+     *
+     * {@link #TEXT_DIRECTION_INHERIT},
+     * {@link #TEXT_DIRECTION_FIRST_STRONG}
+     * {@link #TEXT_DIRECTION_ANY_RTL},
+     * {@link #TEXT_DIRECTION_LTR},
+     * {@link #TEXT_DIRECTION_RTL},
+     *
+     * @hide
+     */
+    public void setTextDirection(int textDirection) {
+        if (textDirection != mTextDirection) {
+            mTextDirection = textDirection;
+            resetResolvedTextDirection();
+            requestLayout();
+        }
+    }
+
+    /**
+     * Return the resolved text direction.
+     *
+     * @return the resolved text direction. Return one of:
+     *
+     * {@link #TEXT_DIRECTION_LTR},
+     * {@link #TEXT_DIRECTION_RTL},
+     *
+     * @hide
+     */
+    public int getResolvedTextDirection() {
+        if (!isResolvedTextDirection()) {
+            resolveTextDirection();
+        }
+        return mResolvedTextDirection;
+    }
+
+    /**
+     * Resolve the text direction. Classes that extend View and want to do a specific text direction
+     * resolution will need to implement this method and set the mResolvedTextDirection to
+     * either TEXT_DIRECTION_LTR if direction is LTR or TEXT_DIRECTION_RTL if
+     * direction is RTL.
+     */
+    protected void resolveTextDirection() {
+    }
+
+    /**
+     * Return if the text direction has been resolved or not.
+     *
+     * @return true, if resolved and false if not resolved
+     *
+     * @hide
+     */
+    public boolean isResolvedTextDirection() {
+        return (mResolvedTextDirection != TEXT_DIRECTION_UNDEFINED);
+    }
+
+    /**
+     * Reset resolved text direction. Will be resolved during a call to getResolvedLayoutDirection().
+     */
+    protected void resetResolvedTextDirection() {
+        mResolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
+    }
+
     //
     // Properties
     //
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 6405398..2a90dde 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -46,6 +46,7 @@
 
 import java.util.ArrayList;
 import java.util.HashSet;
+import java.util.Locale;
 
 /**
  * <p>
@@ -5016,6 +5017,51 @@
     }
 
     /**
+     * This method will be called during text direction resolution (text direction resolution
+     * inheritance)
+     */
+    @Override
+    protected void resolveTextDirection() {
+        int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
+        switch(mTextDirection) {
+            default:
+            case TEXT_DIRECTION_INHERIT:
+                // Try to the text direction from the parent layout
+                if (mParent != null && mParent instanceof ViewGroup) {
+                    resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
+                } else {
+                    // We reached the top of the View hierarchy, so get the direction from
+                    // the Locale
+                    resolvedTextDirection = isLayoutDirectionRtl(Locale.getDefault()) ?
+                            TEXT_DIRECTION_RTL : TEXT_DIRECTION_LTR;
+                }
+                break;
+            // Pass down the hierarchy the following text direction values
+            case TEXT_DIRECTION_FIRST_STRONG:
+            case TEXT_DIRECTION_ANY_RTL:
+            case TEXT_DIRECTION_LTR:
+            case TEXT_DIRECTION_RTL:
+                resolvedTextDirection = mTextDirection;
+                break;
+        }
+        mResolvedTextDirection = resolvedTextDirection;
+    }
+
+    @Override
+    protected void resetResolvedTextDirection() {
+        super.resetResolvedTextDirection();
+
+        // Take care of resetting the children resolution too
+        final int count = getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View child = getChildAt(i);
+            if (child.getTextDirection() == TEXT_DIRECTION_INHERIT) {
+                child.resetResolvedTextDirection();
+            }
+        }
+    }
+
+    /**
      * Return true if the pressed state should be delayed for children or descendants of this
      * ViewGroup. Generally, this should be done for containers that can scroll, such as a List.
      * This prevents the pressed state from appearing when the user is actually trying to scroll
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 47f5e4c..a1a7281 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1126,7 +1126,7 @@
         if (mServedView == mNextServedView && !mNextServedNeedsStart) {
             return;
         }
-        
+
         InputConnection ic = null;
         synchronized (mH) {
             if (mServedView == mNextServedView && !mNextServedNeedsStart) {
@@ -1242,6 +1242,27 @@
     }
 
     /**
+     * Notify the event when the user tapped or clicked the text view.
+     */
+    public void viewClicked(View view) {
+        final boolean focusChanged = mServedView != mNextServedView;
+        checkFocus();
+        synchronized (mH) {
+            if ((mServedView != view && (mServedView == null
+                    || !mServedView.checkInputConnectionProxy(view)))
+                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+                return;
+            }
+            try {
+                if (DEBUG) Log.v(TAG, "onViewClicked: " + focusChanged);
+                mCurMethod.viewClicked(focusChanged);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+            }
+        }
+    }
+
+    /**
      * Returns true if the current input method wants to watch the location
      * of the input editor's cursor in its window.
      */
diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java
index bb03afa..ea6f5ee 100644
--- a/core/java/android/view/inputmethod/InputMethodSession.java
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -63,6 +63,15 @@
             int candidatesStart, int candidatesEnd);
 
     /**
+     * This method is called when the user tapped a text view.
+     * IMEs can't rely on this method being called because this was not part of the original IME
+     * protocol, so applications with custom text editing written before this method appeared will
+     * not call to inform the IME of this interaction.
+     * @param focusChanged true if the user changed the focused view by this click.
+     */
+    public void viewClicked(boolean focusChanged);
+
+    /**
      * This method is called when cursor location of the target input field
      * has changed within its window.  This is not normally called, but will
      * only be reported if requested by the input method.
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 2f4774f..79a5aff 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -1150,11 +1150,12 @@
      * {@link #nativeSslCertErrorProceed(int)} or
      * {@link #nativeSslCertErrorCancel(int, int)}.
      */
-    private void reportSslCertError(final int handle, final int cert_error, byte cert_der[]) {
+    private void reportSslCertError(
+            final int handle, final int cert_error, byte cert_der[], String url) {
         final SslError ssl_error;
         try {
             X509Certificate cert = new X509CertImpl(cert_der);
-            ssl_error = new SslError(cert_error, cert);
+            ssl_error = new SslError(cert_error, cert, url);
         } catch (IOException e) {
             // Can't get the certificate, not much to do.
             Log.e(LOGTAG, "Can't get the certificate from WebKit, canceling");
diff --git a/core/java/android/webkit/CertTool.java b/core/java/android/webkit/CertTool.java
index 4c534f9..a2325c3 100644
--- a/core/java/android/webkit/CertTool.java
+++ b/core/java/android/webkit/CertTool.java
@@ -21,31 +21,27 @@
 import com.android.org.bouncycastle.jce.netscape.NetscapeCertRequest;
 import com.android.org.bouncycastle.util.encoders.Base64;
 
-import android.content.ActivityNotFoundException;
 import android.content.Context;
-import android.content.Intent;
 import android.security.Credentials;
+import android.security.KeyChain;
 import android.util.Log;
 
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
 import java.util.HashMap;
 
-class CertTool {
+final class CertTool {
     private static final String LOGTAG = "CertTool";
 
     private static final AlgorithmIdentifier MD5_WITH_RSA =
             new AlgorithmIdentifier(PKCSObjectIdentifiers.md5WithRSAEncryption);
 
-    static final String CERT = Credentials.CERTIFICATE;
-    static final String PKCS12 = Credentials.PKCS12;
-
     private static HashMap<String, String> sCertificateTypeMap;
     static {
         sCertificateTypeMap = new HashMap<String, String>();
-        sCertificateTypeMap.put("application/x-x509-ca-cert", CertTool.CERT);
-        sCertificateTypeMap.put("application/x-x509-user-cert", CertTool.CERT);
-        sCertificateTypeMap.put("application/x-pkcs12", CertTool.PKCS12);
+        sCertificateTypeMap.put("application/x-x509-ca-cert", KeyChain.EXTRA_CERTIFICATE);
+        sCertificateTypeMap.put("application/x-x509-user-cert", KeyChain.EXTRA_CERTIFICATE);
+        sCertificateTypeMap.put("application/x-pkcs12", KeyChain.EXTRA_PKCS12);
     }
 
     static String[] getKeyStrengthList() {
@@ -77,7 +73,7 @@
 
     static String getCertType(String mimeType) {
         return sCertificateTypeMap.get(mimeType);
-  }
+    }
 
     private CertTool() {}
 }
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index 4cae9d8..11ab0d7 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -104,9 +104,13 @@
 
         public void surfaceDestroyed(SurfaceHolder holder)
         {
-            // after we return from this we can't use the surface any more
-            mSurfaceHolder = null;
+            // After we return from this we can't use the surface any more.
             // The current Video View will be destroy when we play a new video.
+            pauseAndDispatch(mProxy);
+            mSurfaceHolder = null;
+            if (mMediaController != null) {
+                mMediaController.hide();
+            }
         }
     };
 
@@ -210,7 +214,6 @@
                 // which happens when the video view is detached from its parent
                 // view. This happens in the WebChromeClient before this method
                 // is invoked.
-                pauseAndDispatch(mProxy);
                 mProxy.dispatchOnStopFullScreen();
                 mLayout.removeView(getSurfaceView());
 
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 883656b..6c6974b 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -1107,7 +1107,8 @@
                 }
                 reflowText = exceedsMinScaleIncrement(mTextWrapScale, scale);
             }
-            mInitialZoomOverview = !exceedsMinScaleIncrement(scale, overviewScale);
+            mInitialZoomOverview = settings.getLoadWithOverviewMode() &&
+                    !exceedsMinScaleIncrement(scale, overviewScale);
             setZoomScale(scale, reflowText);
 
             // update the zoom buttons as the scale can be changed
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 2947ebe..aa23ad0 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -1201,18 +1201,15 @@
     private void initializeScrollWheel() {
         if (mInitialScrollOffset != Integer.MIN_VALUE) {
             return;
-
         }
         int[] selectorIndices = getSelectorIndices();
         int totalTextHeight = selectorIndices.length * mTextSize;
-        int totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
-        int textGapCount = selectorIndices.length - 1;
-        int selectorTextGapHeight = totalTextGapHeight / textGapCount;
-        // compensate for integer division loss of the components used to
-        // calculate the text gap
-        int integerDivisionLoss = (mTextSize + mBottom - mTop) % textGapCount;
-        mInitialScrollOffset = mCurrentScrollOffset = mTextSize - integerDivisionLoss / 2;
+        float totalTextGapHeight = (mBottom - mTop) - totalTextHeight;
+        float textGapCount = selectorIndices.length - 1;
+        int selectorTextGapHeight = (int) (totalTextGapHeight / textGapCount + 0.5f);
         mSelectorElementHeight = mTextSize + selectorTextGapHeight;
+        mInitialScrollOffset = mTextSize - 3 * (mSelectorElementHeight % 2);
+        mCurrentScrollOffset = mInitialScrollOffset;
         updateInputTextView();
     }
 
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 85e7eec..2a70ac8 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4948,7 +4948,10 @@
                         if (mMovement != null && mText instanceof Editable
                                 && mLayout != null && onCheckIsTextEditor()) {
                             InputMethodManager imm = InputMethodManager.peekInstance();
-                            if (imm != null) imm.showSoftInput(this, 0);
+                            if (imm != null) {
+                                imm.viewClicked(this);
+                                imm.showSoftInput(this, 0);
+                            }
                         }
                     }
                 }
@@ -7398,8 +7401,11 @@
 
             if ((isTextEditable() || mTextIsSelectable) && touchIsFinished) {
                 // Show the IME, except when selecting in read-only text.
+                final InputMethodManager imm = InputMethodManager.peekInstance();
+                if (imm != null) {
+                    imm.viewClicked(this);
+                }
                 if (!mTextIsSelectable) {
-                    final InputMethodManager imm = InputMethodManager.peekInstance();
                     handled |= imm != null && imm.showSoftInput(this, 0);
                 }
 
@@ -10010,6 +10016,127 @@
         return mInBatchEditControllers;
     }
 
+    /**
+     * Resolve the text direction.
+     *
+     * Text direction of paragraphs in a TextView is determined using a heuristic. If the correct
+     * text direction cannot be determined by the heuristic, the view’s resolved layout direction
+     * determines the direction.
+     *
+     * This heuristic and result is applied individually to each paragraph in a TextView, based on
+     * the text and style content of that paragraph. Paragraph text styles can also be used to force
+     * a particular direction.
+     */
+    @Override
+    protected void resolveTextDirection() {
+        int resolvedTextDirection = TEXT_DIRECTION_UNDEFINED;
+        switch(mTextDirection) {
+            default:
+            case TEXT_DIRECTION_INHERIT:
+                // Try to the text direction from the parent layout. If not possible, then we will
+                // use the default layout direction to decide later
+                if (mParent != null && mParent instanceof ViewGroup) {
+                    resolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection();
+                }
+                break;
+            case TEXT_DIRECTION_FIRST_STRONG:
+                resolvedTextDirection = getTextDirectionFromFirstStrong(mText);
+                break;
+            case TEXT_DIRECTION_ANY_RTL:
+                resolvedTextDirection = getTextDirectionFromAnyRtl(mText);
+                break;
+            case TEXT_DIRECTION_LTR:
+                resolvedTextDirection = TEXT_DIRECTION_LTR;
+                break;
+            case TEXT_DIRECTION_RTL:
+                resolvedTextDirection = TEXT_DIRECTION_RTL;
+                break;
+        }
+        // if we have been so far unable to get the text direction from the heuristics, then we are
+        // falling back using the layout direction
+        if (resolvedTextDirection == TEXT_DIRECTION_UNDEFINED) {
+            switch(getResolvedLayoutDirection()) {
+                default:
+                case LAYOUT_DIRECTION_LTR:
+                    resolvedTextDirection = TEXT_DIRECTION_LTR;
+                    break;
+                case LAYOUT_DIRECTION_RTL:
+                    resolvedTextDirection = TEXT_DIRECTION_RTL;
+                    break;
+            }
+        }
+        mResolvedTextDirection = resolvedTextDirection;
+    }
+
+    /**
+     * Get text direction following the "first strong" heuristic.
+     *
+     * @param cs the CharSequence used to get the text direction.
+     *
+     * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if
+     * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found.
+     */
+    private static int getTextDirectionFromFirstStrong(final CharSequence cs) {
+        final int length = cs.length();
+        for(int i = 0; i < length; i++) {
+            final char c = cs.charAt(i);
+            final byte dir = Character.getDirectionality(c);
+            if (isStrongLtrChar(dir)) {
+                return TEXT_DIRECTION_LTR;
+            } else if (isStrongRtlChar(dir)) {
+                return TEXT_DIRECTION_RTL;
+            }
+        }
+        return TEXT_DIRECTION_UNDEFINED;
+    }
+
+    /**
+     * Get text direction following the "any RTL" heuristic.
+     *
+     * @param cs the CharSequence used to get the text direction.
+     *
+     * @return {@link #TEXT_DIRECTION_RTL} if direction it RTL, {@link #TEXT_DIRECTION_LTR} if
+     * direction it LTR or {@link #TEXT_DIRECTION_UNDEFINED} if direction cannot be found.
+     */
+    private static int getTextDirectionFromAnyRtl(final CharSequence cs) {
+        final int length = cs.length();
+        boolean foundStrongLtr = false;
+        boolean foundStrongRtl = false;
+        for(int i = 0; i < length; i++) {
+            final char c = cs.charAt(i);
+            final byte dir = Character.getDirectionality(c);
+            if (isStrongLtrChar(dir)) {
+                foundStrongLtr = true;
+            } else if (isStrongRtlChar(dir)) {
+                foundStrongRtl = true;
+            }
+        }
+        if (foundStrongRtl) {
+            return TEXT_DIRECTION_RTL;
+        }
+        if (foundStrongLtr) {
+            return TEXT_DIRECTION_LTR;
+        }
+        return TEXT_DIRECTION_UNDEFINED;
+    }
+
+    /**
+     * Return true if the char direction is corresponding to a "strong RTL char" following the
+     * Unicode Bidirectional Algorithm (UBA).
+     */
+    private static boolean isStrongRtlChar(final byte dir) {
+        return (dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT ||
+                dir == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC);
+    }
+
+    /**
+     * Return true if the char direction is corresponding to a "strong LTR char" following the
+     * Unicode Bidirectional Algorithm (UBA).
+     */
+    private static boolean isStrongLtrChar(final byte dir) {
+        return (dir == Character.DIRECTIONALITY_LEFT_TO_RIGHT);
+    }
+
     @ViewDebug.ExportedProperty(category = "text")
     private CharSequence            mText;
     private CharSequence            mTransformed;
diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java
index b57046c..ec64552 100644
--- a/core/java/com/android/internal/content/PackageHelper.java
+++ b/core/java/com/android/internal/content/PackageHelper.java
@@ -135,6 +135,16 @@
         return null;
    }
 
+   public static String getSdFilesystem(String cid) {
+       try {
+            return getMountService().getSecureContainerFilesystemPath(cid);
+        } catch (RemoteException e) {
+            Log.e(TAG, "Failed to get container path for " + cid +
+                " with exception " + e);
+        }
+        return null;
+   }
+
     public static boolean finalizeSdDir(String cid) {
         try {
             int rc = getMountService().finalizeSecureContainer(cid);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 12687a1..7b65964 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -41,6 +41,7 @@
 import android.util.Printer;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.util.TimeUtils;
 
 import java.io.BufferedReader;
 import java.io.File;
@@ -4870,8 +4871,8 @@
         return 0;
     }
 
-    void readHistory(Parcel in) {
-        mHistoryBaseTime = in.readLong();
+    void readHistory(Parcel in, boolean andOldHistory) {
+        final long historyBaseTime = in.readLong();
 
         mHistoryBuffer.setDataSize(0);
         mHistoryBuffer.setDataPosition(0);
@@ -4889,15 +4890,35 @@
             in.setDataPosition(curPos + bufSize);
         }
 
-        long oldnow = SystemClock.elapsedRealtime() - (5*60*1000);
-        if (oldnow > 0) {
-            // If the system process has restarted, but not the entire
-            // system, then the mHistoryBaseTime already accounts for
-            // much of the elapsed time.  We thus want to adjust it back,
-            // to avoid large gaps in the data.  We determine we are
-            // in this case by arbitrarily saying it is so if at this
-            // point in boot the elapsed time is already more than 5 minutes.
-            mHistoryBaseTime -= oldnow;
+        if (andOldHistory) {
+            readOldHistory(in);
+        }
+
+        if (DEBUG_HISTORY) {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("****************** OLD mHistoryBaseTime: ");
+            TimeUtils.formatDuration(mHistoryBaseTime, sb);
+            Slog.i(TAG, sb.toString());
+        }
+        mHistoryBaseTime = historyBaseTime;
+        if (DEBUG_HISTORY) {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("****************** NEW mHistoryBaseTime: ");
+            TimeUtils.formatDuration(mHistoryBaseTime, sb);
+            Slog.i(TAG, sb.toString());
+        }
+
+        // We are just arbitrarily going to insert 1 minute from the sample of
+        // the last run until samples in this run.
+        if (mHistoryBaseTime > 0) {
+            long oldnow = SystemClock.elapsedRealtime();
+            mHistoryBaseTime = (mHistoryBaseTime - oldnow) + 60*1000;
+            if (DEBUG_HISTORY) {
+                StringBuilder sb = new StringBuilder(128);
+                sb.append("****************** ADJUSTED mHistoryBaseTime: ");
+                TimeUtils.formatDuration(mHistoryBaseTime, sb);
+                Slog.i(TAG, sb.toString());
+            }
         }
     }
 
@@ -4910,12 +4931,24 @@
         }
     }
 
-    void writeHistory(Parcel out) {
-        out.writeLong(mLastHistoryTime);
+    void writeHistory(Parcel out, boolean andOldHistory) {
+        if (DEBUG_HISTORY) {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("****************** WRITING mHistoryBaseTime: ");
+            TimeUtils.formatDuration(mHistoryBaseTime, sb);
+            sb.append(" mLastHistoryTime: ");
+            TimeUtils.formatDuration(mLastHistoryTime, sb);
+            Slog.i(TAG, sb.toString());
+        }
+        out.writeLong(mHistoryBaseTime + mLastHistoryTime);
         out.writeInt(mHistoryBuffer.dataSize());
         if (DEBUG_HISTORY) Slog.i(TAG, "***************** WRITING HISTORY: "
                 + mHistoryBuffer.dataSize() + " bytes at " + out.dataPosition());
         out.appendFrom(mHistoryBuffer, 0, mHistoryBuffer.dataSize());
+
+        if (andOldHistory) {
+            writeOldHistory(out);
+        }
     }
 
     void writeOldHistory(Parcel out) {
@@ -4935,8 +4968,7 @@
             return;
         }
 
-        readHistory(in);
-        readOldHistory(in);
+        readHistory(in, true);
 
         mStartCount = in.readInt();
         mBatteryUptime = in.readLong();
@@ -5136,8 +5168,7 @@
 
         out.writeInt(VERSION);
 
-        writeHistory(out);
-        writeOldHistory(out);
+        writeHistory(out, true);
 
         out.writeInt(mStartCount);
         out.writeLong(computeBatteryUptime(NOW_SYS, STATS_SINCE_CHARGED));
@@ -5340,7 +5371,7 @@
             throw new ParcelFormatException("Bad magic number");
         }
 
-        readHistory(in);
+        readHistory(in, false);
 
         mStartCount = in.readInt();
         mBatteryUptime = in.readLong();
@@ -5461,7 +5492,7 @@
 
         out.writeInt(MAGIC);
 
-        writeHistory(out);
+        writeHistory(out, false);
 
         out.writeInt(mStartCount);
         out.writeLong(mBatteryUptime);
diff --git a/core/java/com/android/internal/os/RuntimeInit.java b/core/java/com/android/internal/os/RuntimeInit.java
index 5e9cd23..f13e770 100644
--- a/core/java/com/android/internal/os/RuntimeInit.java
+++ b/core/java/com/android/internal/os/RuntimeInit.java
@@ -25,16 +25,13 @@
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Slog;
-
 import com.android.internal.logging.AndroidConfig;
-
+import com.android.server.NetworkManagementSocketTagger;
 import dalvik.system.VMRuntime;
-
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
-import java.util.logging.LogManager;
 import java.util.TimeZone;
-
+import java.util.logging.LogManager;
 import org.apache.harmony.luni.internal.util.TimezoneGetter;
 
 /**
@@ -129,6 +126,11 @@
         System.setProperty("http.agent", userAgent);
 
         /*
+         * Wire socket tagging to traffic stats.
+         */
+        NetworkManagementSocketTagger.install();
+
+        /*
          * If we're running in an emulator launched with "-trace", put the
          * VM into emulator trace profiling mode so that the user can hit
          * F9/F10 at any time to capture traces.  This has performance
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index 338dcaa..f875cbd 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -36,7 +36,9 @@
     void updateSelection(int oldSelStart, int oldSelEnd,
             int newSelStart, int newSelEnd,
             int candidatesStart, int candidatesEnd);
-    
+
+    void viewClicked(boolean focusChanged);
+
     void updateCursor(in Rect newCursor);
     
     void displayCompletions(in CompletionInfo[] completions);
diff --git a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
index 3e7b976..5b35104 100644
--- a/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
+++ b/core/java/com/android/internal/widget/multiwaveview/MultiWaveView.java
@@ -224,8 +224,8 @@
 
     /**
      * Animation used to attract user's attention to the target button.
-     * Assumes mChevronDrawables is an a list with an even number of chevrons filled with left
-     * followed by right chevrons.
+     * Assumes mChevronDrawables is an a list with an even number of chevrons filled with
+     * mFeedbackCount items in the order: left, right, top, bottom.
      */
     private void startChevronAnimation() {
         final float r = mHandleDrawable.getWidth() / 2;
@@ -442,6 +442,7 @@
         mHandleDrawable.setX(mWaveCenterX);
         mHandleDrawable.setY(mWaveCenterY);
         mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
+        Tweener.reset();
     }
 
     @Override
diff --git a/core/java/com/android/internal/widget/multiwaveview/Tweener.java b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
index 0cff00a..bc8a62f 100644
--- a/core/java/com/android/internal/widget/multiwaveview/Tweener.java
+++ b/core/java/com/android/internal/widget/multiwaveview/Tweener.java
@@ -18,25 +18,42 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map.Entry;
 
 import android.animation.Animator.AnimatorListener;
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
 import android.animation.PropertyValuesHolder;
 import android.animation.TimeInterpolator;
 import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.util.Log;
 
 class Tweener {
     private static final String TAG = "Tweener";
+    private static final boolean DEBUG = false;
 
-    private Object object;
     ObjectAnimator animator;
     private static HashMap<Object, Tweener> sTweens = new HashMap<Object, Tweener>();
 
-    public Tweener(Object obj, ObjectAnimator anim) {
-        object = obj;
+    public Tweener(ObjectAnimator anim) {
         animator = anim;
     }
 
+    private static void remove(Animator animator) {
+        Iterator<Entry<Object, Tweener>> iter = sTweens.entrySet().iterator();
+        while (iter.hasNext()) {
+            Entry<Object, Tweener> entry = iter.next();
+            if (entry.getValue().animator == animator) {
+                if (DEBUG) Log.v(TAG, "Removing tweener " + sTweens.get(entry.getKey())
+                        + " sTweens.size() = " + sTweens.size());
+                iter.remove();
+                break; // an animator can only be attached to one object
+            }
+        }
+    }
+
     public static Tweener to(Object object, long duration, Object... vars) {
         long delay = 0;
         AnimatorUpdateListener updateListener = null;
@@ -77,32 +94,35 @@
 
         // Re-use existing tween, if present
         Tweener tween = sTweens.get(object);
+        ObjectAnimator anim = null;
         if (tween == null) {
-            ObjectAnimator anim = ObjectAnimator.ofPropertyValuesHolder(object,
+            anim = ObjectAnimator.ofPropertyValuesHolder(object,
                     props.toArray(new PropertyValuesHolder[props.size()]));
-            tween = new Tweener(object, anim);
+            tween = new Tweener(anim);
             sTweens.put(object, tween);
+            if (DEBUG) Log.v(TAG, "Added new Tweener " + tween);
         } else {
-            tween.animator.cancel();
-            replace(props, object);
+            anim = sTweens.get(object).animator;
+            replace(props, object); // Cancel all animators for given object
         }
 
         if (interpolator != null) {
-            tween.animator.setInterpolator(interpolator);
+            anim.setInterpolator(interpolator);
         }
 
         // Update animation with properties discovered in loop above
-        tween.animator.setStartDelay(delay);
-        tween.animator.setDuration(duration);
+        anim.setStartDelay(delay);
+        anim.setDuration(duration);
         if (updateListener != null) {
-            tween.animator.removeAllUpdateListeners(); // There should be only one
-            tween.animator.addUpdateListener(updateListener);
+            anim.removeAllUpdateListeners(); // There should be only one
+            anim.addUpdateListener(updateListener);
         }
         if (listener != null) {
-            tween.animator.removeAllListeners(); // There should be only one.
-            tween.animator.addListener(listener);
+            anim.removeAllListeners(); // There should be only one.
+            anim.addListener(listener);
         }
-        tween.animator.start();
+        anim.addListener(mCleanupListener);
+        anim.start();
 
         return tween;
     }
@@ -114,18 +134,40 @@
         return Tweener.to(object, duration, vars);
     }
 
-    static void replace(ArrayList<PropertyValuesHolder> props, Object... args) {
+    // Listener to watch for completed animations and remove them.
+    private static AnimatorListener mCleanupListener = new AnimatorListenerAdapter() {
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            remove(animation);
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            remove(animation);
+        }
+    };
+
+    public static void reset() {
+        if (DEBUG) {
+            Log.v(TAG, "Reset()");
+            if (sTweens.size() > 0) {
+                Log.v(TAG, "Cleaning up " + sTweens.size() + " animations");
+            }
+        }
+        sTweens.clear();
+    }
+
+    private static void replace(ArrayList<PropertyValuesHolder> props, Object... args) {
         for (final Object killobject : args) {
             Tweener tween = sTweens.get(killobject);
             if (tween != null) {
-                if (killobject == tween.object) {
-                    tween.animator.cancel();
-                    if (props != null) {
-                        tween.animator.setValues(
-                                props.toArray(new PropertyValuesHolder[props.size()]));
-                    } else {
-                        sTweens.remove(tween);
-                    }
+                tween.animator.cancel();
+                if (props != null) {
+                    tween.animator.setValues(
+                            props.toArray(new PropertyValuesHolder[props.size()]));
+                } else {
+                    sTweens.remove(tween);
                 }
             }
         }
diff --git a/core/java/com/android/server/NetworkManagementSocketTagger.java b/core/java/com/android/server/NetworkManagementSocketTagger.java
new file mode 100644
index 0000000..306d223
--- /dev/null
+++ b/core/java/com/android/server/NetworkManagementSocketTagger.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import dalvik.system.SocketTagger;
+import java.io.FileDescriptor;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.SocketException;
+import java.nio.charset.Charsets;
+
+/**
+ * Assigns tags to sockets for traffic stats.
+ */
+public final class NetworkManagementSocketTagger extends SocketTagger {
+
+    private static final boolean LOGI = false;
+
+    private static ThreadLocal<SocketTags> threadSocketTags = new ThreadLocal<SocketTags>() {
+        @Override protected SocketTags initialValue() {
+            return new SocketTags();
+        }
+    };
+
+    public static void install() {
+        SocketTagger.set(new NetworkManagementSocketTagger());
+    }
+
+    public static void setThreadSocketStatsTag(int tag) {
+        threadSocketTags.get().statsTag = tag;
+    }
+
+    public static void setThreadSocketStatsUid(int uid) {
+        threadSocketTags.get().statsUid = uid;
+    }
+
+    @Override public void tag(FileDescriptor fd) throws SocketException {
+        final SocketTags options = threadSocketTags.get();
+        if (LOGI) {
+            System.logI("tagSocket(" + fd.getInt$() + ") with statsTag="
+                    + options.statsTag + ", statsUid=" + options.statsUid);
+        }
+        try {
+            // TODO: skip tagging when options would be no-op
+            tagSocketFd(fd, options.statsTag, options.statsUid);
+        } catch (IOException e) {
+            throw new SocketException("Problem tagging socket", e);
+        }
+    }
+
+    private void tagSocketFd(FileDescriptor fd, int tag, int uid) throws IOException {
+        final int fdNum = fd.getInt$();
+        if (fdNum == -1 || (tag == -1 && uid == -1)) return;
+
+        String cmd = "t " + fdNum;
+        if (tag == -1) {
+            // Case where just the uid needs adjusting. But probably the caller
+            // will want to track his own name here, just in case.
+            cmd += " 0";
+        } else {
+            cmd += " " + tagToKernel(tag);
+        }
+        if (uid != -1) {
+            cmd += " " + uid;
+        }
+        internalModuleCtrl(cmd);
+    }
+
+    @Override public void untag(FileDescriptor fd) throws SocketException {
+        if (LOGI) {
+            System.logI("untagSocket(" + fd.getInt$() + ")");
+        }
+        try {
+            unTagSocketFd(fd);
+        } catch (IOException e) {
+            throw new SocketException("Problem untagging socket", e);
+        }
+    }
+
+    private void unTagSocketFd(FileDescriptor fd) throws IOException {
+        int fdNum = fd.getInt$();
+        if (fdNum == -1) return;
+        String cmd = "u " + fdNum;
+        internalModuleCtrl(cmd);
+    }
+
+    public static class SocketTags {
+        public int statsTag = -1;
+        public int statsUid = -1;
+    }
+
+    /**
+     * Sends commands to the kernel netfilter module.
+     *
+     * @param cmd command string for the qtaguid netfilter module. May not be null.
+     *   <p>Supports:
+     *     <ul><li>tag a socket:<br>
+     *        <code>t <i>sock_fd</i> <i>acct_tag</i> [<i>uid_in_case_caller_is_acting_on_behalf_of</i>]</code><br>
+     *     <code>*_tag</code> defaults to default_policy_tag_from_uid(uid_of_caller)<br>
+     *     <code>acct_tag</code> is either 0 or greater that 2^32.<br>
+     *     <code>uid_*</code> is only settable by privileged UIDs (DownloadManager,...)
+     *     </li>
+     *     <li>untag a socket, preserving counters:<br>
+     *       <code>u <i>sock_fd</i></code>
+     *     </li></ul>
+     *   <p>Notes:<br>
+     *   <ul><li><i>sock_fd</i> is withing the callers process space.</li>
+     *   <li><i>*_tag</i> are 64bit values</li></ul>
+     *
+     */
+    private void internalModuleCtrl(String cmd) throws IOException {
+        final FileOutputStream procOut;
+        // TODO: Use something like
+        //  android.os.SystemProperties.getInt("persist.bandwidth.enable", 0)
+        // to see if tagging should happen or not.
+        try {
+            procOut = new FileOutputStream("/proc/net/xt_qtaguid/ctrl");
+        } catch (FileNotFoundException e) {
+            if (LOGI) {
+                System.logI("Can't talk to kernel module:" + e);
+            }
+            return;
+        }
+        try {
+            procOut.write(cmd.getBytes(Charsets.US_ASCII));
+        } finally {
+            procOut.close();
+        }
+    }
+
+    /**
+     * Convert {@link Integer} tag to {@code /proc/} format. Assumes unsigned
+     * base-10 format like {@code 2147483647}. Currently strips signed bit to
+     * avoid using {@link java.math.BigInteger}.
+     */
+    public static String tagToKernel(int tag) {
+        // TODO: eventually write in hex, since that's what proc exports
+        // TODO: migrate to direct integer instead of odd shifting
+        return Long.toString((((long) tag) << 32) & 0x7FFFFFFF00000000L);
+    }
+
+    /**
+     * Convert {@code /proc/} tag format to {@link Integer}. Assumes incoming
+     * format like {@code 0x7fffffff00000000}.
+     */
+    public static int kernelToTag(String string) {
+        // TODO: migrate to direct integer instead of odd shifting
+        return (int) (Long.decode(string) >> 32);
+    }
+}
diff --git a/core/jni/android/graphics/SurfaceTexture.cpp b/core/jni/android/graphics/SurfaceTexture.cpp
index dd8b378..2de0932 100644
--- a/core/jni/android/graphics/SurfaceTexture.cpp
+++ b/core/jni/android/graphics/SurfaceTexture.cpp
@@ -233,12 +233,6 @@
     return surfaceTexture->getTimestamp();
 }
 
-static jint SurfaceTexture_getQueuedCount(JNIEnv* env, jobject thiz)
-{
-    sp<SurfaceTexture> surfaceTexture(SurfaceTexture_getSurfaceTexture(env, thiz));
-    return surfaceTexture->getQueuedCount();
-}
-
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gSurfaceTextureMethods[] = {
@@ -249,7 +243,6 @@
     {"nativeUpdateTexImage",     "()V",   (void*)SurfaceTexture_updateTexImage },
     {"nativeGetTransformMatrix", "([F)V", (void*)SurfaceTexture_getTransformMatrix },
     {"nativeGetTimestamp",       "()J",   (void*)SurfaceTexture_getTimestamp },
-    {"nativeGetQueuedCount",     "()I",   (void*)SurfaceTexture_getQueuedCount }
 };
 
 int register_android_graphics_SurfaceTexture(JNIEnv* env)
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 47902a8..1a32060 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1384,7 +1384,7 @@
     <permission android:name="android.permission.READ_NETWORK_USAGE_HISTORY"
         android:label="@string/permlab_readNetworkUsageHistory"
         android:description="@string/permdesc_readNetworkUsageHistory"
-        android:protectionLevel="signature" />
+        android:protectionLevel="signatureOrSystem" />
 
     <!-- Allows an application to manage network policies (such as warning and disable
          limits) and to define application-specific rules. @hide -->
@@ -1393,6 +1393,14 @@
         android:description="@string/permdesc_manageNetworkPolicy"
         android:protectionLevel="signature" />
 
+    <!-- Allows an application to account its network traffic against other UIDs. Used
+         by system services like download manager and media server. Not for use by
+         third party apps. @hide -->
+    <permission android:name="android.permission.MODIFY_NETWORK_ACCOUNTING"
+        android:label="@string/permlab_modifyNetworkAccounting"
+        android:description="@string/permdesc_modifyNetworkAccounting"
+        android:protectionLevel="signatureOrSystem" />
+
     <!-- C2DM permission. 
          @hide Used internally.
      -->
diff --git a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
deleted file mode 100644
index 1f7aeee..0000000
--- a/core/res/res/drawable-hdpi/btn_check_on_disable_focused_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png b/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png
deleted file mode 100755
index 4c1d89d..0000000
--- a/core/res/res/drawable-hdpi/btn_toggle_on_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png b/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png
deleted file mode 100644
index 6963a0e..0000000
--- a/core/res/res/drawable-hdpi/btn_toggle_pressed_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/cab_holo_dark.9.png b/core/res/res/drawable-hdpi/cab_holo_dark.9.png
deleted file mode 100755
index 64e2052..0000000
--- a/core/res/res/drawable-hdpi/cab_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png
deleted file mode 100644
index 3a84de9..0000000
--- a/core/res/res/drawable-hdpi/dialog_bottom_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png b/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png
index e21a87c..0f4bfe6 100644
--- a/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png b/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png
index 3283f99..995705d 100644
--- a/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png
+++ b/core/res/res/drawable-hdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png b/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
deleted file mode 100644
index 48cc017..0000000
--- a/core/res/res/drawable-mdpi/btn_check_on_disable_focused_holo_dark.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png b/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png
deleted file mode 100755
index 66cbe48..0000000
--- a/core/res/res/drawable-mdpi/btn_toggle_on_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png b/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png
deleted file mode 100644
index b6508fc..0000000
--- a/core/res/res/drawable-mdpi/btn_toggle_pressed_holo_light.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/cab_holo_dark.9.png b/core/res/res/drawable-mdpi/cab_holo_dark.9.png
deleted file mode 100755
index 7daae1f..0000000
--- a/core/res/res/drawable-mdpi/cab_holo_dark.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png b/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png
deleted file mode 100644
index cb3d0f2..0000000
--- a/core/res/res/drawable-mdpi/dialog_bottom_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png b/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png
index c10344f..754d7bc 100644
--- a/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png b/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png
index 08c6cfe..0187a02 100644
--- a/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png
+++ b/core/res/res/drawable-mdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png
new file mode 100644
index 0000000..544924e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_normal.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png
new file mode 100644
index 0000000..2d28009
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/ic_lockscreen_handle_pressed.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png b/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png
new file mode 100644
index 0000000..cdc0afb
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_bottom_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png b/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png
new file mode 100644
index 0000000..36b0448
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_bottom_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png b/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png
new file mode 100644
index 0000000..3a7a8b3
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_bottom_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_center_bright.9.png b/core/res/res/drawable-xhdpi/popup_center_bright.9.png
new file mode 100644
index 0000000..b1a8e3e
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_center_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_center_dark.9.png b/core/res/res/drawable-xhdpi/popup_center_dark.9.png
new file mode 100644
index 0000000..87378e1
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_center_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_center_medium.9.png b/core/res/res/drawable-xhdpi/popup_center_medium.9.png
new file mode 100644
index 0000000..ea29ed4
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_center_medium.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_full_bright.9.png b/core/res/res/drawable-xhdpi/popup_full_bright.9.png
new file mode 100644
index 0000000..114faa0
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_full_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_full_dark.9.png b/core/res/res/drawable-xhdpi/popup_full_dark.9.png
new file mode 100644
index 0000000..996beaa
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_full_dark.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_top_bright.9.png b/core/res/res/drawable-xhdpi/popup_top_bright.9.png
new file mode 100644
index 0000000..64e4139
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_top_bright.9.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/popup_top_dark.9.png b/core/res/res/drawable-xhdpi/popup_top_dark.9.png
new file mode 100644
index 0000000..902bc29
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/popup_top_dark.9.png
Binary files differ
diff --git a/core/res/res/layout-sw600dp/date_picker_dialog.xml b/core/res/res/layout-sw600dp/date_picker_dialog.xml
new file mode 100644
index 0000000..004d52a
--- /dev/null
+++ b/core/res/res/layout-sw600dp/date_picker_dialog.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2007, 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.
+*/
+-->
+
+<DatePicker xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/datePicker"
+    android:layout_gravity="center_horizontal"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    />
diff --git a/core/res/res/layout-sw600dp/number_picker.xml b/core/res/res/layout-sw600dp/number_picker.xml
new file mode 100644
index 0000000..807daf2
--- /dev/null
+++ b/core/res/res/layout-sw600dp/number_picker.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, 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.
+*/
+-->
+
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <ImageButton android:id="@+id/increment"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        style="?android:attr/numberPickerUpButtonStyle"
+        android:contentDescription="@string/number_picker_increment_button" />
+
+    <EditText android:id="@+id/numberpicker_input"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        style="?android:attr/numberPickerInputTextStyle" />
+
+    <ImageButton android:id="@+id/decrement"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        style="?android:attr/numberPickerDownButtonStyle"
+        android:contentDescription="@string/number_picker_decrement_button" />
+
+</merge>
diff --git a/core/res/res/layout/date_picker_dialog.xml b/core/res/res/layout/date_picker_dialog.xml
index 004d52a..db8f311 100644
--- a/core/res/res/layout/date_picker_dialog.xml
+++ b/core/res/res/layout/date_picker_dialog.xml
@@ -22,4 +22,6 @@
     android:layout_gravity="center_horizontal"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
+    android:spinnersShown="true"
+    android:calendarViewShown="false"
     />
diff --git a/core/res/res/layout/date_picker_holo.xml b/core/res/res/layout/date_picker_holo.xml
index 026cbfb..8627637 100644
--- a/core/res/res/layout/date_picker_holo.xml
+++ b/core/res/res/layout/date_picker_holo.xml
@@ -77,7 +77,8 @@
         android:id="@+id/calendar_view"
         android:layout_width="245dip"
         android:layout_height="280dip"
-        android:layout_marginLeft="22dip"
+        android:layout_marginLeft="16dip"
+        android:layout_marginRight="16dip"
         android:layout_weight="1"
         android:focusable="true"
         android:focusableInTouchMode="true"
diff --git a/core/res/res/layout/number_picker.xml b/core/res/res/layout/number_picker.xml
index 807daf2..f2f524c 100644
--- a/core/res/res/layout/number_picker.xml
+++ b/core/res/res/layout/number_picker.xml
@@ -23,6 +23,8 @@
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         style="?android:attr/numberPickerUpButtonStyle"
+        android:paddingTop="22dip"
+        android:paddingBottom="22dip"
         android:contentDescription="@string/number_picker_increment_button" />
 
     <EditText android:id="@+id/numberpicker_input"
@@ -34,6 +36,8 @@
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         style="?android:attr/numberPickerDownButtonStyle"
+        android:paddingTop="22dip"
+        android:paddingBottom="22dip"
         android:contentDescription="@string/number_picker_decrement_button" />
 
 </merge>
diff --git a/core/res/res/values-sw600dp/config.xml b/core/res/res/values-sw600dp/config.xml
index d6a0cdd..13bbac6 100644
--- a/core/res/res/values-sw600dp/config.xml
+++ b/core/res/res/values-sw600dp/config.xml
@@ -23,9 +23,6 @@
     <!-- see comment in values/config.xml -->
     <integer name="config_longPressOnPowerBehavior">2</integer>
 
-    <!-- Show sliding tab before lockscreen -->
-    <bool name="config_enableSlidingTabFirst">false</bool>
-
     <!-- Enable lockscreen rotation -->
     <bool name="config_enableLockScreenRotation">true</bool>
 
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 37e6027..fd61cfd 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -1972,6 +1972,24 @@
             <!-- Locale -->
             <enum name="locale" value="3" />
         </attr>
+        <!-- Direction of the text. A heuristic is used to determine the resolved text direction
+             of paragraphs. -->
+        <attr name="textDirection" format="integer">
+            <!-- Default -->
+            <enum name="inherit" value="0" />
+            <!-- Default for the root view. The first strong directional character determines the
+                 paragraph direction.  If there is o strong directional character, the paragraph
+                 direction is the view’s resolved layout direction. -->
+            <enum name="firstStrong" value="1" />
+            <!-- The paragraph direction is RTL if it contains any strong RTL character, otherwise
+                 it is LTR if it contains any strong LTR characters.  If there are neither, the
+                 paragraph direction is the view’s resolved layout direction. -->
+            <enum name="anyRtl" value="2" />
+            <!-- The text direction is left to right. -->
+            <enum name="ltr" value="3" />
+            <!-- The text direction is right to left. -->
+            <enum name="rtl" value="4" />
+        </attr>
     </declare-styleable>
 
     <!-- Attributes that can be used with a {@link android.view.ViewGroup} or any
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 0ad3184..827153e 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -3,16 +3,16 @@
 /*
 ** Copyright 2009, 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 
+** 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 
+**     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 
+** 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.
 */
 -->
@@ -54,25 +54,25 @@
          connected.  If you use the ims apn DCT will block
          any other apn from connecting until ims apn is connected-->
     <bool name="ImsConnectedDefaultValue">false</bool>
-    
+
     <!-- Flag indicating whether the surface flinger is inefficient
          at performing a blur.  Used by parts of the UI to turn off
          the blur effect where it isn't worth the performance hit. -->
     <bool name="config_sf_slowBlur">false</bool>
-    
+
     <!-- The duration (in milliseconds) of a short animation. -->
     <integer name="config_shortAnimTime">200</integer>
-    
+
     <!-- The duration (in milliseconds) of a medium-length animation. -->
     <integer name="config_mediumAnimTime">400</integer>
-    
+
     <!-- The duration (in milliseconds) of a long animation. -->
     <integer name="config_longAnimTime">500</integer>
 
     <!-- The duration (in milliseconds) of the activity open/close and fragment open/close animations. -->
     <integer name="config_activityShortDur">150</integer>
     <integer name="config_activityDefaultDur">220</integer>
-    
+
     <!-- Duration for the dim animation behind a dialog.  This may be either
          a percentage, which is relative to the duration of the enter/open
          animation of the window being shown that is dimming behind, or it may
@@ -83,11 +83,11 @@
          maximum (let them grow as large as the screen).  Actual values are
          specified for -large and -xlarge configurations. -->
     <dimen name="config_prefDialogWidth">320dp</dimen>
-    
+
     <!-- Whether dialogs should close automatically when the user touches outside
          of them.  This should not normally be modified. -->
     <bool name="config_closeDialogWhenTouchOutside">false</bool>
-    
+
     <!-- The duration (in milliseconds) that the radio will scan for a signal
          when there's no network connection. If the scan doesn't timeout, use zero -->
     <integer name="config_radioScanningTimeout">0</integer>
@@ -202,7 +202,7 @@
          the slider is open.  This can be set or unset depending how easily
          the slider can be opened (for example, in a pocket or purse). -->
     <bool name="config_bypass_keyguard_if_slider_open">true</bool>
-    
+
     <!-- Flag indicating whether the we should enable the automatic brightness in Settings.
          Software implementation will be used if config_hardware_auto_brightness_available is not set -->
     <bool name="config_automatic_brightness_available">false</bool>
@@ -212,10 +212,10 @@
 
     <!-- If this is true, the screen will come on when you unplug usb/power/whatever. -->
     <bool name="config_unplugTurnsOnScreen">false</bool>
-    
+
     <!-- If this is true, the screen will fade off. -->
     <bool name="config_animateScreenLights">true</bool>
-    
+
     <!-- XXXXXX END OF RESOURCES USING WRONG NAMING CONVENTION -->
 
     <!-- If true, the screen can be rotated via the accelerometer in all 4
@@ -300,7 +300,7 @@
         <item>20</item>
         <item>21</item>
     </integer-array>
-    
+
     <!-- Vibrator pattern for feedback about touching a virtual key -->
     <integer-array name="config_virtualKeyVibePattern">
         <item>0</item>
@@ -380,8 +380,8 @@
     <!-- Allow the menu hard key to be disabled in LockScreen on some devices -->
     <bool name="config_disableMenuKeyInLockScreen">false</bool>
 
-    <!-- Show sliding tab before lockscreen -->
-    <bool name="config_enableSlidingTabFirst">true</bool>
+    <!-- Don't show lock screen before unlock screen (PIN/pattern/password) -->
+    <bool name="config_enableLockBeforeUnlockScreen">false</bool>
 
     <!-- Diable lockscreen rotation by default -->
     <bool name="config_enableLockScreenRotation">false</bool>
@@ -460,7 +460,7 @@
          This feature should be disabled for most devices. -->
     <integer name="config_virtualKeyQuietTimeMillis">0</integer>
 
-    <!-- Component name of the default wallpaper. This will be ImageWallpaper if not 
+    <!-- Component name of the default wallpaper. This will be ImageWallpaper if not
          specified -->
     <string name="default_wallpaper_component">@null</string>
 
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 7bbfa9c..db6f98f 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1782,4 +1782,5 @@
   <public type="integer" name="status_bar_notification_info_maxnum" />
   <public type="string" name="status_bar_notification_info_overflow" />
 
+  <public type="attr" name="textDirection"/>
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index cd52a5a..88ed9c6b 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1443,6 +1443,11 @@
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_manageNetworkPolicy">Allows an application to manage network policies and define application-specific rules.</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_modifyNetworkAccounting">modify network usage accounting</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_modifyNetworkAccounting">Allows modification of how network usage is accounted against applications. Not for use by normal applications.</string>
+
     <!-- Policy administration -->
 
     <!-- Title of policy access to limiting the user's password choices -->
diff --git a/core/tests/coretests/src/android/widget/TextViewTest.java b/core/tests/coretests/src/android/widget/TextViewTest.java
index d8d145c..a37f1a3 100644
--- a/core/tests/coretests/src/android/widget/TextViewTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewTest.java
@@ -22,6 +22,7 @@
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.SmallTest;
 import android.text.GetChars;
+import android.view.View;
 import android.widget.TextView;
 
 /**
@@ -58,4 +59,122 @@
         assertEquals('o', c2[4]);
         assertEquals('\0', c2[5]);
     }
+
+    @SmallTest
+    public void testTextDirectionDefault() {
+        TextView tv = new TextView(mContext);
+        assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getTextDirection());
+    }
+
+    @SmallTest
+    public void testSetGetTextDirection() {
+        TextView tv = new TextView(mContext);
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+        assertEquals(View.TEXT_DIRECTION_FIRST_STRONG, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
+        assertEquals(View.TEXT_DIRECTION_ANY_RTL, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
+        assertEquals(View.TEXT_DIRECTION_INHERIT, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_RTL);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getTextDirection());
+    }
+
+    @SmallTest
+    public void testGetResolvedTextDirectionLtr() {
+        TextView tv = new TextView(mContext);
+        tv.setText("this is a test");
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_RTL);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+    }
+
+    @SmallTest
+    public void testGetResolvedTextDirectionLtrWithInheritance() {
+        LinearLayout ll = new LinearLayout(mContext);
+        ll.setTextDirection(View.TEXT_DIRECTION_RTL);
+
+        TextView tv = new TextView(mContext);
+        tv.setText("this is a test");
+        ll.addView(tv);
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_RTL);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+    }
+
+    @SmallTest
+    public void testGetResolvedTextDirectionRtl() {
+        TextView tv = new TextView(mContext);
+        tv.setText("\u05DD\u05DE"); // hebrew
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_RTL);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+    }
+
+    @SmallTest
+    public void testGetResolvedTextDirectionRtlWithInheritance() {
+        LinearLayout ll = new LinearLayout(mContext);
+        ll.setTextDirection(View.TEXT_DIRECTION_RTL);
+
+        TextView tv = new TextView(mContext);
+        tv.setText("\u05DD\u05DE"); // hebrew
+        ll.addView(tv);
+
+        tv.setTextDirection(View.TEXT_DIRECTION_FIRST_STRONG);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_ANY_RTL);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_INHERIT);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_LTR);
+        assertEquals(View.TEXT_DIRECTION_LTR, tv.getResolvedTextDirection());
+
+        tv.setTextDirection(View.TEXT_DIRECTION_RTL);
+        assertEquals(View.TEXT_DIRECTION_RTL, tv.getResolvedTextDirection());
+    }
 }
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index b9c0d80..0b8d40f 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -84,6 +84,16 @@
         <group gid="diag" />
     </permission>
 
+    <!-- Group that can read detailed network usage statistics -->
+    <permission name="android.permission.READ_NETWORK_USAGE_HISTORY">
+        <group gid="net_bw_stats" />
+    </permission>
+
+    <!-- Group that can modify how network statistics are accounted -->
+    <permission name="android.permission.MODIFY_NETWORK_ACCOUNTING">
+        <group gid="net_bw_acct" />
+    </permission>
+
     <!-- ================================================================== -->
     <!-- ================================================================== -->
     <!-- ================================================================== -->
diff --git a/data/fonts/DroidSansHebrew-Bold.ttf b/data/fonts/DroidSansHebrew-Bold.ttf
new file mode 100644
index 0000000..c1acb38
--- /dev/null
+++ b/data/fonts/DroidSansHebrew-Bold.ttf
Binary files differ
diff --git a/data/fonts/DroidSansHebrew-Regular.ttf b/data/fonts/DroidSansHebrew-Regular.ttf
new file mode 100644
index 0000000..af6a58d
--- /dev/null
+++ b/data/fonts/DroidSansHebrew-Regular.ttf
Binary files differ
diff --git a/data/fonts/DroidSansHebrew.ttf b/data/fonts/DroidSansHebrew.ttf
deleted file mode 100644
index 8d77e3e..0000000
--- a/data/fonts/DroidSansHebrew.ttf
+++ /dev/null
Binary files differ
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index 692ce34..d222c0b 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -18,7 +18,8 @@
     frameworks/base/data/fonts/DroidSans.ttf:system/fonts/DroidSans.ttf \
     frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.ttf \
     frameworks/base/data/fonts/DroidSansArabic.ttf:system/fonts/DroidSansArabic.ttf \
-    frameworks/base/data/fonts/DroidSansHebrew.ttf:system/fonts/DroidSansHebrew.ttf \
+    frameworks/base/data/fonts/DroidSansHebrew-Regular.ttf:system/fonts/DroidSansHebrew-Regular.ttf \
+    frameworks/base/data/fonts/DroidSansHebrew-Bold.ttf:system/fonts/DroidSansHebrew-Bold.ttf \
     frameworks/base/data/fonts/DroidSansThai.ttf:system/fonts/DroidSansThai.ttf \
     frameworks/base/data/fonts/DroidSerif-Regular.ttf:system/fonts/DroidSerif-Regular.ttf \
     frameworks/base/data/fonts/DroidSerif-Bold.ttf:system/fonts/DroidSerif-Bold.ttf \
diff --git a/docs/html/sdk/android-3.1-highlights.jd b/docs/html/sdk/android-3.1-highlights.jd
index 3d132a3..88bc1ee 100644
--- a/docs/html/sdk/android-3.1-highlights.jd
+++ b/docs/html/sdk/android-3.1-highlights.jd
@@ -143,8 +143,8 @@
 <p>To make the platform even better for gaming, Android 3.1 adds support for
 most PC joysticks and gamepads that are connected over USB or Bluetooth HID.</p>
 
-<p>For example, users can connect Sony Playstation&trade; 3 and XBox 360&trade; game
-controllers over USB (but not Bluetooth), Logitech Dual Action&trade; gamepads and
+<p>For example, users can connect PlayStation<sup>&reg;</sup>3 and Xbox 360<sup>&reg;</sup>
+game controllers over USB (but not Bluetooth), Logitech Dual Action&trade; gamepads and
 flight sticks, or a car racing controller. Game controllers that use proprietary
 networking or pairing are not supported by default, but in general, the platform
 supports most PC-connectible joysticks and gamepads.</p>
diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java
index adb6eac..90a7ac2 100644
--- a/graphics/java/android/graphics/SurfaceTexture.java
+++ b/graphics/java/android/graphics/SurfaceTexture.java
@@ -144,10 +144,6 @@
      */
     public void updateTexImage() {
         nativeUpdateTexImage();
-        if (nativeGetQueuedCount() > 0) {
-            Message m = mEventHandler.obtainMessage();
-            mEventHandler.sendMessage(m);
-        }
     }
 
     /**
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index e558dfd..c82fb9b 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -46,11 +46,14 @@
     enum { NUM_BUFFER_SLOTS = 32 };
 
     struct FrameAvailableListener : public virtual RefBase {
-        // onFrameAvailable() is called from queueBuffer() is the FIFO is
-        // empty. You can use SurfaceTexture::getQueuedCount() to
-        // figure out if there are more frames waiting.
-        // This is called without any lock held can be called concurrently by
-        // multiple threads.
+        // onFrameAvailable() is called from queueBuffer() each time an
+        // additional frame becomes available for consumption. This means that
+        // frames that are queued while in asynchronous mode only trigger the
+        // callback if no previous frames are pending. Frames queued while in
+        // synchronous mode always trigger the callback.
+        //
+        // This is called without any lock held and can be called concurrently
+        // by multiple threads.
         virtual void onFrameAvailable() = 0;
     };
 
@@ -101,11 +104,6 @@
     // target texture belongs is bound to the calling thread.
     status_t updateTexImage();
 
-    // getqueuedCount returns the number of queued frames waiting in the
-    // FIFO. In asynchronous mode, this always returns 0 or 1 since
-    // frames are not accumulating in the FIFO.
-    size_t getQueuedCount() const;
-
     // setBufferCountServer set the buffer count. If the client has requested
     // a buffer count using setBufferCount, the server-buffer count will
     // take effect once the client sets the count back to zero.
diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h
index f2c6505..e4c1c49 100644
--- a/include/media/stagefright/MPEG2TSWriter.h
+++ b/include/media/stagefright/MPEG2TSWriter.h
@@ -31,6 +31,10 @@
     MPEG2TSWriter(int fd);
     MPEG2TSWriter(const char *filename);
 
+    MPEG2TSWriter(
+            void *cookie,
+            ssize_t (*write)(void *cookie, const void *data, size_t size));
+
     virtual status_t addSource(const sp<MediaSource> &source);
     virtual status_t start(MetaData *param = NULL);
     virtual status_t stop();
@@ -51,6 +55,10 @@
     struct SourceInfo;
 
     FILE *mFile;
+
+    void *mWriteCookie;
+    ssize_t (*mWriteFunc)(void *cookie, const void *data, size_t size);
+
     sp<ALooper> mLooper;
     sp<AHandlerReflector<MPEG2TSWriter> > mReflector;
 
@@ -69,6 +77,8 @@
     void writeProgramMap();
     void writeAccessUnit(int32_t sourceIndex, const sp<ABuffer> &buffer);
 
+    ssize_t internalWrite(const void *data, size_t size);
+
     DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSWriter);
 };
 
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 589cefd..92331a1 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -79,6 +79,13 @@
     // from MediaBufferObserver
     virtual void signalBufferReturned(MediaBuffer *buffer);
 
+    // for use by ACodec
+    static void findMatchingCodecs(
+            const char *mime,
+            bool createEncoder, const char *matchComponentName,
+            uint32_t flags,
+            Vector<String8> *matchingCodecs);
+
 protected:
     virtual ~OMXCodec();
 
@@ -311,12 +318,6 @@
     static uint32_t getComponentQuirks(
             const char *componentName, bool isEncoder);
 
-    static void findMatchingCodecs(
-            const char *mime,
-            bool createEncoder, const char *matchComponentName,
-            uint32_t flags,
-            Vector<String8> *matchingCodecs);
-
     void restorePatchedDataPointer(BufferInfo *info);
 
     status_t applyRotation();
diff --git a/include/private/surfaceflinger/LayerState.h b/include/private/surfaceflinger/LayerState.h
index d7fe572e..d2fed41 100644
--- a/include/private/surfaceflinger/LayerState.h
+++ b/include/private/surfaceflinger/LayerState.h
@@ -29,6 +29,7 @@
 namespace android {
 
 class Parcel;
+class ISurfaceComposerClient;
 
 struct layer_state_t {
 
@@ -68,6 +69,13 @@
             Region          transparentRegion;
 };
 
+struct ComposerState {
+    sp<ISurfaceComposerClient> client;
+    layer_state_t state;
+    status_t    write(Parcel& output) const;
+    status_t    read(const Parcel& input);
+};
+
 }; // namespace android
 
 #endif // ANDROID_SF_LAYER_STATE_H
diff --git a/include/surfaceflinger/ISurfaceComposer.h b/include/surfaceflinger/ISurfaceComposer.h
index 03fd01b..dba98a3 100644
--- a/include/surfaceflinger/ISurfaceComposer.h
+++ b/include/surfaceflinger/ISurfaceComposer.h
@@ -34,6 +34,7 @@
 // ----------------------------------------------------------------------------
 
 class IMemoryHeap;
+class ComposerState;
 
 class ISurfaceComposer : public IInterface
 {
@@ -105,8 +106,7 @@
     virtual sp<IMemoryHeap> getCblk() const = 0;
 
     /* open/close transactions. requires ACCESS_SURFACE_FLINGER permission */
-    virtual void openGlobalTransaction() = 0;
-    virtual void closeGlobalTransaction() = 0;
+    virtual void setTransactionState(const Vector<ComposerState>& state) = 0;
 
     /* [un]freeze display. requires ACCESS_SURFACE_FLINGER permission */
     virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags) = 0;
@@ -149,8 +149,7 @@
         CREATE_CONNECTION,
         CREATE_GRAPHIC_BUFFER_ALLOC,
         GET_CBLK,
-        OPEN_GLOBAL_TRANSACTION,
-        CLOSE_GLOBAL_TRANSACTION,
+        SET_TRANSACTION_STATE,
         SET_ORIENTATION,
         FREEZE_DISPLAY,
         UNFREEZE_DISPLAY,
diff --git a/include/surfaceflinger/ISurfaceComposerClient.h b/include/surfaceflinger/ISurfaceComposerClient.h
index 2e75a0e..6e9a654 100644
--- a/include/surfaceflinger/ISurfaceComposerClient.h
+++ b/include/surfaceflinger/ISurfaceComposerClient.h
@@ -37,8 +37,6 @@
 
 // ----------------------------------------------------------------------------
 
-class layer_state_t;
-
 class ISurfaceComposerClient : public IInterface
 {
 public:
@@ -69,11 +67,6 @@
      * Requires ACCESS_SURFACE_FLINGER permission
      */
     virtual status_t    destroySurface(SurfaceID sid) = 0;
-
-    /*
-     * Requires ACCESS_SURFACE_FLINGER permission
-     */
-    virtual status_t    setState(int32_t count, const layer_state_t* states) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/surfaceflinger/SurfaceComposerClient.h b/include/surfaceflinger/SurfaceComposerClient.h
index 140b9f8..7fbbfb24 100644
--- a/include/surfaceflinger/SurfaceComposerClient.h
+++ b/include/surfaceflinger/SurfaceComposerClient.h
@@ -37,10 +37,12 @@
 // ---------------------------------------------------------------------------
 
 class DisplayInfo;
+class Composer;
 class IMemoryHeap;
 class ISurfaceComposer;
 class Region;
 class surface_flinger_cblk_t;
+struct layer_state_t;
 
 // ---------------------------------------------------------------------------
 
@@ -59,8 +61,11 @@
 
 // ---------------------------------------------------------------------------
 
+class Composer;
+
 class SurfaceComposerClient : public RefBase
 {
+    friend class Composer;
 public:    
                 SurfaceComposerClient();
     virtual     ~SurfaceComposerClient();
@@ -101,13 +106,7 @@
     // All composer parameters must be changed within a transaction
     // several surfaces can be updated in one transaction, all changes are
     // committed at once when the transaction is closed.
-    // CloseTransaction() usually requires an IPC with the server.
-    
-    //! Open a composer transaction
-    status_t    openTransaction();
-
-    //! commit the transaction
-    status_t    closeTransaction();
+    // closeGlobalTransaction() usually requires an IPC with the server.
 
     //! Open a composer transaction on all active SurfaceComposerClients.
     static void openGlobalTransaction();
@@ -152,19 +151,12 @@
 
 private:
     virtual void onFirstRef();
-    inline layer_state_t*   get_state_l(SurfaceID id);
-    layer_state_t*          lockLayerState(SurfaceID id);
-    inline void             unlockLayerState();
+    Composer& getComposer();
 
-    mutable     Mutex                               mLock;
-                SortedVector<layer_state_t>         mStates;
-                int32_t                             mTransactionOpen;
-                layer_state_t*                      mPrebuiltLayerState;
-
-                // these don't need to be protected because they never change
-                // after assignment
+    mutable     Mutex                       mLock;
                 status_t                    mStatus;
                 sp<ISurfaceComposerClient>  mClient;
+                Composer&                   mComposer;
 };
 
 // ---------------------------------------------------------------------------
diff --git a/include/utils/LinearTransform.h b/include/utils/LinearTransform.h
new file mode 100644
index 0000000..04cb355
--- /dev/null
+++ b/include/utils/LinearTransform.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef _LIBS_UTILS_LINEAR_TRANSFORM_H
+#define _LIBS_UTILS_LINEAR_TRANSFORM_H
+
+#include <stdint.h>
+
+namespace android {
+
+// LinearTransform defines a structure which hold the definition of a
+// transformation from single dimensional coordinate system A into coordinate
+// system B (and back again).  Values in A and in B are 64 bit, the linear
+// scale factor is expressed as a rational number using two 32 bit values.
+//
+// Specifically, let
+// f(a) = b
+// F(b) = f^-1(b) = a
+// then
+//
+// f(a) = (((a - a_zero) * a_to_b_numer) / a_to_b_denom) + b_zero;
+//
+// and
+//
+// F(b) = (((b - b_zero) * a_to_b_denom) / a_to_b_numer) + a_zero;
+//
+struct LinearTransform {
+  int64_t  a_zero;
+  int64_t  b_zero;
+  int32_t  a_to_b_numer;
+  uint32_t a_to_b_denom;
+
+  // Transform from A->B
+  // Returns true on success, or false in the case of a singularity or an
+  // overflow.
+  bool doForwardTransform(int64_t a_in, int64_t* b_out) const;
+
+  // Transform from B->A
+  // Returns true on success, or false in the case of a singularity or an
+  // overflow.
+  bool doReverseTransform(int64_t b_in, int64_t* a_out) const;
+
+  // Helpers which will reduce the fraction N/D using Euclid's method.
+  template <class T> static void reduce(T* N, T* D);
+  static void reduce(int32_t* N, uint32_t* D);
+};
+
+
+}
+
+#endif  // _LIBS_UTILS_LINEAR_TRANSFORM_H
diff --git a/include/utils/SortedVector.h b/include/utils/SortedVector.h
index 8beec57..0e98aeb 100644
--- a/include/utils/SortedVector.h
+++ b/include/utils/SortedVector.h
@@ -32,6 +32,8 @@
 template <class TYPE>
 class SortedVector : private SortedVectorImpl
 {
+    friend class Vector<TYPE>;
+
 public:
             typedef TYPE    value_type;
     
diff --git a/include/utils/Vector.h b/include/utils/Vector.h
index f1e87e6..b908e2a 100644
--- a/include/utils/Vector.h
+++ b/include/utils/Vector.h
@@ -29,6 +29,9 @@
 
 namespace android {
 
+template <typename TYPE>
+class SortedVector;
+
 /*!
  * The main templated vector class ensuring type safety
  * while making use of VectorImpl.
@@ -47,13 +50,17 @@
     
                             Vector();
                             Vector(const Vector<TYPE>& rhs);
+    explicit                Vector(const SortedVector<TYPE>& rhs);
     virtual                 ~Vector();
 
     /*! copy operator */
             const Vector<TYPE>&     operator = (const Vector<TYPE>& rhs) const;
             Vector<TYPE>&           operator = (const Vector<TYPE>& rhs);    
 
-    /*
+            const Vector<TYPE>&     operator = (const SortedVector<TYPE>& rhs) const;
+            Vector<TYPE>&           operator = (const SortedVector<TYPE>& rhs);
+
+            /*
      * empty the vector
      */
 
@@ -215,6 +222,11 @@
 }
 
 template<class TYPE> inline
+Vector<TYPE>::Vector(const SortedVector<TYPE>& rhs)
+    : VectorImpl(static_cast<const VectorImpl&>(rhs)) {
+}
+
+template<class TYPE> inline
 Vector<TYPE>::~Vector() {
     finish_vector();
 }
@@ -227,6 +239,18 @@
 
 template<class TYPE> inline
 const Vector<TYPE>& Vector<TYPE>::operator = (const Vector<TYPE>& rhs) const {
+    VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
+    return *this;
+}
+
+template<class TYPE> inline
+Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) {
+    VectorImpl::operator = (static_cast<const VectorImpl&>(rhs));
+    return *this;
+}
+
+template<class TYPE> inline
+const Vector<TYPE>& Vector<TYPE>::operator = (const SortedVector<TYPE>& rhs) const {
     VectorImpl::operator = (rhs);
     return *this; 
 }
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index ab4b9e0..f75208d 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -60,16 +60,10 @@
     public static final String WIFI = "WIFI_";
 
     /** Data type for public keys. */
-    public static final String PUBLIC_KEY = "KEY";
+    public static final String EXTRA_PUBLIC_KEY = "KEY";
 
     /** Data type for private keys. */
-    public static final String PRIVATE_KEY = "PKEY";
-
-    /** Data type for certificates. */
-    public static final String CERTIFICATE = "CERT";
-
-    /** Data type for PKCS12. */
-    public static final String PKCS12 = "PKCS12";
+    public static final String EXTRA_PRIVATE_KEY = "PKEY";
 
     // historically used by Android
     public static final String EXTENSION_CRT = ".crt";
@@ -130,16 +124,9 @@
         }
     }
 
-    private Intent createInstallIntent() {
-        Intent intent = new Intent(INSTALL_ACTION);
-        intent.setClassName("com.android.certinstaller",
-                "com.android.certinstaller.CertInstallerMain");
-        return intent;
-    }
-
     public void install(Context context) {
         try {
-            Intent intent = createInstallIntent();
+            Intent intent = KeyChain.createInstallIntent();
             context.startActivity(intent);
         } catch (ActivityNotFoundException e) {
             Log.w(LOGTAG, e.toString());
@@ -148,9 +135,9 @@
 
     public void install(Context context, KeyPair pair) {
         try {
-            Intent intent = createInstallIntent();
-            intent.putExtra(PRIVATE_KEY, pair.getPrivate().getEncoded());
-            intent.putExtra(PUBLIC_KEY, pair.getPublic().getEncoded());
+            Intent intent = KeyChain.createInstallIntent();
+            intent.putExtra(EXTRA_PRIVATE_KEY, pair.getPrivate().getEncoded());
+            intent.putExtra(EXTRA_PUBLIC_KEY, pair.getPublic().getEncoded());
             context.startActivity(intent);
         } catch (ActivityNotFoundException e) {
             Log.w(LOGTAG, e.toString());
@@ -159,7 +146,7 @@
 
     public void install(Context context, String type, byte[] value) {
         try {
-            Intent intent = createInstallIntent();
+            Intent intent = KeyChain.createInstallIntent();
             intent.putExtra(type, value);
             context.startActivity(intent);
         } catch (ActivityNotFoundException e) {
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index 18011e6..b567207 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -89,31 +89,117 @@
     public static final String ACCOUNT_TYPE = "com.android.keychain";
 
     /**
+     * Action to bring up the KeyChainActivity
+     */
+    private static final String ACTION_CHOOSER = "com.android.keychain.CHOOSER";
+
+    /**
+     * Extra for use with {@link #ACTION_CHOOSER}
      * @hide Also used by KeyChainActivity implementation
      */
     public static final String EXTRA_RESPONSE = "response";
 
     /**
+     * Extra for use with {@link #ACTION_CHOOSER}
      * @hide Also used by KeyChainActivity implementation
      */
     public static final String EXTRA_HOST = "host";
 
     /**
+     * Extra for use with {@link #ACTION_CHOOSER}
      * @hide Also used by KeyChainActivity implementation
      */
     public static final String EXTRA_PORT = "port";
 
     /**
+     * Extra for use with {@link #ACTION_CHOOSER}
      * @hide Also used by KeyChainActivity implementation
      */
     public static final String EXTRA_ALIAS = "alias";
 
     /**
+     * Extra for use with {@link #ACTION_CHOOSER}
      * @hide Also used by KeyChainActivity implementation
      */
     public static final String EXTRA_SENDER = "sender";
 
     /**
+     * Action to bring up the CertInstaller
+     */
+    private static final String ACTION_INSTALL = "android.credentials.INSTALL";
+
+    /**
+     * Optional extra to specify a {@code String} credential name on
+     * the {@code Intent} returned by {@link #createInstallIntent}.
+     *
+     * @hide TODO make public
+     */
+    // Compatible with old com.android.certinstaller.CredentialHelper.CERT_NAME_KEY
+    public static final String EXTRA_NAME = "name";
+
+    /**
+     * Optional extra to specify an X.509 certificate to install on
+     * the {@code Intent} returned by {@link #createInstallIntent}.
+     * The extra value should be a PEM or ASN.1 DER encoded {@code
+     * byte[]}. An {@link X509Certificate} can be converted to DER
+     * encoded bytes with {@link X509Certificate#getEncoded}.
+     *
+     * <p>{@link #EXTRA_NAME} may be used to provide a default alias
+     * name for the installed certificate.
+     *
+     * @hide TODO make public
+     */
+    // Compatible with old android.security.Credentials.CERTIFICATE
+    public static final String EXTRA_CERTIFICATE = "CERT";
+
+    /**
+     * Optional extra for use with the {@code Intent} returned by
+     * {@link #createInstallIntent} to specify a PKCS#12 key store to
+     * install. The extra value should be a {@code byte[]}. The bytes
+     * may come from an external source or be generated with {@link
+     * KeyStore#store} on a "PKCS12" instance.
+     *
+     * <p>The user will be prompted for the password to load the key store.
+     *
+     * <p>The key store will be scanned for {@link
+     * java.security.KeyStore.PrivateKeyEntry} entries and both the
+     * private key and associated certificate chain will be installed.
+     *
+     * <p>{@link #EXTRA_NAME} may be used to provide a default alias
+     * name for the installed credentials.
+     *
+     * @hide TODO make public
+     */
+    // Compatible with old android.security.Credentials.PKCS12
+    public static final String EXTRA_PKCS12 = "PKCS12";
+
+    /**
+     * Returns an {@code Intent} that can be used for credential
+     * installation. The intent may be used without any extras, in
+     * which case the user will be able to install credentials from
+     * their own source.
+     *
+     * <p>Alternatively, {@link #EXTRA_CERTIFICATE} or {@link
+     * #EXTRA_PKCS12} maybe used to specify the bytes of an X.509
+     * certificate or a PKCS#12 key store for installation. These
+     * extras may be combined with {@link EXTRA_NAME} to provide a
+     * default alias name for credentials being installed.
+     *
+     * <p>When used with {@link Activity#startActivityForResult},
+     * {@link Activity#RESULT_OK} will be returned if a credential was
+     * successfully installed, otherwise {@link
+     * Activity#RESULT_CANCELED} will be returned.
+     *
+     * @hide TODO make public with createInstallIntent, EXTRA_NAME, EXTRA_CERTIFICATE, EXTRA_PKCS12
+     */
+    public static Intent createInstallIntent() {
+        Intent intent = new Intent(ACTION_INSTALL);
+        intent.setClassName("com.android.certinstaller",
+                            "com.android.certinstaller.CertInstallerMain");
+        return intent;
+    }
+
+    /**
      * Launches an {@code Activity} for the user to select the alias
      * for a private key and certificate pair for authentication. The
      * selected alias or null will be returned via the
@@ -176,7 +262,7 @@
         if (response == null) {
             throw new NullPointerException("response == null");
         }
-        Intent intent = new Intent("com.android.keychain.CHOOSER");
+        Intent intent = new Intent(ACTION_CHOOSER);
         intent.putExtra(EXTRA_RESPONSE, new AliasResponse(activity, response));
         intent.putExtra(EXTRA_HOST, host);
         intent.putExtra(EXTRA_PORT, port);
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index 40450a3..c1156d5 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -25,6 +25,8 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 
+#include <private/surfaceflinger/LayerState.h>
+
 #include <surfaceflinger/ISurfaceComposer.h>
 
 #include <ui/DisplayInfo.h>
@@ -74,18 +76,17 @@
         return interface_cast<IMemoryHeap>(reply.readStrongBinder());
     }
 
-    virtual void openGlobalTransaction()
+    virtual void setTransactionState(const Vector<ComposerState>& state)
     {
         Parcel data, reply;
         data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        remote()->transact(BnSurfaceComposer::OPEN_GLOBAL_TRANSACTION, data, &reply);
-    }
-
-    virtual void closeGlobalTransaction()
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
-        remote()->transact(BnSurfaceComposer::CLOSE_GLOBAL_TRANSACTION, data, &reply);
+        Vector<ComposerState>::const_iterator b(state.begin());
+        Vector<ComposerState>::const_iterator e(state.end());
+        data.writeInt32(state.size());
+        for ( ; b != e ; ++b ) {
+            b->write(data);
+        }
+        remote()->transact(BnSurfaceComposer::SET_TRANSACTION_STATE, data, &reply);
     }
 
     virtual status_t freezeDisplay(DisplayID dpy, uint32_t flags)
@@ -218,13 +219,17 @@
             sp<IBinder> b = createGraphicBufferAlloc()->asBinder();
             reply->writeStrongBinder(b);
         } break;
-        case OPEN_GLOBAL_TRANSACTION: {
+        case SET_TRANSACTION_STATE: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            openGlobalTransaction();
-        } break;
-        case CLOSE_GLOBAL_TRANSACTION: {
-            CHECK_INTERFACE(ISurfaceComposer, data, reply);
-            closeGlobalTransaction();
+            size_t count = data.readInt32();
+            ComposerState s;
+            Vector<ComposerState> state;
+            state.setCapacity(count);
+            for (size_t i=0 ; i<count ; i++) {
+                s.read(data);
+                state.add(s);
+            }
+            setTransactionState(state);
         } break;
         case SET_ORIENTATION: {
             CHECK_INTERFACE(ISurfaceComposer, data, reply);
diff --git a/libs/gui/ISurfaceComposerClient.cpp b/libs/gui/ISurfaceComposerClient.cpp
index 8d83392..bc97cac 100644
--- a/libs/gui/ISurfaceComposerClient.cpp
+++ b/libs/gui/ISurfaceComposerClient.cpp
@@ -51,8 +51,7 @@
 
 enum {
     CREATE_SURFACE = IBinder::FIRST_CALL_TRANSACTION,
-    DESTROY_SURFACE,
-    SET_STATE
+    DESTROY_SURFACE
 };
 
 class BpSurfaceComposerClient : public BpInterface<ISurfaceComposerClient>
@@ -92,17 +91,6 @@
         remote()->transact(DESTROY_SURFACE, data, &reply);
         return reply.readInt32();
     }
-
-    virtual status_t setState(int32_t count, const layer_state_t* states)
-    {
-        Parcel data, reply;
-        data.writeInterfaceToken(ISurfaceComposerClient::getInterfaceDescriptor());
-        data.writeInt32(count);
-        for (int i=0 ; i<count ; i++)
-            states[i].write(data);
-        remote()->transact(SET_STATE, data, &reply);
-        return reply.readInt32();
-    }
 };
 
 IMPLEMENT_META_INTERFACE(SurfaceComposerClient, "android.ui.ISurfaceComposerClient");
@@ -133,17 +121,6 @@
             reply->writeInt32( destroySurface( data.readInt32() ) );
             return NO_ERROR;
         } break;
-        case SET_STATE: {
-            CHECK_INTERFACE(ISurfaceComposerClient, data, reply);
-            int32_t count = data.readInt32();
-            layer_state_t* states = new layer_state_t[count];
-            for (int i=0 ; i<count ; i++)
-                states[i].read(data);
-            status_t err = setState(count, states);
-            delete [] states;
-            reply->writeInt32(err);
-            return NO_ERROR;
-        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp
index 01c4c7e..87901e8 100644
--- a/libs/gui/LayerState.cpp
+++ b/libs/gui/LayerState.cpp
@@ -17,6 +17,7 @@
 #include <utils/Errors.h>
 #include <binder/Parcel.h>
 #include <private/surfaceflinger/LayerState.h>
+#include <surfaceflinger/ISurfaceComposerClient.h>
 
 namespace android {
 
@@ -58,4 +59,14 @@
     return NO_ERROR;
 }
 
+status_t ComposerState::write(Parcel& output) const {
+    output.writeStrongBinder(client->asBinder());
+    return state.write(output);
+}
+
+status_t ComposerState::read(const Parcel& input) {
+    client = interface_cast<ISurfaceComposerClient>(input.readStrongBinder());
+    return state.read(input);
+}
+
 }; // namespace android
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 1678711..8cead80 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -74,75 +74,52 @@
 
 // ---------------------------------------------------------------------------
 
+// NOTE: this is NOT a member function (it's a friend defined with its
+// declaration).
+static inline
+int compare_type( const ComposerState& lhs, const ComposerState& rhs) {
+    if (lhs.client < rhs.client)  return -1;
+    if (lhs.client > rhs.client)  return 1;
+    if (lhs.state.surface < rhs.state.surface)  return -1;
+    if (lhs.state.surface > rhs.state.surface)  return 1;
+    return 0;
+}
+
 class Composer : public Singleton<Composer>
 {
-    Mutex mLock;
-    SortedVector< wp<SurfaceComposerClient> > mActiveConnections;
-    SortedVector<sp<SurfaceComposerClient> > mOpenTransactions;
-
-    Composer() : Singleton<Composer>() {
-    }
-
-    void addClientImpl(const sp<SurfaceComposerClient>& client) {
-        Mutex::Autolock _l(mLock);
-        mActiveConnections.add(client);
-    }
-
-    void removeClientImpl(const sp<SurfaceComposerClient>& client) {
-        Mutex::Autolock _l(mLock);
-        mActiveConnections.remove(client);
-    }
-
-    void openGlobalTransactionImpl()
-    {
-        Mutex::Autolock _l(mLock);
-        if (mOpenTransactions.size()) {
-            LOGE("openGlobalTransaction() called more than once. skipping.");
-            return;
-        }
-        const size_t N = mActiveConnections.size();
-        for (size_t i=0; i<N; i++) {
-            sp<SurfaceComposerClient> client(mActiveConnections[i].promote());
-            if (client != 0 && mOpenTransactions.indexOf(client) < 0) {
-                if (client->openTransaction() == NO_ERROR) {
-                    mOpenTransactions.add(client);
-                } else {
-                    LOGE("openTransaction on client %p failed", client.get());
-                    // let it go, it'll fail later when the user
-                    // tries to do something with the transaction
-                }
-            }
-        }
-    }
-
-    void closeGlobalTransactionImpl()
-    {
-        mLock.lock();
-            SortedVector< sp<SurfaceComposerClient> > clients(mOpenTransactions);
-            mOpenTransactions.clear();
-        mLock.unlock();
-
-        sp<ISurfaceComposer> sm(getComposerService());
-        sm->openGlobalTransaction();
-            const size_t N = clients.size();
-            for (size_t i=0; i<N; i++) {
-                clients[i]->closeTransaction();
-            }
-        sm->closeGlobalTransaction();
-    }
-
     friend class Singleton<Composer>;
 
+    mutable Mutex               mLock;
+    SortedVector<ComposerState> mStates;
+
+    Composer() : Singleton<Composer>() { }
+
+    void closeGlobalTransactionImpl();
+
+    layer_state_t* getLayerStateLocked(
+            const sp<SurfaceComposerClient>& client, SurfaceID id);
+
 public:
-    static void addClient(const sp<SurfaceComposerClient>& client) {
-        Composer::getInstance().addClientImpl(client);
-    }
-    static void removeClient(const sp<SurfaceComposerClient>& client) {
-        Composer::getInstance().removeClientImpl(client);
-    }
-    static void openGlobalTransaction() {
-        Composer::getInstance().openGlobalTransactionImpl();
-    }
+
+    status_t setPosition(const sp<SurfaceComposerClient>& client, SurfaceID id,
+            int32_t x, int32_t y);
+    status_t setSize(const sp<SurfaceComposerClient>& client, SurfaceID id,
+            uint32_t w, uint32_t h);
+    status_t setLayer(const sp<SurfaceComposerClient>& client, SurfaceID id,
+            int32_t z);
+    status_t setFlags(const sp<SurfaceComposerClient>& client, SurfaceID id,
+            uint32_t flags, uint32_t mask);
+    status_t setTransparentRegionHint(
+            const sp<SurfaceComposerClient>& client, SurfaceID id,
+            const Region& transparentRegion);
+    status_t setAlpha(const sp<SurfaceComposerClient>& client, SurfaceID id,
+            float alpha);
+    status_t setMatrix(const sp<SurfaceComposerClient>& client, SurfaceID id,
+            float dsdx, float dtdx, float dsdy, float dtdy);
+    status_t setFreezeTint(
+            const sp<SurfaceComposerClient>& client, SurfaceID id,
+            uint32_t tint);
+
     static void closeGlobalTransaction() {
         Composer::getInstance().closeGlobalTransactionImpl();
     }
@@ -152,69 +129,306 @@
 
 // ---------------------------------------------------------------------------
 
-static inline int compare_type( const layer_state_t& lhs,
-                                const layer_state_t& rhs) {
-    if (lhs.surface < rhs.surface)  return -1;
-    if (lhs.surface > rhs.surface)  return 1;
-    return 0;
+void Composer::closeGlobalTransactionImpl() {
+    sp<ISurfaceComposer> sm(getComposerService());
+
+    Vector<ComposerState> transaction;
+
+    { // scope for the lock
+        Mutex::Autolock _l(mLock);
+        transaction = mStates;
+        mStates.clear();
+    }
+
+   sm->setTransactionState(transaction);
 }
 
+layer_state_t* Composer::getLayerStateLocked(
+        const sp<SurfaceComposerClient>& client, SurfaceID id) {
+
+    ComposerState s;
+    s.client = client->mClient;
+    s.state.surface = id;
+
+    ssize_t index = mStates.indexOf(s);
+    if (index < 0) {
+        // we don't have it, add an initialized layer_state to our list
+        index = mStates.add(s);
+    }
+
+    ComposerState* const out = mStates.editArray();
+    return &(out[index].state);
+}
+
+status_t Composer::setPosition(const sp<SurfaceComposerClient>& client,
+        SurfaceID id, int32_t x, int32_t y) {
+    Mutex::Autolock _l(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s)
+        return BAD_INDEX;
+    s->what |= ISurfaceComposer::ePositionChanged;
+    s->x = x;
+    s->y = y;
+    return NO_ERROR;
+}
+
+status_t Composer::setSize(const sp<SurfaceComposerClient>& client,
+        SurfaceID id, uint32_t w, uint32_t h) {
+    Mutex::Autolock _l(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s)
+        return BAD_INDEX;
+    s->what |= ISurfaceComposer::eSizeChanged;
+    s->w = w;
+    s->h = h;
+    return NO_ERROR;
+}
+
+status_t Composer::setLayer(const sp<SurfaceComposerClient>& client,
+        SurfaceID id, int32_t z) {
+    Mutex::Autolock _l(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s)
+        return BAD_INDEX;
+    s->what |= ISurfaceComposer::eLayerChanged;
+    s->z = z;
+    return NO_ERROR;
+}
+
+status_t Composer::setFlags(const sp<SurfaceComposerClient>& client,
+        SurfaceID id, uint32_t flags,
+        uint32_t mask) {
+    Mutex::Autolock _l(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s)
+        return BAD_INDEX;
+    s->what |= ISurfaceComposer::eVisibilityChanged;
+    s->flags &= ~mask;
+    s->flags |= (flags & mask);
+    s->mask |= mask;
+    return NO_ERROR;
+}
+
+status_t Composer::setTransparentRegionHint(
+        const sp<SurfaceComposerClient>& client, SurfaceID id,
+        const Region& transparentRegion) {
+    Mutex::Autolock _l(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s)
+        return BAD_INDEX;
+    s->what |= ISurfaceComposer::eTransparentRegionChanged;
+    s->transparentRegion = transparentRegion;
+    return NO_ERROR;
+}
+
+status_t Composer::setAlpha(const sp<SurfaceComposerClient>& client,
+        SurfaceID id, float alpha) {
+    Mutex::Autolock _l(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s)
+        return BAD_INDEX;
+    s->what |= ISurfaceComposer::eAlphaChanged;
+    s->alpha = alpha;
+    return NO_ERROR;
+}
+
+status_t Composer::setMatrix(const sp<SurfaceComposerClient>& client,
+        SurfaceID id, float dsdx, float dtdx,
+        float dsdy, float dtdy) {
+    Mutex::Autolock _l(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s)
+        return BAD_INDEX;
+    s->what |= ISurfaceComposer::eMatrixChanged;
+    layer_state_t::matrix22_t matrix;
+    matrix.dsdx = dsdx;
+    matrix.dtdx = dtdx;
+    matrix.dsdy = dsdy;
+    matrix.dtdy = dtdy;
+    s->matrix = matrix;
+    return NO_ERROR;
+}
+
+status_t Composer::setFreezeTint(const sp<SurfaceComposerClient>& client,
+        SurfaceID id, uint32_t tint) {
+    Mutex::Autolock _l(mLock);
+    layer_state_t* s = getLayerStateLocked(client, id);
+    if (!s)
+        return BAD_INDEX;
+    s->what |= ISurfaceComposer::eFreezeTintChanged;
+    s->tint = tint;
+    return NO_ERROR;
+}
+
+// ---------------------------------------------------------------------------
+
 SurfaceComposerClient::SurfaceComposerClient()
-    : mTransactionOpen(0), mPrebuiltLayerState(0), mStatus(NO_INIT)
+    : mStatus(NO_INIT), mComposer(Composer::getInstance())
 {
 }
 
-void SurfaceComposerClient::onFirstRef()
-{
+void SurfaceComposerClient::onFirstRef() {
     sp<ISurfaceComposer> sm(getComposerService());
     if (sm != 0) {
         sp<ISurfaceComposerClient> conn = sm->createConnection();
         if (conn != 0) {
             mClient = conn;
-            Composer::addClient(this);
-            mPrebuiltLayerState = new layer_state_t;
             mStatus = NO_ERROR;
         }
     }
 }
 
-SurfaceComposerClient::~SurfaceComposerClient()
-{
-    delete mPrebuiltLayerState;
+SurfaceComposerClient::~SurfaceComposerClient() {
     dispose();
 }
 
-status_t SurfaceComposerClient::initCheck() const
-{
+status_t SurfaceComposerClient::initCheck() const {
     return mStatus;
 }
 
-sp<IBinder> SurfaceComposerClient::connection() const
-{
+sp<IBinder> SurfaceComposerClient::connection() const {
     return (mClient != 0) ? mClient->asBinder() : 0;
 }
 
 status_t SurfaceComposerClient::linkToComposerDeath(
         const sp<IBinder::DeathRecipient>& recipient,
-        void* cookie, uint32_t flags)
-{
+        void* cookie, uint32_t flags) {
     sp<ISurfaceComposer> sm(getComposerService());
     return sm->asBinder()->linkToDeath(recipient, cookie, flags);
 }
 
-void SurfaceComposerClient::dispose()
-{
+void SurfaceComposerClient::dispose() {
     // this can be called more than once.
     sp<ISurfaceComposerClient> client;
     Mutex::Autolock _lm(mLock);
     if (mClient != 0) {
-        Composer::removeClient(this);
         client = mClient; // hold ref while lock is held
         mClient.clear();
     }
     mStatus = NO_INIT;
 }
 
+sp<SurfaceControl> SurfaceComposerClient::createSurface(
+        DisplayID display,
+        uint32_t w,
+        uint32_t h,
+        PixelFormat format,
+        uint32_t flags)
+{
+    String8 name;
+    const size_t SIZE = 128;
+    char buffer[SIZE];
+    snprintf(buffer, SIZE, "<pid_%d>", getpid());
+    name.append(buffer);
+
+    return SurfaceComposerClient::createSurface(name, display,
+            w, h, format, flags);
+}
+
+sp<SurfaceControl> SurfaceComposerClient::createSurface(
+        const String8& name,
+        DisplayID display,
+        uint32_t w,
+        uint32_t h,
+        PixelFormat format,
+        uint32_t flags)
+{
+    sp<SurfaceControl> result;
+    if (mStatus == NO_ERROR) {
+        ISurfaceComposerClient::surface_data_t data;
+        sp<ISurface> surface = mClient->createSurface(&data, name,
+                display, w, h, format, flags);
+        if (surface != 0) {
+            result = new SurfaceControl(this, surface, data, w, h, format, flags);
+        }
+    }
+    return result;
+}
+
+status_t SurfaceComposerClient::destroySurface(SurfaceID sid) {
+    if (mStatus != NO_ERROR)
+        return mStatus;
+    status_t err = mClient->destroySurface(sid);
+    return err;
+}
+
+inline Composer& SurfaceComposerClient::getComposer() {
+    return mComposer;
+}
+
+// ----------------------------------------------------------------------------
+
+void SurfaceComposerClient::openGlobalTransaction() {
+    // Currently a no-op
+}
+
+void SurfaceComposerClient::closeGlobalTransaction() {
+    Composer::closeGlobalTransaction();
+}
+
+// ----------------------------------------------------------------------------
+
+status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint) {
+    return getComposer().setFreezeTint(this, id, tint);
+}
+
+status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y) {
+    return getComposer().setPosition(this, id, x, y);
+}
+
+status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h) {
+    return getComposer().setSize(this, id, w, h);
+}
+
+status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z) {
+    return getComposer().setLayer(this, id, z);
+}
+
+status_t SurfaceComposerClient::hide(SurfaceID id) {
+    return getComposer().setFlags(this, id,
+            ISurfaceComposer::eLayerHidden,
+            ISurfaceComposer::eLayerHidden);
+}
+
+status_t SurfaceComposerClient::show(SurfaceID id, int32_t) {
+    return getComposer().setFlags(this, id,
+            0,
+            ISurfaceComposer::eLayerHidden);
+}
+
+status_t SurfaceComposerClient::freeze(SurfaceID id) {
+    return getComposer().setFlags(this, id,
+            ISurfaceComposer::eLayerFrozen,
+            ISurfaceComposer::eLayerFrozen);
+}
+
+status_t SurfaceComposerClient::unfreeze(SurfaceID id) {
+    return getComposer().setFlags(this, id,
+            0,
+            ISurfaceComposer::eLayerFrozen);
+}
+
+status_t SurfaceComposerClient::setFlags(SurfaceID id, uint32_t flags,
+        uint32_t mask) {
+    return getComposer().setFlags(this, id, flags, mask);
+}
+
+status_t SurfaceComposerClient::setTransparentRegionHint(SurfaceID id,
+        const Region& transparentRegion) {
+    return getComposer().setTransparentRegionHint(this, id, transparentRegion);
+}
+
+status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha) {
+    return getComposer().setAlpha(this, id, alpha);
+}
+
+status_t SurfaceComposerClient::setMatrix(SurfaceID id, float dsdx, float dtdx,
+        float dsdy, float dtdy) {
+    return getComposer().setMatrix(this, id, dsdx, dtdx, dsdy, dtdy);
+}
+
+// ----------------------------------------------------------------------------
+
 status_t SurfaceComposerClient::getDisplayInfo(
         DisplayID dpy, DisplayInfo* info)
 {
@@ -273,70 +487,7 @@
     return n;
 }
 
-sp<SurfaceControl> SurfaceComposerClient::createSurface(
-        DisplayID display,
-        uint32_t w,
-        uint32_t h,
-        PixelFormat format,
-        uint32_t flags)
-{
-    String8 name;
-    const size_t SIZE = 128;
-    char buffer[SIZE];
-    snprintf(buffer, SIZE, "<pid_%d>", getpid());
-    name.append(buffer);
-
-    return SurfaceComposerClient::createSurface(name, display,
-            w, h, format, flags);
-}
-
-sp<SurfaceControl> SurfaceComposerClient::createSurface(
-        const String8& name,
-        DisplayID display,
-        uint32_t w,
-        uint32_t h,
-        PixelFormat format,
-        uint32_t flags)
-{
-    sp<SurfaceControl> result;
-    if (mStatus == NO_ERROR) {
-        ISurfaceComposerClient::surface_data_t data;
-        sp<ISurface> surface = mClient->createSurface(&data, name,
-                display, w, h, format, flags);
-        if (surface != 0) {
-            result = new SurfaceControl(this, surface, data, w, h, format, flags);
-        }
-    }
-    return result;
-}
-
-status_t SurfaceComposerClient::destroySurface(SurfaceID sid)
-{
-    if (mStatus != NO_ERROR)
-        return mStatus;
-
-    // it's okay to destroy a surface while a transaction is open,
-    // (transactions really are a client-side concept)
-    // however, this indicates probably a misuse of the API or a bug
-    // in the client code.
-    LOGW_IF(mTransactionOpen,
-         "Destroying surface while a transaction is open. "
-         "Client %p: destroying surface %d, mTransactionOpen=%d",
-         this, sid, mTransactionOpen);
-
-    status_t err = mClient->destroySurface(sid);
-    return err;
-}
-
-void SurfaceComposerClient::openGlobalTransaction()
-{
-    Composer::openGlobalTransaction();
-}
-
-void SurfaceComposerClient::closeGlobalTransaction()
-{
-    Composer::closeGlobalTransaction();
-}
+// ----------------------------------------------------------------------------
 
 status_t SurfaceComposerClient::freezeDisplay(DisplayID dpy, uint32_t flags)
 {
@@ -350,199 +501,13 @@
     return sm->unfreezeDisplay(dpy, flags);
 }
 
-int SurfaceComposerClient::setOrientation(DisplayID dpy, 
+int SurfaceComposerClient::setOrientation(DisplayID dpy,
         int orientation, uint32_t flags)
 {
     sp<ISurfaceComposer> sm(getComposerService());
     return sm->setOrientation(dpy, orientation, flags);
 }
 
-status_t SurfaceComposerClient::openTransaction()
-{
-    if (mStatus != NO_ERROR)
-        return mStatus;
-    Mutex::Autolock _l(mLock);
-    mTransactionOpen++;
-    return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::closeTransaction()
-{
-    if (mStatus != NO_ERROR)
-        return mStatus;
-
-    Mutex::Autolock _l(mLock);
-    if (mTransactionOpen <= 0) {
-        LOGE(   "closeTransaction (client %p, mTransactionOpen=%d) "
-                "called more times than openTransaction()",
-                this, mTransactionOpen);
-        return INVALID_OPERATION;
-    }
-
-    if (mTransactionOpen >= 2) {
-        mTransactionOpen--;
-        return NO_ERROR;
-    }
-
-    mTransactionOpen = 0;
-    const ssize_t count = mStates.size();
-    if (count) {
-        mClient->setState(count, mStates.array());
-        mStates.clear();
-    }
-    return NO_ERROR;
-}
-
-layer_state_t* SurfaceComposerClient::get_state_l(SurfaceID index)
-{
-    // API usage error, do nothing.
-    if (mTransactionOpen<=0) {
-        LOGE("Not in transaction (client=%p, SurfaceID=%d, mTransactionOpen=%d",
-                this, int(index), mTransactionOpen);
-        return 0;
-    }
-
-    // use mPrebuiltLayerState just to find out if we already have it
-    layer_state_t& dummy(*mPrebuiltLayerState);
-    dummy.surface = index;
-    ssize_t i = mStates.indexOf(dummy);
-    if (i < 0) {
-        // we don't have it, add an initialized layer_state to our list
-        i = mStates.add(dummy);
-    }
-    return mStates.editArray() + i;
-}
-
-layer_state_t* SurfaceComposerClient::lockLayerState(SurfaceID id)
-{
-    layer_state_t* s;
-    mLock.lock();
-    s = get_state_l(id);
-    if (!s) mLock.unlock();
-    return s;
-}
-
-void SurfaceComposerClient::unlockLayerState()
-{
-    mLock.unlock();
-}
-
-status_t SurfaceComposerClient::setPosition(SurfaceID id, int32_t x, int32_t y)
-{
-    layer_state_t* s = lockLayerState(id);
-    if (!s) return BAD_INDEX;
-    s->what |= ISurfaceComposer::ePositionChanged;
-    s->x = x;
-    s->y = y;
-    unlockLayerState();
-    return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::setSize(SurfaceID id, uint32_t w, uint32_t h)
-{
-    layer_state_t* s = lockLayerState(id);
-    if (!s) return BAD_INDEX;
-    s->what |= ISurfaceComposer::eSizeChanged;
-    s->w = w;
-    s->h = h;
-    unlockLayerState();
-    return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::setLayer(SurfaceID id, int32_t z)
-{
-    layer_state_t* s = lockLayerState(id);
-    if (!s) return BAD_INDEX;
-    s->what |= ISurfaceComposer::eLayerChanged;
-    s->z = z;
-    unlockLayerState();
-    return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::hide(SurfaceID id)
-{
-    return setFlags(id, ISurfaceComposer::eLayerHidden,
-            ISurfaceComposer::eLayerHidden);
-}
-
-status_t SurfaceComposerClient::show(SurfaceID id, int32_t)
-{
-    return setFlags(id, 0, ISurfaceComposer::eLayerHidden);
-}
-
-status_t SurfaceComposerClient::freeze(SurfaceID id)
-{
-    return setFlags(id, ISurfaceComposer::eLayerFrozen,
-            ISurfaceComposer::eLayerFrozen);
-}
-
-status_t SurfaceComposerClient::unfreeze(SurfaceID id)
-{
-    return setFlags(id, 0, ISurfaceComposer::eLayerFrozen);
-}
-
-status_t SurfaceComposerClient::setFlags(SurfaceID id,
-        uint32_t flags, uint32_t mask)
-{
-    layer_state_t* s = lockLayerState(id);
-    if (!s) return BAD_INDEX;
-    s->what |= ISurfaceComposer::eVisibilityChanged;
-    s->flags &= ~mask;
-    s->flags |= (flags & mask);
-    s->mask |= mask;
-    unlockLayerState();
-    return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::setTransparentRegionHint(
-        SurfaceID id, const Region& transparentRegion)
-{
-    layer_state_t* s = lockLayerState(id);
-    if (!s) return BAD_INDEX;
-    s->what |= ISurfaceComposer::eTransparentRegionChanged;
-    s->transparentRegion = transparentRegion;
-    unlockLayerState();
-    return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::setAlpha(SurfaceID id, float alpha)
-{
-    layer_state_t* s = lockLayerState(id);
-    if (!s) return BAD_INDEX;
-    s->what |= ISurfaceComposer::eAlphaChanged;
-    s->alpha = alpha;
-    unlockLayerState();
-    return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::setMatrix(
-        SurfaceID id,
-        float dsdx, float dtdx,
-        float dsdy, float dtdy )
-{
-    layer_state_t* s = lockLayerState(id);
-    if (!s) return BAD_INDEX;
-    s->what |= ISurfaceComposer::eMatrixChanged;
-    layer_state_t::matrix22_t matrix;
-    matrix.dsdx = dsdx;
-    matrix.dtdx = dtdx;
-    matrix.dsdy = dsdy;
-    matrix.dtdy = dtdy;
-    s->matrix = matrix;
-    unlockLayerState();
-    return NO_ERROR;
-}
-
-status_t SurfaceComposerClient::setFreezeTint(SurfaceID id, uint32_t tint)
-{
-    layer_state_t* s = lockLayerState(id);
-    if (!s) return BAD_INDEX;
-    s->what |= ISurfaceComposer::eFreezeTintChanged;
-    s->tint = tint;
-    unlockLayerState();
-    return NO_ERROR;
-}
-
 // ----------------------------------------------------------------------------
 
 ScreenshotClient::ScreenshotClient()
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 37e6d11..0925001 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -417,17 +417,22 @@
             return -EINVAL;
         }
 
-        if (mQueue.empty()) {
-            listener = mFrameAvailableListener;
-        }
-
         if (mSynchronousMode) {
-            // in synchronous mode we queue all buffers in a FIFO
+            // In synchronous mode we queue all buffers in a FIFO.
             mQueue.push_back(buf);
+
+            // Synchronous mode always signals that an additional frame should
+            // be consumed.
+            listener = mFrameAvailableListener;
         } else {
-            // in asynchronous mode we only keep the most recent buffer
+            // In asynchronous mode we only keep the most recent buffer.
             if (mQueue.empty()) {
                 mQueue.push_back(buf);
+
+                // Asynchronous mode only signals that a frame should be
+                // consumed if no previous frame was pending. If a frame were
+                // pending then the consumer would have already been notified.
+                listener = mFrameAvailableListener;
             } else {
                 Fifo::iterator front(mQueue.begin());
                 // buffer currently queued is freed
@@ -483,24 +488,14 @@
 
 status_t SurfaceTexture::updateTexImage() {
     LOGV("SurfaceTexture::updateTexImage");
-
     Mutex::Autolock lock(mMutex);
 
-    int buf = mCurrentTexture;
+    // In asynchronous mode the list is guaranteed to be one buffer
+    // deep, while in synchronous mode we use the oldest buffer.
     if (!mQueue.empty()) {
-        // in asynchronous mode the list is guaranteed to be one buffer deep,
-        // while in synchronous mode we use the oldest buffer
         Fifo::iterator front(mQueue.begin());
-        buf = *front;
-        mQueue.erase(front);
-        if (mQueue.isEmpty()) {
-            mDequeueCondition.signal();
-        }
-    }
+        int buf = *front;
 
-    // Initially both mCurrentTexture and buf are INVALID_BUFFER_SLOT,
-    // so this check will fail until a buffer gets queued.
-    if (mCurrentTexture != buf) {
         // Update the GL texture object.
         EGLImageKHR image = mSlots[buf].mEglImage;
         if (image == EGL_NO_IMAGE_KHR) {
@@ -538,7 +533,7 @@
         }
 
         if (mCurrentTexture != INVALID_BUFFER_SLOT) {
-            // the current buffer becomes FREE if it was still in the queued
+            // The current buffer becomes FREE if it was still in the queued
             // state. If it has already been given to the client
             // (synchronous mode), then it stays in DEQUEUED state.
             if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED)
@@ -553,17 +548,17 @@
         mCurrentTransform = mSlots[buf].mTransform;
         mCurrentTimestamp = mSlots[buf].mTimestamp;
         computeCurrentTransformMatrix();
+
+        // Now that we've passed the point at which failures can happen,
+        // it's safe to remove the buffer from the front of the queue.
+        mQueue.erase(front);
         mDequeueCondition.signal();
     } else {
         // We always bind the texture even if we don't update its contents.
         glBindTexture(mCurrentTextureTarget, mTexName);
     }
-    return OK;
-}
 
-size_t SurfaceTexture::getQueuedCount() const {
-    Mutex::Autolock lock(mMutex);
-    return mQueue.size();
+    return OK;
 }
 
 bool SurfaceTexture::isExternalFormat(uint32_t format)
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index f219639..c06400e 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -84,10 +84,10 @@
             ASSERT_TRUE(mSurfaceControl != NULL);
             ASSERT_TRUE(mSurfaceControl->isValid());
 
-            ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction());
+            SurfaceComposerClient::openGlobalTransaction();
             ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF));
             ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
-            ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction());
+            SurfaceComposerClient::closeGlobalTransaction();
 
             sp<ANativeWindow> window = mSurfaceControl->getSurface();
             mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
@@ -419,6 +419,31 @@
         ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
     }
 
+    class FrameWaiter : public SurfaceTexture::FrameAvailableListener {
+    public:
+        FrameWaiter():
+                mPendingFrames(0) {
+        }
+
+        void waitForFrame() {
+            Mutex::Autolock lock(mMutex);
+            while (mPendingFrames == 0) {
+                mCondition.wait(mMutex);
+            }
+            mPendingFrames--;
+        }
+
+        virtual void onFrameAvailable() {
+            Mutex::Autolock lock(mMutex);
+            mPendingFrames++;
+            mCondition.signal();
+        }
+
+        int mPendingFrames;
+        Mutex mMutex;
+        Condition mCondition;
+    };
+
     sp<SurfaceTexture> mST;
     sp<SurfaceTextureClient> mSTC;
     sp<ANativeWindow> mANW;
@@ -648,6 +673,157 @@
     }
 }
 
+// This test is intended to catch synchronization bugs between the CPU-written
+// and GPU-read buffers.
+TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) {
+    enum { texWidth = 16 };
+    enum { texHeight = 16 };
+    enum { numFrames = 1024 };
+
+    ASSERT_EQ(NO_ERROR, mST->setSynchronousMode(true));
+    ASSERT_EQ(NO_ERROR, mST->setBufferCountServer(2));
+    ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(),
+            texWidth, texHeight, HAL_PIXEL_FORMAT_YV12));
+    ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(),
+            GRALLOC_USAGE_SW_WRITE_OFTEN));
+
+    struct TestPixel {
+        int x;
+        int y;
+    };
+    const TestPixel testPixels[] = {
+        {  4, 11 },
+        { 12, 14 },
+        {  7,  2 },
+    };
+    enum {numTestPixels = sizeof(testPixels) / sizeof(testPixels[0])};
+
+    class ProducerThread : public Thread {
+    public:
+        ProducerThread(const sp<ANativeWindow>& anw, const TestPixel* testPixels):
+                mANW(anw),
+                mTestPixels(testPixels) {
+        }
+
+        virtual ~ProducerThread() {
+        }
+
+        virtual bool threadLoop() {
+            for (int i = 0; i < numFrames; i++) {
+                ANativeWindowBuffer* anb;
+                if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) {
+                    return false;
+                }
+                if (anb == NULL) {
+                    return false;
+                }
+
+                sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
+                if (mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())
+                        != NO_ERROR) {
+                    return false;
+                }
+
+                const int yuvTexOffsetY = 0;
+                int stride = buf->getStride();
+                int yuvTexStrideY = stride;
+                int yuvTexOffsetV = yuvTexStrideY * texHeight;
+                int yuvTexStrideV = (yuvTexStrideY/2 + 0xf) & ~0xf;
+                int yuvTexOffsetU = yuvTexOffsetV + yuvTexStrideV * texHeight/2;
+                int yuvTexStrideU = yuvTexStrideV;
+
+                uint8_t* img = NULL;
+                buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
+
+                // Gray out all the test pixels first, so we're more likely to
+                // see a failure if GL is still texturing from the buffer we
+                // just dequeued.
+                for (int j = 0; j < numTestPixels; j++) {
+                    int x = mTestPixels[j].x;
+                    int y = mTestPixels[j].y;
+                    uint8_t value = 128;
+                    img[y*stride + x] = value;
+                }
+
+                // Fill the buffer with gray.
+                for (int y = 0; y < texHeight; y++) {
+                    for (int x = 0; x < texWidth; x++) {
+                        img[yuvTexOffsetY + y*yuvTexStrideY + x] = 128;
+                        img[yuvTexOffsetU + (y/2)*yuvTexStrideU + x/2] = 128;
+                        img[yuvTexOffsetV + (y/2)*yuvTexStrideV + x/2] = 128;
+                    }
+                }
+
+                // Set the test pixels to either white or black.
+                for (int j = 0; j < numTestPixels; j++) {
+                    int x = mTestPixels[j].x;
+                    int y = mTestPixels[j].y;
+                    uint8_t value = 0;
+                    if (j == (i % numTestPixels)) {
+                        value = 255;
+                    }
+                    img[y*stride + x] = value;
+                }
+
+                buf->unlock();
+                if (mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())
+                        != NO_ERROR) {
+                    return false;
+                }
+            }
+            return false;
+        }
+
+        sp<ANativeWindow> mANW;
+        const TestPixel* mTestPixels;
+    };
+
+    sp<FrameWaiter> fw(new FrameWaiter);
+    mST->setFrameAvailableListener(fw);
+
+    sp<Thread> pt(new ProducerThread(mANW, testPixels));
+    pt->run();
+
+    glViewport(0, 0, texWidth, texHeight);
+
+    glClearColor(0.2, 0.2, 0.2, 0.2);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    // We wait for the first two frames up front so that the producer will be
+    // likely to dequeue the buffer that's currently being textured from.
+    fw->waitForFrame();
+    fw->waitForFrame();
+
+    for (int i = 0; i < numFrames; i++) {
+        SCOPED_TRACE(String8::format("frame %d", i).string());
+
+        // We must wait for each frame to come in because if we ever do an
+        // updateTexImage call that doesn't consume a newly available buffer
+        // then the producer and consumer will get out of sync, which will cause
+        // a deadlock.
+        if (i > 1) {
+            fw->waitForFrame();
+        }
+        mST->updateTexImage();
+        drawTexture();
+
+        for (int j = 0; j < numTestPixels; j++) {
+            int x = testPixels[j].x;
+            int y = testPixels[j].y;
+            uint8_t value = 0;
+            if (j == (i % numTestPixels)) {
+                // We must y-invert the texture coords
+                EXPECT_TRUE(checkPixel(x, texHeight-y-1, 255, 255, 255, 255));
+            } else {
+                // We must y-invert the texture coords
+                EXPECT_TRUE(checkPixel(x, texHeight-y-1, 0, 0, 0, 255));
+            }
+        }
+    }
+
+    pt->requestExitAndWait();
+}
+
 // XXX: This test is disabled because there are currently no drivers that can
 // handle RGBA textures with the GL_TEXTURE_EXTERNAL_OES target.
 TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledRGBABufferNpot) {
diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp
index 35c8640..450cdf1 100644
--- a/libs/gui/tests/Surface_test.cpp
+++ b/libs/gui/tests/Surface_test.cpp
@@ -36,10 +36,10 @@
         ASSERT_TRUE(mSurfaceControl != NULL);
         ASSERT_TRUE(mSurfaceControl->isValid());
 
-        ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction());
+        SurfaceComposerClient::openGlobalTransaction();
         ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000));
         ASSERT_EQ(NO_ERROR, mSurfaceControl->show());
-        ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction());
+        SurfaceComposerClient::closeGlobalTransaction();
 
         mSurface = mSurfaceControl->getSurface();
         ASSERT_TRUE(mSurface != NULL);
diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp
index e43f6e5..8b1caeee 100644
--- a/libs/hwui/DisplayListRenderer.cpp
+++ b/libs/hwui/DisplayListRenderer.cpp
@@ -699,9 +699,10 @@
                 float* vertices = getFloats(verticesCount);
                 bool hasColors = getInt();
                 int* colors = hasColors ? getInts(colorsCount) : NULL;
+                SkPaint* paint = getPaint();
 
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
-                renderer.drawBitmapMesh(bitmap, meshWidth, meshHeight, vertices, colors, getPaint());
+                renderer.drawBitmapMesh(bitmap, meshWidth, meshHeight, vertices, colors, paint);
             }
             break;
             case DrawPatch: {
@@ -718,9 +719,15 @@
                 yDivs = getInts(yDivsCount);
                 colors = getUInts(numColors);
 
+                float left = getFloat();
+                float top = getFloat();
+                float right = getFloat();
+                float bottom = getFloat();
+                SkPaint* paint = getPaint();
+
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
                 renderer.drawPatch(bitmap, xDivs, yDivs, colors, xDivsCount, yDivsCount,
-                        numColors, getFloat(), getFloat(), getFloat(), getFloat(), getPaint());
+                        numColors, left, top, right, bottom, paint);
             }
             break;
             case DrawColor: {
@@ -799,15 +806,17 @@
             case DrawLines: {
                 int count = 0;
                 float* points = getFloats(count);
+                SkPaint* paint = getPaint();
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
-                renderer.drawLines(points, count, getPaint());
+                renderer.drawLines(points, count, paint);
             }
             break;
             case DrawPoints: {
                 int count = 0;
                 float* points = getFloats(count);
+                SkPaint* paint = getPaint();
                 DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]);
-                renderer.drawPoints(points, count, getPaint());
+                renderer.drawPoints(points, count, paint);
             }
             break;
             case DrawText: {
diff --git a/libs/ui/InputTransport.cpp b/libs/ui/InputTransport.cpp
index ffdfe66..c46d6f4 100644
--- a/libs/ui/InputTransport.cpp
+++ b/libs/ui/InputTransport.cpp
@@ -443,7 +443,8 @@
 
     if (! mPinned || ! mMotionEventSampleDataTail) {
         LOGE("channel '%s' publisher ~ Cannot append motion sample because there is no current "
-                "AMOTION_EVENT_ACTION_MOVE event.", mChannel->getName().string());
+                "AMOTION_EVENT_ACTION_MOVE or AMOTION_EVENT_ACTION_HOVER_MOVE event.",
+                mChannel->getName().string());
         return INVALID_OPERATION;
     }
 
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk
index 093189c..774e8c9 100644
--- a/libs/utils/Android.mk
+++ b/libs/utils/Android.mk
@@ -27,6 +27,7 @@
 	Debug.cpp \
 	FileMap.cpp \
 	Flattenable.cpp \
+	LinearTransform.cpp \
 	ObbFile.cpp \
 	Pool.cpp \
 	PropertyMap.cpp \
diff --git a/libs/utils/LinearTransform.cpp b/libs/utils/LinearTransform.cpp
new file mode 100644
index 0000000..d752415
--- /dev/null
+++ b/libs/utils/LinearTransform.cpp
@@ -0,0 +1,262 @@
+/*
+ * 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.
+ */
+
+#define __STDC_LIMIT_MACROS
+
+#include <assert.h>
+#include <stdint.h>
+
+#include <utils/LinearTransform.h>
+
+namespace android {
+
+template<class T> static inline T ABS(T x) { return (x < 0) ? -x : x; }
+
+// Static math methods involving linear transformations
+static bool scale_u64_to_u64(
+        uint64_t val,
+        uint32_t N,
+        uint32_t D,
+        uint64_t* res,
+        bool round_up_not_down) {
+    uint64_t tmp1, tmp2;
+    uint32_t r;
+
+    assert(res);
+    assert(D);
+
+    // Let U32(X) denote a uint32_t containing the upper 32 bits of a 64 bit
+    // integer X.
+    // Let L32(X) denote a uint32_t containing the lower 32 bits of a 64 bit
+    // integer X.
+    // Let X[A, B] with A <= B denote bits A through B of the integer X.
+    // Let (A | B) denote the concatination of two 32 bit ints, A and B.
+    // IOW X = (A | B) => U32(X) == A && L32(X) == B
+    //
+    // compute M = val * N (a 96 bit int)
+    // ---------------------------------
+    // tmp2 = U32(val) * N (a 64 bit int)
+    // tmp1 = L32(val) * N (a 64 bit int)
+    // which means
+    // M = val * N = (tmp2 << 32) + tmp1
+    tmp2 = (val >> 32) * N;
+    tmp1 = (val & UINT32_MAX) * N;
+
+    // compute M[32, 95]
+    // tmp2 = tmp2 + U32(tmp1)
+    //      = (U32(val) * N) + U32(L32(val) * N)
+    //      = M[32, 95]
+    tmp2 += tmp1 >> 32;
+
+    // if M[64, 95] >= D, then M/D has bits > 63 set and we have
+    // an overflow.
+    if ((tmp2 >> 32) >= D) {
+        *res = UINT64_MAX;
+        return false;
+    }
+
+    // Divide.  Going in we know
+    // tmp2 = M[32, 95]
+    // U32(tmp2) < D
+    r = tmp2 % D;
+    tmp2 /= D;
+
+    // At this point
+    // tmp1      = L32(val) * N
+    // tmp2      = M[32, 95] / D
+    //           = (M / D)[32, 95]
+    // r         = M[32, 95] % D
+    // U32(tmp2) = 0
+    //
+    // compute tmp1 = (r | M[0, 31])
+    tmp1 = (tmp1 & UINT32_MAX) | ((uint64_t)r << 32);
+
+    // Divide again.  Keep the remainder around in order to round properly.
+    r = tmp1 % D;
+    tmp1 /= D;
+
+    // At this point
+    // tmp2      = (M / D)[32, 95]
+    // tmp1      = (M / D)[ 0, 31]
+    // r         =  M % D
+    // U32(tmp1) = 0
+    // U32(tmp2) = 0
+
+    // Pack the result and deal with the round-up case (As well as the
+    // remote possiblility over overflow in such a case).
+    *res = (tmp2 << 32) | tmp1;
+    if (r && round_up_not_down) {
+        ++(*res);
+        if (!(*res)) {
+            *res = UINT64_MAX;
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool linear_transform_s64_to_s64(
+        int64_t  val,
+        int64_t  basis1,
+        int32_t  N,
+        uint32_t D,
+        int64_t  basis2,
+        int64_t* out) {
+    uint64_t scaled, res;
+    uint64_t abs_val;
+    bool is_neg;
+
+    if (!out)
+        return false;
+
+    // Compute abs(val - basis_64). Keep track of whether or not this delta
+    // will be negative after the scale opertaion.
+    if (val < basis1) {
+        is_neg = true;
+        abs_val = basis1 - val;
+    } else {
+        is_neg = false;
+        abs_val = val - basis1;
+    }
+
+    if (N < 0)
+        is_neg = !is_neg;
+
+    if (!scale_u64_to_u64(abs_val,
+                          ABS(N),
+                          D,
+                          &scaled,
+                          is_neg))
+        return false; // overflow/undeflow
+
+    // if scaled is >= 0x8000<etc>, then we are going to overflow or
+    // underflow unless ABS(basis2) is large enough to pull us back into the
+    // non-overflow/underflow region.
+    if (scaled & INT64_MIN) {
+        if (is_neg && (basis2 < 0))
+            return false; // certain underflow
+
+        if (!is_neg && (basis2 >= 0))
+            return false; // certain overflow
+
+        if (ABS(basis2) <= static_cast<int64_t>(scaled & INT64_MAX))
+            return false; // not enough
+
+        // Looks like we are OK
+        *out = (is_neg ? (-scaled) : scaled) + basis2;
+    } else {
+        // Scaled fits within signed bounds, so we just need to check for
+        // over/underflow for two signed integers.  Basically, if both scaled
+        // and basis2 have the same sign bit, and the result has a different
+        // sign bit, then we have under/overflow.  An easy way to compute this
+        // is
+        // (scaled_signbit XNOR basis_signbit) &&
+        // (scaled_signbit XOR res_signbit)
+        // ==
+        // (scaled_signbit XOR basis_signbit XOR 1) &&
+        // (scaled_signbit XOR res_signbit)
+
+        if (is_neg)
+            scaled = -scaled;
+        res = scaled + basis2;
+
+        if ((scaled ^ basis2 ^ INT64_MIN) & (scaled ^ res) & INT64_MIN)
+            return false;
+
+        *out = res;
+    }
+
+    return true;
+}
+
+bool LinearTransform::doForwardTransform(int64_t a_in, int64_t* b_out) const {
+    if (0 == a_to_b_denom)
+        return false;
+
+    return linear_transform_s64_to_s64(a_in,
+                                       a_zero,
+                                       a_to_b_numer,
+                                       a_to_b_denom,
+                                       b_zero,
+                                       b_out);
+}
+
+bool LinearTransform::doReverseTransform(int64_t b_in, int64_t* a_out) const {
+    if (0 == a_to_b_numer)
+        return false;
+
+    return linear_transform_s64_to_s64(b_in,
+                                       b_zero,
+                                       a_to_b_denom,
+                                       a_to_b_numer,
+                                       a_zero,
+                                       a_out);
+}
+
+template <class T> void LinearTransform::reduce(T* N, T* D) {
+    T a, b;
+    if (!N || !D || !(*D)) {
+        assert(false);
+        return;
+    }
+
+    a = *N;
+    b = *D;
+
+    if (a == 0) {
+        *D = 1;
+        return;
+    }
+
+    // This implements Euclid's method to find GCD.
+    if (a < b) {
+        T tmp = a;
+        a = b;
+        b = tmp;
+    }
+
+    while (1) {
+        // a is now the greater of the two.
+        const T remainder = a % b;
+        if (remainder == 0) {
+            *N /= b;
+            *D /= b;
+            return;
+        }
+        // by swapping remainder and b, we are guaranteeing that a is
+        // still the greater of the two upon entrance to the loop.
+        a = b;
+        b = remainder;
+    }
+};
+
+template void LinearTransform::reduce<uint64_t>(uint64_t* N, uint64_t* D);
+template void LinearTransform::reduce<uint32_t>(uint32_t* N, uint32_t* D);
+
+void LinearTransform::reduce(int32_t* N, uint32_t* D) {
+    if (N && D && *D) {
+        if (*N < 0) {
+            *N = -(*N);
+            reduce(reinterpret_cast<uint32_t*>(N), D);
+            *N = -(*N);
+        } else {
+            reduce(reinterpret_cast<uint32_t*>(N), D);
+        }
+    }
+}
+
+}  // namespace android
diff --git a/location/java/android/location/Country.java b/location/java/android/location/Country.java
index 3c05403..939bd4a 100755
--- a/location/java/android/location/Country.java
+++ b/location/java/android/location/Country.java
@@ -19,6 +19,8 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.util.Locale;
+
 /**
  * This class wraps the country information.
  *
@@ -74,7 +76,7 @@
                 || source > COUNTRY_SOURCE_LOCALE) {
             throw new IllegalArgumentException();
         }
-        mCountryIso = countryIso.toLowerCase();
+        mCountryIso = countryIso.toUpperCase(Locale.US);
         mSource = source;
     }
 
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 6f42596..e3cbd57 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -720,8 +720,9 @@
      *
      * <p>Since API level 13, if applications set a camera via
      * {@link #setCamera(Camera)}, the apps can use the camera after this method
-     * call. The apps do not need to lock the camera again. The apps should not
-     * start another recording session during recording.
+     * call. The apps do not need to lock the camera again. However, if this
+     * method fails, the apps should still lock the camera back. The apps should
+     * not start another recording session during recording.
      *
      * @throws IllegalStateException if it is called before
      * prepare().
diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index 29c4b89..0d2bcd5 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -957,12 +957,8 @@
 
         public static final int FADE_FROM_BLACK = 8;
 
-        public static final int CURTAIN_OPENING = 9;
-
         public static final int FADE_TO_BLACK = 16;
 
-        public static final int CURTAIN_CLOSING = 17;
-
         public static final int EXTERNAL = 256;
 
         public static final int BLACK_AND_WHITE = 257;
diff --git a/media/jni/mediaeditor/VideoEditorClasses.cpp b/media/jni/mediaeditor/VideoEditorClasses.cpp
index d43a562..277e16c 100755
--- a/media/jni/mediaeditor/VideoEditorClasses.cpp
+++ b/media/jni/mediaeditor/VideoEditorClasses.cpp
@@ -378,9 +378,7 @@
 {
     VIDEOEDIT_JAVA_CONSTANT_INIT("NONE",            M4VSS3GPP_kVideoEffectType_None),
     VIDEOEDIT_JAVA_CONSTANT_INIT("FADE_FROM_BLACK", M4VSS3GPP_kVideoEffectType_FadeFromBlack),
-    VIDEOEDIT_JAVA_CONSTANT_INIT("CURTAIN_OPENING", M4VSS3GPP_kVideoEffectType_CurtainOpening),
     VIDEOEDIT_JAVA_CONSTANT_INIT("FADE_TO_BLACK",   M4VSS3GPP_kVideoEffectType_FadeToBlack),
-    VIDEOEDIT_JAVA_CONSTANT_INIT("CURTAIN_CLOSING", M4VSS3GPP_kVideoEffectType_CurtainClosing),
     VIDEOEDIT_JAVA_CONSTANT_INIT("EXTERNAL",        M4VSS3GPP_kVideoEffectType_External),
     VIDEOEDIT_JAVA_CONSTANT_INIT("BLACK_AND_WHITE", M4xVSS_kVideoEffectType_BlackAndWhite),
     VIDEOEDIT_JAVA_CONSTANT_INIT("PINK",            M4xVSS_kVideoEffectType_Pink),
diff --git a/media/jni/mediaeditor/VideoEditorOsal.cpp b/media/jni/mediaeditor/VideoEditorOsal.cpp
index 53e7de1..a8c08ac 100755
--- a/media/jni/mediaeditor/VideoEditorOsal.cpp
+++ b/media/jni/mediaeditor/VideoEditorOsal.cpp
@@ -166,7 +166,6 @@
     VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_NO_SUPPORTED_VIDEO_STREAM_IN_FILE        ),
     VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_INTERNAL_STATE                           ),
     VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_LUMA_FILTER_ERROR                        ),
-    VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_CURTAIN_FILTER_ERROR                     ),
     VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_TRANSITION_FILTER_ERROR                  ),
     VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_AUDIO_DECODER_INIT_FAILED                ),
     VIDEOEDIT_OSAL_RESULT_INIT(M4VSS3GPP_ERR_AUDIO_DECODED_PCM_SIZE_ISSUE             ),
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index d628301..513eda8 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -29,6 +29,7 @@
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/NativeWindowWrapper.h>
 #include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
 
 #include <surfaceflinger/Surface.h>
 #include <gui/SurfaceTextureClient.h>
@@ -401,11 +402,22 @@
         CHECK(mem.get() != NULL);
 
         IOMX::buffer_id buffer;
-#if 0
-        err = mOMX->allocateBufferWithBackup(mNode, portIndex, mem, &buffer);
-#else
-        err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
-#endif
+
+        if (!strcasecmp(
+                    mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.DECODER")) {
+            if (portIndex == kPortIndexInput && i == 0) {
+                // Only log this warning once per allocation round.
+
+                LOGW("OMX.TI.DUCATI1.VIDEO.DECODER requires the use of "
+                     "OMX_AllocateBuffer instead of the preferred "
+                     "OMX_UseBuffer. Vendor must fix this.");
+            }
+
+            err = mOMX->allocateBufferWithBackup(
+                    mNode, portIndex, mem, &buffer);
+        } else {
+            err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
+        }
 
         if (err != OK) {
             return err;
@@ -891,6 +903,7 @@
     CHECK(format.eColorFormat == OMX_COLOR_FormatYUV420Planar
            || format.eColorFormat == OMX_COLOR_FormatYUV420SemiPlanar
            || format.eColorFormat == OMX_COLOR_FormatCbYCrY
+           || format.eColorFormat == OMX_TI_COLOR_FormatYUV420PackedSemiPlanar
            || format.eColorFormat == OMX_QCOM_COLOR_FormatYVU420SemiPlanar);
 
     return mOMX->setParameter(
@@ -1639,27 +1652,33 @@
     AString mime;
     CHECK(msg->findString("mime", &mime));
 
-    AString componentName;
-
-    if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_AVC)) {
-        componentName = "OMX.Nvidia.h264.decode";
-    } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_AAC)) {
-        componentName = "OMX.google.aac.decoder";
-    } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_AUDIO_MPEG)) {
-        componentName = "OMX.Nvidia.mp3.decoder";
-    } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG2)) {
-        componentName = "OMX.Nvidia.mpeg2v.decode";
-    } else if (!strcasecmp(mime.c_str(), MEDIA_MIMETYPE_VIDEO_MPEG4)) {
-        componentName = "OMX.google.mpeg4.decoder";
-    } else {
-        TRESPASS();
-    }
+    Vector<String8> matchingCodecs;
+    OMXCodec::findMatchingCodecs(
+            mime.c_str(),
+            false, // createEncoder
+            NULL,  // matchComponentName
+            0,     // flags
+            &matchingCodecs);
 
     sp<CodecObserver> observer = new CodecObserver;
+    IOMX::node_id node = NULL;
 
-    IOMX::node_id node;
-    CHECK_EQ(omx->allocateNode(componentName.c_str(), observer, &node),
-             (status_t)OK);
+    AString componentName;
+
+    for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
+            ++matchIndex) {
+        componentName = matchingCodecs.itemAt(matchIndex).string();
+
+        status_t err = omx->allocateNode(componentName.c_str(), observer, &node);
+
+        if (err == OK) {
+            break;
+        }
+
+        node = NULL;
+    }
+
+    CHECK(node != NULL);
 
     sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
     observer->setNotificationMessage(notify);
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index bdffce9..ed8149a 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -158,9 +158,12 @@
     mVideoSize.width  = -1;
     mVideoSize.height = -1;
 
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
     mInitCheck = init(camera, proxy, cameraId,
                     videoSize, frameRate,
                     storeMetaDataInVideoBuffers);
+    if (mInitCheck != OK) releaseCamera();
+    IPCThreadState::self()->restoreCallingIdentity(token);
 }
 
 status_t CameraSource::initCheck() const {
@@ -295,7 +298,6 @@
     if (width != -1 && height != -1) {
         if (!isVideoSizeSupported(width, height, sizes)) {
             LOGE("Video dimension (%dx%d) is unsupported", width, height);
-            releaseCamera();
             return BAD_VALUE;
         }
         if (isSetVideoSizeSupportedByCamera) {
@@ -309,7 +311,6 @@
         // If one and only one of the width and height is -1
         // we reject such a request.
         LOGE("Requested video size (%dx%d) is not supported", width, height);
-        releaseCamera();
         return BAD_VALUE;
     } else {  // width == -1 && height == -1
         // Do not configure the camera.
@@ -327,7 +328,6 @@
         if (strstr(supportedFrameRates, buf) == NULL) {
             LOGE("Requested frame rate (%d) is not supported: %s",
                 frameRate, supportedFrameRates);
-            releaseCamera();
             return BAD_VALUE;
         }
 
@@ -463,7 +463,6 @@
         bool storeMetaDataInVideoBuffers) {
 
     status_t err = OK;
-    int64_t token = IPCThreadState::self()->clearCallingIdentity();
 
     if ((err = isCameraAvailable(camera, proxy, cameraId)) != OK) {
         LOGE("Camera connection could not be established.");
@@ -505,8 +504,6 @@
         }
     }
 
-    IPCThreadState::self()->restoreCallingIdentity(token);
-
     int64_t glitchDurationUs = (1000000LL / mVideoFrameRate);
     if (glitchDurationUs > mGlitchDurationThresholdUs) {
         mGlitchDurationThresholdUs = glitchDurationUs;
@@ -573,13 +570,21 @@
 
 void CameraSource::releaseCamera() {
     LOGV("releaseCamera");
-    if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) {
-        LOGV("Camera was cold when we started, stopping preview");
-        mCamera->stopPreview();
+    if (mCamera != 0) {
+        if ((mCameraFlags & FLAGS_HOT_CAMERA) == 0) {
+            LOGV("Camera was cold when we started, stopping preview");
+            mCamera->stopPreview();
+            mCamera->disconnect();
+        } else {
+            // Unlock the camera so the application can lock it back.
+            mCamera->unlock();
+        }
+        mCamera.clear();
     }
-    mCamera.clear();
-    mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier);
-    mCameraRecordingProxy.clear();
+    if (mCameraRecordingProxy != 0) {
+        mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier);
+        mCameraRecordingProxy.clear();
+    }
     mCameraFlags = 0;
 }
 
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 4e4f289..02eeb40 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -466,6 +466,8 @@
 
 MPEG2TSWriter::MPEG2TSWriter(int fd)
     : mFile(fdopen(dup(fd), "wb")),
+      mWriteCookie(NULL),
+      mWriteFunc(NULL),
       mStarted(false),
       mNumSourcesDone(0),
       mNumTSPacketsWritten(0),
@@ -475,6 +477,21 @@
 
 MPEG2TSWriter::MPEG2TSWriter(const char *filename)
     : mFile(fopen(filename, "wb")),
+      mWriteCookie(NULL),
+      mWriteFunc(NULL),
+      mStarted(false),
+      mNumSourcesDone(0),
+      mNumTSPacketsWritten(0),
+      mNumTSPacketsBeforeMeta(0) {
+    init();
+}
+
+MPEG2TSWriter::MPEG2TSWriter(
+        void *cookie,
+        ssize_t (*write)(void *cookie, const void *data, size_t size))
+    : mFile(NULL),
+      mWriteCookie(cookie),
+      mWriteFunc(write),
       mStarted(false),
       mNumSourcesDone(0),
       mNumTSPacketsWritten(0),
@@ -483,7 +500,7 @@
 }
 
 void MPEG2TSWriter::init() {
-    CHECK(mFile != NULL);
+    CHECK(mFile != NULL || mWriteFunc != NULL);
 
     mLooper = new ALooper;
     mLooper->setName("MPEG2TSWriter");
@@ -502,8 +519,10 @@
     mLooper->unregisterHandler(mReflector->id());
     mLooper->stop();
 
-    fclose(mFile);
-    mFile = NULL;
+    if (mFile != NULL) {
+        fclose(mFile);
+        mFile = NULL;
+    }
 }
 
 status_t MPEG2TSWriter::addSource(const sp<MediaSource> &source) {
@@ -718,7 +737,7 @@
     static const unsigned kContinuityCounter = 5;
     buffer->data()[3] |= kContinuityCounter;
 
-    CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size());
+    CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size());
 }
 
 void MPEG2TSWriter::writeProgramMap() {
@@ -794,7 +813,7 @@
     *ptr++ = 0x00;
     *ptr++ = 0x00;
 
-    CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size());
+    CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size());
 }
 
 void MPEG2TSWriter::writeAccessUnit(
@@ -890,7 +909,7 @@
 
     memcpy(ptr, accessUnit->data(), copy);
 
-    CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size());
+    CHECK_EQ(internalWrite(buffer->data(), buffer->size()), buffer->size());
 
     size_t offset = copy;
     while (offset < accessUnit->size()) {
@@ -923,7 +942,7 @@
         }
 
         memcpy(ptr, accessUnit->data() + offset, copy);
-        CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile),
+        CHECK_EQ(internalWrite(buffer->data(), buffer->size()),
                  buffer->size());
 
         offset += copy;
@@ -939,5 +958,13 @@
     }
 }
 
+ssize_t MPEG2TSWriter::internalWrite(const void *data, size_t size) {
+    if (mFile != NULL) {
+        return fwrite(data, 1, size, mFile);
+    }
+
+    return (*mWriteFunc)(mWriteCookie, data, size);
+}
+
 }  // namespace android
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index cd97302..e36b01f 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1838,7 +1838,7 @@
         }
     }
 
-    LOGV("native_window_set_usage usage=0x%x", usage);
+    LOGV("native_window_set_usage usage=0x%lx", usage);
     err = native_window_set_usage(
             mNativeWindow.get(), usage | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP);
     if (err != 0) {
diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp
index ed6846c..967f126 100644
--- a/media/libstagefright/chromium_http/support.cpp
+++ b/media/libstagefright/chromium_http/support.cpp
@@ -41,6 +41,7 @@
 static Mutex gNetworkThreadLock;
 static base::Thread *gNetworkThread = NULL;
 static scoped_refptr<net::URLRequestContext> gReqContext;
+static scoped_ptr<net::NetworkChangeNotifier> gNetworkChangeNotifier;
 
 static void InitializeNetworkThreadIfNecessary() {
     Mutex::Autolock autoLock(gNetworkThreadLock);
@@ -52,6 +53,8 @@
 
         gReqContext = new SfRequestContext;
 
+        gNetworkChangeNotifier.reset(net::NetworkChangeNotifier::Create());
+
         net::AndroidNetworkLibrary::RegisterSharedInstance(
                 new SfNetworkLibrary);
     }
diff --git a/native/include/android/input.h b/native/include/android/input.h
index 26cac50..0d8ea28 100644
--- a/native/include/android/input.h
+++ b/native/include/android/input.h
@@ -404,7 +404,6 @@
     AMOTION_EVENT_BUTTON_TERTIARY = 1 << 2,
     AMOTION_EVENT_BUTTON_BACK = 1 << 3,
     AMOTION_EVENT_BUTTON_FORWARD = 1 << 4,
-    AMOTION_EVENT_BUTTON_ERASER = 1 << 5,
 };
 
 /*
diff --git a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
index eb2f6f8..63c2de2 100644
--- a/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
+++ b/nfc-extras/java/com/android/nfc_extras/NfcExecutionEnvironment.java
@@ -55,6 +55,64 @@
      */
     public static final String EXTRA_AID = "com.android.nfc_extras.extra.AID";
 
+    /**
+     * Broadcast action: A filtered APDU was received.
+     *
+     * <p>This happens when an APDU of interest was matched by the Nfc adapter,
+     * for instance as the result of matching an externally-configured filter.
+     *
+     * <p>The filter configuration mechanism is not currently defined.
+     *
+     * <p>Always contains the extra field {@link EXTRA_APDU_BYTES}.
+     *
+     * @hide
+     */
+    public static final String ACTION_APDU_RECEIVED =
+        "com.android.nfc_extras.action.APDU_RECEIVED";
+
+    /**
+     * Mandatory byte array extra field in {@link #ACTION_APDU_RECEIVED}.
+     *
+     * <p>Contains the bytes of the received APDU.
+     *
+     * @hide
+     */
+    public static final String EXTRA_APDU_BYTES =
+        "com.android.nfc_extras.extra.APDU_BYTES";
+
+    /**
+     * Broadcast action: An EMV card removal event was detected.
+     *
+     * @hide
+     */
+    public static final String ACTION_EMV_CARD_REMOVAL =
+        "com.android.nfc_extras.action.EMV_CARD_REMOVAL";
+
+    /**
+     * Broadcast action: An adapter implementing MIFARE Classic via card
+     * emulation detected that a block has been accessed.
+     *
+     * <p>This may only be issued for the first block that the reader
+     * authenticates to.
+     *
+     * <p>May contain the extra field {@link #EXTRA_MIFARE_BLOCK}.
+     *
+     * @hide
+     */
+    public static final String ACTION_MIFARE_ACCESS_DETECTED =
+        "com.android.nfc_extras.action.MIFARE_ACCESS_DETECTED";
+
+    /**
+     * Optional integer extra field in {@link #ACTION_MIFARE_ACCESS_DETECTED}.
+     *
+     * <p>Provides the block number being accessed.  If not set, the block
+     * number being accessed is unknown.
+     *
+     * @hide
+     */
+    public static final String EXTRA_MIFARE_BLOCK =
+        "com.android.nfc_extras.extra.MIFARE_BLOCK";
+
     NfcExecutionEnvironment(NfcAdapterExtras extras) {
         mExtras = extras;
     }
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
index a9aa31b..9469601 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsBackupAgent.java
@@ -531,7 +531,12 @@
     private void restoreFileData(String filename, BackupDataInput data) {
         byte[] bytes = new byte[data.getDataSize()];
         if (bytes.length <= 0) return;
-        restoreFileData(filename, bytes, bytes.length);
+        try {
+            data.readEntityData(bytes, 0, data.getDataSize());
+            restoreFileData(filename, bytes, bytes.length);
+        } catch (IOException e) {
+            Log.w(TAG, "Unable to read file data for " + filename);
+        }
     }
 
     private void restoreFileData(String filename, byte[] bytes, int size) {
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index bc2f7ee..51e7d97 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -25,14 +25,13 @@
     android:layout_width="match_parent"
     >
 
-    <FrameLayout
-        android:id="@+id/background"
+    <FrameLayout android:id="@+id/rot0"
         android:layout_height="match_parent"
         android:layout_width="match_parent"
         android:background="#FF000000"
         >
 
-        <LinearLayout android:id="@+id/rot0"
+        <LinearLayout
             android:layout_height="match_parent"
             android:layout_width="match_parent"
             android:orientation="horizontal"
@@ -40,12 +39,12 @@
 
             <!-- navigation controls -->
             <View
-                android:layout_width="32dp"
+                android:layout_width="40dp"
                 android:layout_height="match_parent"
                 android:layout_weight="0"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
-                android:layout_width="54dp"
+                android:layout_width="80dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_back_default"
                 systemui:keyCode="4"
@@ -57,7 +56,7 @@
                 android:layout_weight="1"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
-                android:layout_width="54dp"
+                android:layout_width="80dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_home_default"
                 systemui:keyCode="3"
@@ -69,13 +68,13 @@
                 android:layout_weight="1"
                 />
             <ImageView android:id="@+id/recent_apps"
-                android:layout_width="54dp"
+                android:layout_width="80dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_recent_default"
                 android:layout_weight="0"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
-                android:layout_width="32dp"
+                android:layout_width="40dp"
                 android:layout_height="match_parent"
                 android:src="@drawable/ic_sysbar_menu_default"
                 systemui:keyCode="82"
@@ -84,17 +83,31 @@
                 />
         </LinearLayout>
 
-        <LinearLayout android:id="@+id/rot90"
+        <View android:id="@+id/deadzone"
+            android:layout_height="@dimen/navigation_bar_deadzone_size"
+            android:layout_width="match_parent"
+            android:layout_gravity="top"
+            android:clickable="true"
+            />
+    </FrameLayout>
+
+    <FrameLayout android:id="@+id/rot90"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:background="#FF000000"
+        android:visibility="gone"
+        android:paddingTop="24dp"
+        >
+
+        <LinearLayout 
             android:layout_height="match_parent"
             android:layout_width="match_parent"
             android:orientation="vertical"
-            android:visibility="gone"
-            android:paddingTop="24dp"
             >
             
             <!-- navigation controls -->
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/menu"
-                android:layout_height="32dp"
+                android:layout_height="40dp"
                 android:layout_width="match_parent"
                 android:src="@drawable/ic_sysbar_menu_default_land"
                 systemui:keyCode="82"
@@ -102,7 +115,7 @@
                 android:visibility="invisible"
                 />
             <ImageView android:id="@+id/recent_apps"
-                android:layout_height="54dp"
+                android:layout_height="80dp"
                 android:layout_width="match_parent"
                 android:src="@drawable/ic_sysbar_recent_default_land"
                 android:layout_weight="0"
@@ -113,7 +126,7 @@
                 android:layout_weight="1"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/home"
-                android:layout_height="54dp"
+                android:layout_height="80dp"
                 android:layout_width="match_parent"
                 android:src="@drawable/ic_sysbar_home_default_land"
                 systemui:keyCode="3"
@@ -125,28 +138,32 @@
                 android:layout_weight="1"
                 />
             <com.android.systemui.statusbar.policy.KeyButtonView android:id="@+id/back"
-                android:layout_height="54dp"
+                android:layout_height="80dp"
                 android:layout_width="match_parent"
                 android:src="@drawable/ic_sysbar_back_default_land"
                 systemui:keyCode="4"
                 android:layout_weight="0"
                 />
             <View
-                android:layout_height="32dp"
+                android:layout_height="40dp"
                 android:layout_width="match_parent"
                 android:layout_weight="0"
                 />
         </LinearLayout>
 
-        <LinearLayout android:id="@+id/rot270"
+        <View android:id="@+id/deadzone"
+            android:layout_width="@dimen/navigation_bar_deadzone_size"
             android:layout_height="match_parent"
-            android:layout_width="match_parent"
-            android:orientation="vertical"
-            android:visibility="gone"
-            >
-
-            <!-- not used -->
-        </LinearLayout>
-    
+            android:layout_gravity="left"
+            android:clickable="true"
+            />
     </FrameLayout>
+
+    <!-- not used -->
+    <View android:id="@+id/rot270"
+        android:layout_height="match_parent"
+        android:layout_width="match_parent"
+        android:visibility="gone"
+        />
+
 </com.android.systemui.statusbar.phone.NavigationBarView>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 3944c203..da28e1e 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -40,7 +40,11 @@
     <dimen name="peek_window_y_offset">-12dp</dimen>
 
     <!-- thickness (height) of the navigation bar on phones that require it -->
-    <dimen name="navigation_bar_size">32dp</dimen>
+    <dimen name="navigation_bar_size">48dp</dimen>
+
+    <!-- thickness (height) of the dead zone at the top of the navigation bar,
+         reducing false presses on navbar buttons; approx 2mm -->
+    <dimen name="navigation_bar_deadzone_size">12dp</dimen>
 
     <!-- thickness (height) of each notification row, including any separators or padding -->
     <dimen name="notification_height">65dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index 22181b8..550fc57 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -36,13 +36,14 @@
 import com.android.systemui.R;
 
 public class NavigationBarView extends LinearLayout {
+    final static boolean DEBUG_DEADZONE = false;
+
     final static boolean NAVBAR_ALWAYS_AT_RIGHT = true;
 
     protected IStatusBarService mBarService;
     final Display mDisplay;
     View mCurrentView = null;
     View[] mRotatedViews = new View[4];
-    View mBackground;
     Animator mLastAnimator = null;
 
     public View getRecentsButton() {
@@ -74,13 +75,13 @@
     }
 
     private void setLights(final boolean on) {
-        float oldAlpha = mBackground.getAlpha();
+        float oldAlpha = mCurrentView.getAlpha();
         android.util.Log.d("NavigationBarView", "animating alpha: " + oldAlpha + " -> "
             + (on ? 1f : 0f));
 
         if (mLastAnimator != null && mLastAnimator.isRunning()) mLastAnimator.cancel();
 
-        mLastAnimator = ObjectAnimator.ofFloat(mBackground, "alpha", oldAlpha, on ? 1f : 0f)
+        mLastAnimator = ObjectAnimator.ofFloat(mCurrentView, "alpha", oldAlpha, on ? 1f : 0f)
             .setDuration(on ? 250 : 1500);
         mLastAnimator.addListener(new AnimatorListenerAdapter() {
             @Override
@@ -92,8 +93,6 @@
     }
 
     public void onFinishInflate() {
-        mBackground = findViewById(R.id.background);
-
         mRotatedViews[Surface.ROTATION_0] = 
         mRotatedViews[Surface.ROTATION_180] = findViewById(R.id.rot0);
 
@@ -121,6 +120,10 @@
         mCurrentView = mRotatedViews[rot];
         mCurrentView.setVisibility(View.VISIBLE);
 
+        if (DEBUG_DEADZONE) {
+            mCurrentView.findViewById(R.id.deadzone).setBackgroundColor(0x808080FF);
+        }
+
         android.util.Log.d("NavigationBarView", "reorient(): rot=" + mDisplay.getRotation());
     }
 }
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index eea30407..b60bae7 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -682,8 +682,9 @@
             final boolean usingLockPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
                     == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
 
-            boolean showSlidingTab = getResources().getBoolean(R.bool.config_enableSlidingTabFirst);
-            if (isSecure() && (usingLockPattern || !showSlidingTab)) {
+            boolean showLockBeforeUnlock = getResources()
+                    .getBoolean(R.bool.config_enableLockBeforeUnlockScreen);
+            if (isSecure() && (usingLockPattern || !showLockBeforeUnlock)) {
                 return Mode.UnlockScreen;
             } else {
                 return Mode.LockScreen;
diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp
index 4a50d8a..85ce38a 100644
--- a/services/input/InputDispatcher.cpp
+++ b/services/input/InputDispatcher.cpp
@@ -569,9 +569,9 @@
         break;
     case DROP_REASON_BLOCKED:
         LOGI("Dropped event because the current application is not responding and the user "
-                "has started interating with a different application.");
+                "has started interacting with a different application.");
         reason = "inbound event was dropped because the current application is not responding "
-                "and the user has started interating with a different application";
+                "and the user has started interacting with a different application";
         break;
     case DROP_REASON_STALE:
         LOGI("Dropped event because it is stale.");
@@ -1239,37 +1239,35 @@
     const InputWindow* newHoverWindow = NULL;
 
     bool isSplit = mTouchState.split;
-    bool wrongDevice = mTouchState.down
-            && (mTouchState.deviceId != entry->deviceId
-                    || mTouchState.source != entry->source);
+    bool switchedDevice = mTouchState.deviceId != entry->deviceId
+            || mTouchState.source != entry->source;
     bool isHoverAction = (maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
             || maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
             || maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT);
     bool newGesture = (maskedAction == AMOTION_EVENT_ACTION_DOWN
             || maskedAction == AMOTION_EVENT_ACTION_SCROLL
             || isHoverAction);
+    bool wrongDevice = false;
     if (newGesture) {
         bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
-        if (wrongDevice && !down) {
+        if (switchedDevice && mTouchState.down && !down) {
+#if DEBUG_FOCUS
+            LOGD("Dropping event because a pointer for a different device is already down.");
+#endif
             mTempTouchState.copyFrom(mTouchState);
-        } else {
-            mTempTouchState.reset();
-            mTempTouchState.down = down;
-            mTempTouchState.deviceId = entry->deviceId;
-            mTempTouchState.source = entry->source;
-            isSplit = false;
-            wrongDevice = false;
+            injectionResult = INPUT_EVENT_INJECTION_FAILED;
+            switchedDevice = false;
+            wrongDevice = true;
+            goto Failed;
         }
+        mTempTouchState.reset();
+        mTempTouchState.down = down;
+        mTempTouchState.deviceId = entry->deviceId;
+        mTempTouchState.source = entry->source;
+        isSplit = false;
     } else {
         mTempTouchState.copyFrom(mTouchState);
     }
-    if (wrongDevice) {
-#if DEBUG_FOCUS
-        LOGD("Dropping event because a pointer for a different device is already down.");
-#endif
-        injectionResult = INPUT_EVENT_INJECTION_FAILED;
-        goto Failed;
-    }
 
     if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
         /* Case 1: New splittable pointer going down, or need target for hover or scroll. */
@@ -1598,18 +1596,38 @@
     // Update final pieces of touch state if the injector had permission.
     if (injectionPermission == INJECTION_PERMISSION_GRANTED) {
         if (!wrongDevice) {
-            if (maskedAction == AMOTION_EVENT_ACTION_UP
-                    || maskedAction == AMOTION_EVENT_ACTION_CANCEL
-                    || isHoverAction) {
+            if (switchedDevice) {
+#if DEBUG_FOCUS
+                LOGD("Conflicting pointer actions: Switched to a different device.");
+#endif
+                *outConflictingPointerActions = true;
+            }
+
+            if (isHoverAction) {
+                // Started hovering, therefore no longer down.
+                if (mTouchState.down) {
+#if DEBUG_FOCUS
+                    LOGD("Conflicting pointer actions: Hover received while pointer was down.");
+#endif
+                    *outConflictingPointerActions = true;
+                }
+                mTouchState.reset();
+                if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER
+                        || maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+                    mTouchState.deviceId = entry->deviceId;
+                    mTouchState.source = entry->source;
+                }
+            } else if (maskedAction == AMOTION_EVENT_ACTION_UP
+                    || maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
                 // All pointers up or canceled.
                 mTouchState.reset();
             } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
                 // First pointer went down.
                 if (mTouchState.down) {
-                    *outConflictingPointerActions = true;
 #if DEBUG_FOCUS
-                    LOGD("Pointer down received while already down.");
+                    LOGD("Conflicting pointer actions: Down received while already down.");
 #endif
+                    *outConflictingPointerActions = true;
                 }
                 mTouchState.copyFrom(mTempTouchState);
             } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
@@ -1868,6 +1886,18 @@
                 return;
             }
 
+            // If the motion event was modified in flight, then we cannot stream the sample.
+            if ((motionEventDispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_MASK)
+                    != InputTarget::FLAG_DISPATCH_AS_IS) {
+#if DEBUG_BATCHING
+                LOGD("channel '%s' ~ Not streaming because the motion event was not "
+                        "being dispatched as-is.  "
+                        "(Waiting for next dispatch cycle to start.)",
+                        connection->getInputChannelName());
+#endif
+                return;
+            }
+
             // The dispatch entry is in progress and is still potentially open for streaming.
             // Try to stream the new motion sample.  This might fail if the consumer has already
             // consumed the motion event (or if the channel is broken).
@@ -1972,6 +2002,66 @@
         dispatchEntry->headMotionSample = appendedMotionSample;
     }
 
+    // Apply target flags and update the connection's input state.
+    switch (eventEntry->type) {
+    case EventEntry::TYPE_KEY: {
+        KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
+        dispatchEntry->resolvedAction = keyEntry->action;
+        dispatchEntry->resolvedFlags = keyEntry->flags;
+
+        if (!connection->inputState.trackKey(keyEntry,
+                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
+#if DEBUG_DISPATCH_CYCLE
+            LOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
+                    connection->getInputChannelName());
+#endif
+            return; // skip the inconsistent event
+        }
+        break;
+    }
+
+    case EventEntry::TYPE_MOTION: {
+        MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
+        if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
+            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_OUTSIDE;
+        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
+            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_EXIT;
+        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
+            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
+            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_CANCEL;
+        } else if (dispatchMode & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
+            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_DOWN;
+        } else {
+            dispatchEntry->resolvedAction = motionEntry->action;
+        }
+        if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE
+                && !connection->inputState.isHovering(
+                        motionEntry->deviceId, motionEntry->source)) {
+#if DEBUG_DISPATCH_CYCLE
+        LOGD("channel '%s' ~ enqueueDispatchEntryLocked: filling in missing hover enter event",
+                connection->getInputChannelName());
+#endif
+            dispatchEntry->resolvedAction = AMOTION_EVENT_ACTION_HOVER_ENTER;
+        }
+
+        dispatchEntry->resolvedFlags = motionEntry->flags;
+        if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
+            dispatchEntry->resolvedFlags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
+        }
+
+        if (!connection->inputState.trackMotion(motionEntry,
+                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags)) {
+#if DEBUG_DISPATCH_CYCLE
+            LOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent motion event",
+                    connection->getInputChannelName());
+#endif
+            return; // skip the inconsistent event
+        }
+        break;
+    }
+    }
+
     // Enqueue the dispatch entry.
     connection->outboundQueue.enqueueAtTail(dispatchEntry);
 }
@@ -1999,16 +2089,11 @@
     case EventEntry::TYPE_KEY: {
         KeyEntry* keyEntry = static_cast<KeyEntry*>(eventEntry);
 
-        // Apply target flags.
-        int32_t action = keyEntry->action;
-        int32_t flags = keyEntry->flags;
-
-        // Update the connection's input state.
-        connection->inputState.trackKey(keyEntry, action);
-
         // Publish the key event.
-        status = connection->inputPublisher.publishKeyEvent(keyEntry->deviceId, keyEntry->source,
-                action, flags, keyEntry->keyCode, keyEntry->scanCode,
+        status = connection->inputPublisher.publishKeyEvent(
+                keyEntry->deviceId, keyEntry->source,
+                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
+                keyEntry->keyCode, keyEntry->scanCode,
                 keyEntry->metaState, keyEntry->repeatCount, keyEntry->downTime,
                 keyEntry->eventTime);
 
@@ -2024,24 +2109,6 @@
     case EventEntry::TYPE_MOTION: {
         MotionEntry* motionEntry = static_cast<MotionEntry*>(eventEntry);
 
-        // Apply target flags.
-        int32_t action = motionEntry->action;
-        int32_t flags = motionEntry->flags;
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
-            action = AMOTION_EVENT_ACTION_OUTSIDE;
-        } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT) {
-            action = AMOTION_EVENT_ACTION_HOVER_EXIT;
-        } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER) {
-            action = AMOTION_EVENT_ACTION_HOVER_ENTER;
-        } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT) {
-            action = AMOTION_EVENT_ACTION_CANCEL;
-        } else if (dispatchEntry->targetFlags & InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER) {
-            action = AMOTION_EVENT_ACTION_DOWN;
-        }
-        if (dispatchEntry->targetFlags & InputTarget::FLAG_WINDOW_IS_OBSCURED) {
-            flags |= AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED;
-        }
-
         // If headMotionSample is non-NULL, then it points to the first new sample that we
         // were unable to dispatch during the previous cycle so we resume dispatching from
         // that point in the list of motion samples.
@@ -2082,13 +2149,11 @@
             }
         }
 
-        // Update the connection's input state.
-        connection->inputState.trackMotion(motionEntry, action);
-
         // Publish the motion event and the first motion sample.
-        status = connection->inputPublisher.publishMotionEvent(motionEntry->deviceId,
-                motionEntry->source, action, flags, motionEntry->edgeFlags,
-                motionEntry->metaState, motionEntry->buttonState,
+        status = connection->inputPublisher.publishMotionEvent(
+                motionEntry->deviceId, motionEntry->source,
+                dispatchEntry->resolvedAction, dispatchEntry->resolvedFlags,
+                motionEntry->edgeFlags, motionEntry->metaState, motionEntry->buttonState,
                 xOffset, yOffset,
                 motionEntry->xPrecision, motionEntry->yPrecision,
                 motionEntry->downTime, firstMotionSample->eventTime,
@@ -2102,8 +2167,8 @@
             return;
         }
 
-        if (action == AMOTION_EVENT_ACTION_MOVE
-                || action == AMOTION_EVENT_ACTION_HOVER_MOVE) {
+        if (dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_MOVE
+                || dispatchEntry->resolvedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
             // Append additional motion samples.
             MotionSample* nextMotionSample = firstMotionSample->next;
             for (; nextMotionSample != NULL; nextMotionSample = nextMotionSample->next) {
@@ -2355,23 +2420,22 @@
                 break;
             }
 
-            int32_t xOffset, yOffset;
-            float scaleFactor;
+            InputTarget target;
             const InputWindow* window = getWindowLocked(connection->inputChannel);
             if (window) {
-                xOffset = -window->frameLeft;
-                yOffset = -window->frameTop;
-                scaleFactor = window->scaleFactor;
+                target.xOffset = -window->frameLeft;
+                target.yOffset = -window->frameTop;
+                target.scaleFactor = window->scaleFactor;
             } else {
-                xOffset = 0;
-                yOffset = 0;
-                scaleFactor = 1.0f;
+                target.xOffset = 0;
+                target.yOffset = 0;
+                target.scaleFactor = 1.0f;
             }
+            target.inputChannel = connection->inputChannel;
+            target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
 
-            DispatchEntry* cancelationDispatchEntry =
-                    mAllocator.obtainDispatchEntry(cancelationEventEntry, // increments ref
-                    0, xOffset, yOffset, scaleFactor);
-            connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry);
+            enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
+                    &target, false, InputTarget::FLAG_DISPATCH_AS_IS);
 
             mAllocator.releaseEventEntry(cancelationEventEntry);
         }
@@ -3327,6 +3391,7 @@
     resetTargetsLocked();
 
     mTouchState.reset();
+    mLastHoverWindow = NULL;
 }
 
 void InputDispatcher::logDispatchStateLocked() {
@@ -4125,111 +4190,186 @@
     return mKeyMementos.isEmpty() && mMotionMementos.isEmpty();
 }
 
-void InputDispatcher::InputState::trackEvent(const EventEntry* entry, int32_t action) {
-    switch (entry->type) {
-    case EventEntry::TYPE_KEY:
-        trackKey(static_cast<const KeyEntry*>(entry), action);
-        break;
+bool InputDispatcher::InputState::isHovering(int32_t deviceId, uint32_t source) const {
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
+        const MotionMemento& memento = mMotionMementos.itemAt(i);
+        if (memento.deviceId == deviceId
+                && memento.source == source
+                && memento.hovering) {
+            return true;
+        }
+    }
+    return false;
+}
 
-    case EventEntry::TYPE_MOTION:
-        trackMotion(static_cast<const MotionEntry*>(entry), action);
-        break;
+bool InputDispatcher::InputState::trackKey(const KeyEntry* entry,
+        int32_t action, int32_t flags) {
+    switch (action) {
+    case AKEY_EVENT_ACTION_UP: {
+        if (entry->flags & AKEY_EVENT_FLAG_FALLBACK) {
+            for (size_t i = 0; i < mFallbackKeys.size(); ) {
+                if (mFallbackKeys.valueAt(i) == entry->keyCode) {
+                    mFallbackKeys.removeItemsAt(i);
+                } else {
+                    i += 1;
+                }
+            }
+        }
+        ssize_t index = findKeyMemento(entry);
+        if (index >= 0) {
+            mKeyMementos.removeAt(index);
+            return true;
+        }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+        LOGD("Dropping inconsistent key up event: deviceId=%d, source=%08x, "
+                "keyCode=%d, scanCode=%d",
+                entry->deviceId, entry->source, entry->keyCode, entry->scanCode);
+#endif
+        return false;
+    }
+
+    case AKEY_EVENT_ACTION_DOWN: {
+        ssize_t index = findKeyMemento(entry);
+        if (index >= 0) {
+            mKeyMementos.removeAt(index);
+        }
+        addKeyMemento(entry, flags);
+        return true;
+    }
+
+    default:
+        return true;
     }
 }
 
-void InputDispatcher::InputState::trackKey(const KeyEntry* entry, int32_t action) {
-    if (action == AKEY_EVENT_ACTION_UP
-            && (entry->flags & AKEY_EVENT_FLAG_FALLBACK)) {
-        for (size_t i = 0; i < mFallbackKeys.size(); ) {
-            if (mFallbackKeys.valueAt(i) == entry->keyCode) {
-                mFallbackKeys.removeItemsAt(i);
-            } else {
-                i += 1;
-            }
+bool InputDispatcher::InputState::trackMotion(const MotionEntry* entry,
+        int32_t action, int32_t flags) {
+    int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
+    switch (actionMasked) {
+    case AMOTION_EVENT_ACTION_UP:
+    case AMOTION_EVENT_ACTION_CANCEL: {
+        ssize_t index = findMotionMemento(entry, false /*hovering*/);
+        if (index >= 0) {
+            mMotionMementos.removeAt(index);
+            return true;
         }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+        LOGD("Dropping inconsistent motion up or cancel event: deviceId=%d, source=%08x, "
+                "actionMasked=%d",
+                entry->deviceId, entry->source, actionMasked);
+#endif
+        return false;
     }
 
+    case AMOTION_EVENT_ACTION_DOWN: {
+        ssize_t index = findMotionMemento(entry, false /*hovering*/);
+        if (index >= 0) {
+            mMotionMementos.removeAt(index);
+        }
+        addMotionMemento(entry, flags, false /*hovering*/);
+        return true;
+    }
+
+    case AMOTION_EVENT_ACTION_POINTER_UP:
+    case AMOTION_EVENT_ACTION_POINTER_DOWN:
+    case AMOTION_EVENT_ACTION_MOVE: {
+        ssize_t index = findMotionMemento(entry, false /*hovering*/);
+        if (index >= 0) {
+            MotionMemento& memento = mMotionMementos.editItemAt(index);
+            memento.setPointers(entry);
+            return true;
+        }
+        if (actionMasked == AMOTION_EVENT_ACTION_MOVE
+                && (entry->source & (AINPUT_SOURCE_CLASS_JOYSTICK
+                        | AINPUT_SOURCE_CLASS_NAVIGATION))) {
+            // Joysticks and trackballs can send MOVE events without corresponding DOWN or UP.
+            return true;
+        }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+        LOGD("Dropping inconsistent motion pointer up/down or move event: "
+                "deviceId=%d, source=%08x, actionMasked=%d",
+                entry->deviceId, entry->source, actionMasked);
+#endif
+        return false;
+    }
+
+    case AMOTION_EVENT_ACTION_HOVER_EXIT: {
+        ssize_t index = findMotionMemento(entry, true /*hovering*/);
+        if (index >= 0) {
+            mMotionMementos.removeAt(index);
+            return true;
+        }
+#if DEBUG_OUTBOUND_EVENT_DETAILS
+        LOGD("Dropping inconsistent motion hover exit event: deviceId=%d, source=%08x",
+                entry->deviceId, entry->source);
+#endif
+        return false;
+    }
+
+    case AMOTION_EVENT_ACTION_HOVER_ENTER:
+    case AMOTION_EVENT_ACTION_HOVER_MOVE: {
+        ssize_t index = findMotionMemento(entry, true /*hovering*/);
+        if (index >= 0) {
+            mMotionMementos.removeAt(index);
+        }
+        addMotionMemento(entry, flags, true /*hovering*/);
+        return true;
+    }
+
+    default:
+        return true;
+    }
+}
+
+ssize_t InputDispatcher::InputState::findKeyMemento(const KeyEntry* entry) const {
     for (size_t i = 0; i < mKeyMementos.size(); i++) {
-        KeyMemento& memento = mKeyMementos.editItemAt(i);
+        const KeyMemento& memento = mKeyMementos.itemAt(i);
         if (memento.deviceId == entry->deviceId
                 && memento.source == entry->source
                 && memento.keyCode == entry->keyCode
                 && memento.scanCode == entry->scanCode) {
-            switch (action) {
-            case AKEY_EVENT_ACTION_UP:
-                mKeyMementos.removeAt(i);
-                return;
-
-            case AKEY_EVENT_ACTION_DOWN:
-                mKeyMementos.removeAt(i);
-                goto Found;
-
-            default:
-                return;
-            }
+            return i;
         }
     }
-
-Found:
-    if (action == AKEY_EVENT_ACTION_DOWN) {
-        mKeyMementos.push();
-        KeyMemento& memento = mKeyMementos.editTop();
-        memento.deviceId = entry->deviceId;
-        memento.source = entry->source;
-        memento.keyCode = entry->keyCode;
-        memento.scanCode = entry->scanCode;
-        memento.flags = entry->flags;
-        memento.downTime = entry->downTime;
-    }
+    return -1;
 }
 
-void InputDispatcher::InputState::trackMotion(const MotionEntry* entry, int32_t action) {
-    int32_t actionMasked = action & AMOTION_EVENT_ACTION_MASK;
+ssize_t InputDispatcher::InputState::findMotionMemento(const MotionEntry* entry,
+        bool hovering) const {
     for (size_t i = 0; i < mMotionMementos.size(); i++) {
-        MotionMemento& memento = mMotionMementos.editItemAt(i);
+        const MotionMemento& memento = mMotionMementos.itemAt(i);
         if (memento.deviceId == entry->deviceId
-                && memento.source == entry->source) {
-            switch (actionMasked) {
-            case AMOTION_EVENT_ACTION_UP:
-            case AMOTION_EVENT_ACTION_CANCEL:
-            case AMOTION_EVENT_ACTION_HOVER_ENTER:
-            case AMOTION_EVENT_ACTION_HOVER_MOVE:
-            case AMOTION_EVENT_ACTION_HOVER_EXIT:
-                mMotionMementos.removeAt(i);
-                return;
-
-            case AMOTION_EVENT_ACTION_DOWN:
-                mMotionMementos.removeAt(i);
-                goto Found;
-
-            case AMOTION_EVENT_ACTION_POINTER_UP:
-            case AMOTION_EVENT_ACTION_POINTER_DOWN:
-            case AMOTION_EVENT_ACTION_MOVE:
-                memento.setPointers(entry);
-                return;
-
-            default:
-                return;
-            }
+                && memento.source == entry->source
+                && memento.hovering == hovering) {
+            return i;
         }
     }
+    return -1;
+}
 
-Found:
-    switch (actionMasked) {
-    case AMOTION_EVENT_ACTION_DOWN:
-    case AMOTION_EVENT_ACTION_HOVER_ENTER:
-    case AMOTION_EVENT_ACTION_HOVER_MOVE:
-    case AMOTION_EVENT_ACTION_HOVER_EXIT:
-        mMotionMementos.push();
-        MotionMemento& memento = mMotionMementos.editTop();
-        memento.deviceId = entry->deviceId;
-        memento.source = entry->source;
-        memento.xPrecision = entry->xPrecision;
-        memento.yPrecision = entry->yPrecision;
-        memento.downTime = entry->downTime;
-        memento.setPointers(entry);
-        memento.hovering = actionMasked != AMOTION_EVENT_ACTION_DOWN;
-    }
+void InputDispatcher::InputState::addKeyMemento(const KeyEntry* entry, int32_t flags) {
+    mKeyMementos.push();
+    KeyMemento& memento = mKeyMementos.editTop();
+    memento.deviceId = entry->deviceId;
+    memento.source = entry->source;
+    memento.keyCode = entry->keyCode;
+    memento.scanCode = entry->scanCode;
+    memento.flags = flags;
+    memento.downTime = entry->downTime;
+}
+
+void InputDispatcher::InputState::addMotionMemento(const MotionEntry* entry,
+        int32_t flags, bool hovering) {
+    mMotionMementos.push();
+    MotionMemento& memento = mMotionMementos.editTop();
+    memento.deviceId = entry->deviceId;
+    memento.source = entry->source;
+    memento.flags = flags;
+    memento.xPrecision = entry->xPrecision;
+    memento.yPrecision = entry->yPrecision;
+    memento.downTime = entry->downTime;
+    memento.setPointers(entry);
+    memento.hovering = hovering;
 }
 
 void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) {
@@ -4243,20 +4383,17 @@
 void InputDispatcher::InputState::synthesizeCancelationEvents(nsecs_t currentTime,
         Allocator* allocator, Vector<EventEntry*>& outEvents,
         const CancelationOptions& options) {
-    for (size_t i = 0; i < mKeyMementos.size(); ) {
+    for (size_t i = 0; i < mKeyMementos.size(); i++) {
         const KeyMemento& memento = mKeyMementos.itemAt(i);
         if (shouldCancelKey(memento, options)) {
             outEvents.push(allocator->obtainKeyEntry(currentTime,
                     memento.deviceId, memento.source, 0,
                     AKEY_EVENT_ACTION_UP, memento.flags | AKEY_EVENT_FLAG_CANCELED,
                     memento.keyCode, memento.scanCode, 0, 0, memento.downTime));
-            mKeyMementos.removeAt(i);
-        } else {
-            i += 1;
         }
     }
 
-    for (size_t i = 0; i < mMotionMementos.size(); ) {
+    for (size_t i = 0; i < mMotionMementos.size(); i++) {
         const MotionMemento& memento = mMotionMementos.itemAt(i);
         if (shouldCancelMotion(memento, options)) {
             outEvents.push(allocator->obtainMotionEntry(currentTime,
@@ -4264,12 +4401,9 @@
                     memento.hovering
                             ? AMOTION_EVENT_ACTION_HOVER_EXIT
                             : AMOTION_EVENT_ACTION_CANCEL,
-                    0, 0, 0, 0,
+                    memento.flags, 0, 0, 0,
                     memento.xPrecision, memento.yPrecision, memento.downTime,
                     memento.pointerCount, memento.pointerProperties, memento.pointerCoords));
-            mMotionMementos.removeAt(i);
-        } else {
-            i += 1;
         }
     }
 }
diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h
index 676d162..bdd1922 100644
--- a/services/input/InputDispatcher.h
+++ b/services/input/InputDispatcher.h
@@ -522,6 +522,10 @@
         // True if dispatch has started.
         bool inProgress;
 
+        // Set to the resolved action and flags when the event is enqueued.
+        int32_t resolvedAction;
+        int32_t resolvedFlags;
+
         // For motion events:
         //   Pointer to the first motion sample to dispatch in this cycle.
         //   Usually NULL to indicate that the list of motion samples begins at
@@ -709,14 +713,19 @@
         // Returns true if there is no state to be canceled.
         bool isNeutral() const;
 
-        // Records tracking information for an event that has just been published.
-        void trackEvent(const EventEntry* entry, int32_t action);
+        // Returns true if the specified source is known to have received a hover enter
+        // motion event.
+        bool isHovering(int32_t deviceId, uint32_t source) const;
 
         // Records tracking information for a key event that has just been published.
-        void trackKey(const KeyEntry* entry, int32_t action);
+        // Returns true if the event should be delivered, false if it is inconsistent
+        // and should be skipped.
+        bool trackKey(const KeyEntry* entry, int32_t action, int32_t flags);
 
         // Records tracking information for a motion event that has just been published.
-        void trackMotion(const MotionEntry* entry, int32_t action);
+        // Returns true if the event should be delivered, false if it is inconsistent
+        // and should be skipped.
+        bool trackMotion(const MotionEntry* entry, int32_t action, int32_t flags);
 
         // Synthesizes cancelation events for the current state and resets the tracked state.
         void synthesizeCancelationEvents(nsecs_t currentTime, Allocator* allocator,
@@ -756,6 +765,7 @@
         struct MotionMemento {
             int32_t deviceId;
             uint32_t source;
+            int32_t flags;
             float xPrecision;
             float yPrecision;
             nsecs_t downTime;
@@ -771,6 +781,12 @@
         Vector<MotionMemento> mMotionMementos;
         KeyedVector<int32_t, int32_t> mFallbackKeys;
 
+        ssize_t findKeyMemento(const KeyEntry* entry) const;
+        ssize_t findMotionMemento(const MotionEntry* entry, bool hovering) const;
+
+        void addKeyMemento(const KeyEntry* entry, int32_t flags);
+        void addMotionMemento(const MotionEntry* entry, int32_t flags, bool hovering);
+
         static bool shouldCancelKey(const KeyMemento& memento,
                 const CancelationOptions& options);
         static bool shouldCancelMotion(const MotionMemento& memento,
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index 3e4c666..82c3af3 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -53,6 +53,7 @@
 #define INDENT2 "    "
 #define INDENT3 "      "
 #define INDENT4 "        "
+#define INDENT5 "          "
 
 namespace android {
 
@@ -154,14 +155,15 @@
     case BTN_LEFT:
         return AMOTION_EVENT_BUTTON_PRIMARY;
     case BTN_RIGHT:
+    case BTN_STYLUS:
         return AMOTION_EVENT_BUTTON_SECONDARY;
     case BTN_MIDDLE:
+    case BTN_STYLUS2:
         return AMOTION_EVENT_BUTTON_TERTIARY;
     case BTN_SIDE:
         return AMOTION_EVENT_BUTTON_BACK;
-    case BTN_EXTRA:
-        return AMOTION_EVENT_BUTTON_FORWARD;
     case BTN_FORWARD:
+    case BTN_EXTRA:
         return AMOTION_EVENT_BUTTON_FORWARD;
     case BTN_BACK:
         return AMOTION_EVENT_BUTTON_BACK;
@@ -176,8 +178,7 @@
 static bool isPointerDown(int32_t buttonState) {
     return buttonState &
             (AMOTION_EVENT_BUTTON_PRIMARY | AMOTION_EVENT_BUTTON_SECONDARY
-                    | AMOTION_EVENT_BUTTON_TERTIARY
-                    | AMOTION_EVENT_BUTTON_ERASER);
+                    | AMOTION_EVENT_BUTTON_TERTIARY);
 }
 
 static int32_t calculateEdgeFlagsUsingPointerBounds(
@@ -921,7 +922,7 @@
     for (const RawEvent* rawEvent = rawEvents; count--; rawEvent++) {
 #if DEBUG_RAW_EVENTS
         LOGD("Input event: device=%d type=0x%04x scancode=0x%04x "
-                "keycode=0x%04x value=0x%04x flags=0x%08x",
+                "keycode=0x%04x value=0x%08x flags=0x%08x",
                 rawEvent->deviceId, rawEvent->type, rawEvent->scanCode, rawEvent->keyCode,
                 rawEvent->value, rawEvent->flags);
 #endif
@@ -1921,8 +1922,17 @@
         dump.appendFormat(INDENT4 "DistanceScale: %0.3f\n", mLocked.distanceScale);
 
         dump.appendFormat(INDENT3 "Last Touch:\n");
-        dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount);
         dump.appendFormat(INDENT4 "Button State: 0x%08x\n", mLastTouch.buttonState);
+        dump.appendFormat(INDENT4 "Pointer Count: %d\n", mLastTouch.pointerCount);
+        for (uint32_t i = 0; i < mLastTouch.pointerCount; i++) {
+            const PointerData& pointer = mLastTouch.pointers[i];
+            dump.appendFormat(INDENT5 "[%d]: id=%d, x=%d, y=%d, pressure=%d, "
+                    "touchMajor=%d, touchMinor=%d, toolMajor=%d, toolMinor=%d, "
+                    "orientation=%d, distance=%d, isStylus=%s\n", i,
+                    pointer.id, pointer.x, pointer.y, pointer.pressure,
+                    pointer.touchMajor, pointer.touchMinor, pointer.toolMajor, pointer.toolMinor,
+                    pointer.orientation, pointer.distance, toString(pointer.isStylus));
+        }
 
         if (mParameters.deviceType == Parameters::DEVICE_TYPE_POINTER) {
             dump.appendFormat(INDENT3 "Pointer Gesture Detector:\n");
@@ -3704,6 +3714,29 @@
                 mPointerGesture.currentGestureCoords, mPointerGesture.currentGestureIdToIndex,
                 mPointerGesture.currentGestureIdBits, -1,
                 0, 0, mPointerGesture.downTime);
+    } else if (dispatchedGestureIdBits.isEmpty()
+            && !mPointerGesture.lastGestureIdBits.isEmpty()) {
+        // Synthesize a hover move event after all pointers go up to indicate that
+        // the pointer is hovering again even if the user is not currently touching
+        // the touch pad.  This ensures that a view will receive a fresh hover enter
+        // event after a tap.
+        float x, y;
+        mPointerController->getPosition(&x, &y);
+
+        PointerProperties pointerProperties;
+        pointerProperties.clear();
+        pointerProperties.id = 0;
+        pointerProperties.toolType = AMOTION_EVENT_TOOL_TYPE_INDIRECT_FINGER;
+
+        PointerCoords pointerCoords;
+        pointerCoords.clear();
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_X, x);
+        pointerCoords.setAxisValue(AMOTION_EVENT_AXIS_Y, y);
+
+        getDispatcher()->notifyMotion(when, getDeviceId(), mPointerSource, policyFlags,
+                AMOTION_EVENT_ACTION_HOVER_MOVE, 0,
+                metaState, buttonState, AMOTION_EVENT_EDGE_FLAG_NONE,
+                1, &pointerProperties, &pointerCoords, 0, 0, mPointerGesture.downTime);
     }
 
     // Update state.
diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp
index 08cc75e..0ae2ab8 100644
--- a/services/input/SpriteController.cpp
+++ b/services/input/SpriteController.cpp
@@ -252,11 +252,7 @@
                         | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
             status_t status;
             if (!haveTransaction) {
-                status = mSurfaceComposerClient->openTransaction();
-                if (status) {
-                    LOGE("Error %d opening transation to update sprite surface.", status);
-                    break;
-                }
+                SurfaceComposerClient::openGlobalTransaction();
                 haveTransaction = true;
             }
 
@@ -322,10 +318,7 @@
     }
 
     if (haveTransaction) {
-        status_t status = mSurfaceComposerClient->closeTransaction();
-        if (status) {
-            LOGE("Error %d closing transaction to update sprite surface.", status);
-        }
+        SurfaceComposerClient::closeGlobalTransaction();
     }
 
     // If any surfaces were changed, write back the new surface properties to the sprites.
diff --git a/services/java/com/android/server/DnsPinger.java b/services/java/com/android/server/DnsPinger.java
index 05de53a..a7324d9 100644
--- a/services/java/com/android/server/DnsPinger.java
+++ b/services/java/com/android/server/DnsPinger.java
@@ -20,7 +20,9 @@
 import android.content.Context;
 import android.net.ConnectivityManager;
 import android.net.LinkProperties;
+import android.net.NetworkUtils;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.util.Slog;
 
 import java.net.DatagramPacket;
@@ -38,7 +40,7 @@
  * 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 : More general API. Socket does not bind to specified connection type
  * TODO : Choice of DNS query location - current looks up www.android.com
  *
  * @hide
@@ -56,44 +58,65 @@
     private static Random sRandom = new Random();
 
     private ConnectivityManager mConnectivityManager = null;
-    private ContentResolver mContentResolver;
     private Context mContext;
     private int mConnectionType;
+    private InetAddress mDefaultDns;
 
     private String TAG;
 
-
     /**
-     * @param connectionType The connection type from @link {@link ConnectivityManager}
+     * @param connectionType The connection type from {@link ConnectivityManager}
      */
     public DnsPinger(String TAG, Context context, int connectionType) {
         mContext = context;
-        mContentResolver = context.getContentResolver();
         mConnectionType = connectionType;
+        if (!ConnectivityManager.isNetworkTypeValid(connectionType)) {
+            Slog.e(TAG, "Invalid connectionType in constructor: " + connectionType);
+        }
         this.TAG = TAG;
+
+        mDefaultDns = getDefaultDns();
     }
 
     /**
-     * @return The first DNS in the link properties of the specified connection type
+     * @return The first DNS in the link properties of the specified connection
+     *         type or the default system DNS if the link properties has null
+     *         dns set. Should not be null.
      */
     public InetAddress getDns() {
-        LinkProperties linkProperties = getCurLinkProperties();
-        if (linkProperties == null)
-            return null;
-
-        Collection<InetAddress> dnses = linkProperties.getDnses();
-        if (dnses == null || dnses.size() == 0)
-            return null;
-
-        return dnses.iterator().next();
-    }
-
-    private LinkProperties getCurLinkProperties() {
         if (mConnectivityManager == null) {
             mConnectivityManager = (ConnectivityManager) mContext.getSystemService(
                     Context.CONNECTIVITY_SERVICE);
         }
-        return mConnectivityManager.getLinkProperties(mConnectionType);
+
+        LinkProperties curLinkProps = mConnectivityManager.getLinkProperties(mConnectionType);
+        if (curLinkProps == null) {
+            Slog.e(TAG, "getCurLinkProperties:: LP for type" + mConnectionType + " is null!");
+            return mDefaultDns;
+        }
+
+        Collection<InetAddress> dnses = curLinkProps.getDnses();
+        if (dnses == null || dnses.size() == 0) {
+            Slog.v(TAG, "getDns::LinkProps has null dns - returning default");
+            return mDefaultDns;
+        }
+
+        return dnses.iterator().next();
+    }
+
+    private InetAddress getDefaultDns() {
+        String dns = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.DEFAULT_DNS_SERVER);
+        if (dns == null || dns.length() == 0) {
+            dns = mContext.getResources().getString(
+                    com.android.internal.R.string.config_default_dns_server);
+        }
+        try {
+            return NetworkUtils.numericToInetAddress(dns);
+        } catch (IllegalArgumentException e) {
+            Slog.w(TAG, "getDefaultDns::malformed default dns address");
+            return null;
+        }
     }
 
     /**
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index b03bd4d..14abf80 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1641,7 +1641,7 @@
             final InputMethodInfo imi = mMethodMap.get(mCurMethodId);
             if (imi == null) return false;
             final int N = subtypes.length;
-            mFileManager.addInputMethodSubtypes(mCurMethodId, subtypes);
+            mFileManager.addInputMethodSubtypes(imi, subtypes);
             buildInputMethodListLocked(mMethodList, mMethodMap);
             return true;
         }
@@ -2023,25 +2023,26 @@
                 final CharSequence label = imi.loadLabel(pm);
                 if (showSubtypes && enabledSubtypeSet.size() > 0) {
                     final int subtypeCount = imi.getSubtypeCount();
+                    if (DEBUG) {
+                        Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
+                    }
                     for (int j = 0; j < subtypeCount; ++j) {
-                        InputMethodSubtype subtype = imi.getSubtypeAt(j);
-                        if (enabledSubtypeSet.contains(String.valueOf(subtype.hashCode()))
-                                && !subtype.isAuxiliary()) {
+                        final InputMethodSubtype subtype = imi.getSubtypeAt(j);
+                        final String subtypeHashCode = String.valueOf(subtype.hashCode());
+                        // We show all enabled IMEs and subtypes when an IME is shown.
+                        if (enabledSubtypeSet.contains(subtypeHashCode)
+                                && (mInputShown || !subtype.isAuxiliary())) {
                             final CharSequence title;
-                            int nameResId = subtype.getNameResId();
-                            String mode = subtype.getMode();
-                            if (nameResId != 0) {
-                                title = TextUtils.concat(subtype.getDisplayName(context,
-                                        imi.getPackageName(), imi.getServiceInfo().applicationInfo),
-                                        (TextUtils.isEmpty(label) ? "" : " (" + label + ")"));
-                            } else {
-                                CharSequence language = subtype.getLocale();
-                                // TODO: Use more friendly Title and UI
-                                title = label + "," + (mode == null ? "" : mode) + ","
-                                        + (language == null ? "" : language);
-                            }
+                            final String mode = subtype.getMode();
+                            title = TextUtils.concat(subtype.getDisplayName(context,
+                                    imi.getPackageName(), imi.getServiceInfo().applicationInfo),
+                                    (TextUtils.isEmpty(label) ? "" : " (" + label + ")"));
                             imList.add(new Pair<CharSequence, Pair<InputMethodInfo, Integer>>(
                                     title, new Pair<InputMethodInfo, Integer>(imi, j)));
+                            // Removing this subtype from enabledSubtypeSet because we no longer
+                            // need to add an entry of this subtype to imList to avoid duplicated
+                            // entries.
+                            enabledSubtypeSet.remove(subtypeHashCode);
                         }
                     }
                 } else {
@@ -2338,7 +2339,7 @@
                 }
             }
         }
-        ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
+        final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>(
                 applicableModeAndSubtypesMap.values());
         if (!containsKeyboardSubtype) {
             InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked(
@@ -3016,17 +3017,23 @@
         }
 
         public void addInputMethodSubtypes(
-                String imiId, InputMethodSubtype[] additionalSubtypes) {
+                InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) {
             synchronized (mMethodMap) {
+                final HashSet<InputMethodSubtype> existingSubtypes =
+                        new HashSet<InputMethodSubtype>();
+                for (int i = 0; i < imi.getSubtypeCount(); ++i) {
+                    existingSubtypes.add(imi.getSubtypeAt(i));
+                }
+
                 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
                 final int N = additionalSubtypes.length;
                 for (int i = 0; i < N; ++i) {
                     final InputMethodSubtype subtype = additionalSubtypes[i];
-                    if (!subtypes.contains(subtype)) {
+                    if (!subtypes.contains(subtype) && !existingSubtypes.contains(subtype)) {
                         subtypes.add(subtype);
                     }
                 }
-                mSubtypesMap.put(imiId, subtypes);
+                mSubtypesMap.put(imi.getId(), subtypes);
                 writeAdditionalInputMethodSubtypes(mSubtypesMap, mAdditionalInputMethodSubtypeFile,
                         mMethodMap);
             }
@@ -3133,8 +3140,8 @@
                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_MODE);
                         final String imeSubtypeExtraValue =
                                 parser.getAttributeValue(null, ATTR_IME_SUBTYPE_EXTRA_VALUE);
-                        final boolean isAuxiliary =
-                                Boolean.valueOf(parser.getAttributeValue(null, ATTR_IS_AUXILIARY));
+                        final boolean isAuxiliary = "1".equals(String.valueOf(
+                                parser.getAttributeValue(null, ATTR_IS_AUXILIARY)));
                         final InputMethodSubtype subtype =
                                 new InputMethodSubtype(label, icon, imeSubtypeLocale,
                                         imeSubtypeMode, imeSubtypeExtraValue, isAuxiliary);
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d3244ec..54e5432 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1626,6 +1626,30 @@
         }
     }
 
+    public String getSecureContainerFilesystemPath(String id) {
+        validatePermission(android.Manifest.permission.ASEC_ACCESS);
+        waitForReady();
+        warnOnNotMounted();
+
+        try {
+            ArrayList<String> rsp = mConnector.doCommand(String.format("asec fspath %s", id));
+            String []tok = rsp.get(0).split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code != VoldResponseCode.AsecPathResult) {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+            return tok[1];
+        } catch (NativeDaemonConnectorException e) {
+            int code = e.getCode();
+            if (code == VoldResponseCode.OpFailedStorageNotFound) {
+                Slog.i(TAG, String.format("Container '%s' not found", id));
+                return null;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+    }
+
     public void finishMediaUpdate() {
         mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
     }
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index e730d0d..adc6570 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -37,6 +37,11 @@
 import android.os.SystemProperties;
 import android.util.Log;
 import android.util.Slog;
+import android.util.SparseBooleanArray;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
 
 import java.io.BufferedReader;
 import java.io.DataInputStream;
@@ -48,6 +53,8 @@
 import java.net.Inet4Address;
 import java.net.InetAddress;
 import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
 import java.util.NoSuchElementException;
 import java.util.StringTokenizer;
 import java.util.concurrent.CountDownLatch;
@@ -65,9 +72,18 @@
     private static final int ADD = 1;
     private static final int REMOVE = 2;
 
+    /** Path to {@code /proc/uid_stat}. */
     @Deprecated
-    private static final File STATS_UIDSTAT = new File("/proc/uid_stat");
-    private static final File STATS_NETFILTER = new File("/proc/net/xt_qtaguid/stats");
+    private final File mProcStatsUidstat;
+    /** Path to {@code /proc/net/xt_qtaguid/stats}. */
+    private final File mProcStatsNetfilter;
+
+    /** {@link #mProcStatsNetfilter} headers. */
+    private static final String KEY_IFACE = "iface";
+    private static final String KEY_TAG_HEX = "acct_tag_hex";
+    private static final String KEY_UID = "uid_tag_int";
+    private static final String KEY_RX = "rx_bytes";
+    private static final String KEY_TX = "tx_bytes";
 
     class NetdResponseCode {
         public static final int InterfaceListResult       = 110;
@@ -102,15 +118,23 @@
 
     private ArrayList<INetworkManagementEventObserver> mObservers;
 
+    /** Set of interfaces with active quotas. */
+    private HashSet<String> mInterfaceQuota = Sets.newHashSet();
+    /** Set of UIDs with active reject rules. */
+    private SparseBooleanArray mUidRejectOnQuota = new SparseBooleanArray();
+
     /**
      * Constructs a new NetworkManagementService instance
      *
      * @param context  Binder context for this service
      */
-    private NetworkManagementService(Context context) {
+    private NetworkManagementService(Context context, File procRoot) {
         mContext = context;
         mObservers = new ArrayList<INetworkManagementEventObserver>();
 
+        mProcStatsUidstat = new File(procRoot, "uid_stat");
+        mProcStatsNetfilter = new File(procRoot, "net/xt_qtaguid/stats");
+
         if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
             return;
         }
@@ -121,7 +145,8 @@
     }
 
     public static NetworkManagementService create(Context context) throws InterruptedException {
-        NetworkManagementService service = new NetworkManagementService(context);
+        NetworkManagementService service = new NetworkManagementService(
+                context, new File("/proc/"));
         if (DBG) Slog.d(TAG, "Creating NetworkManagementService");
         service.mThread.start();
         if (DBG) Slog.d(TAG, "Awaiting socket connection");
@@ -130,6 +155,12 @@
         return service;
     }
 
+    // @VisibleForTesting
+    public static NetworkManagementService createForTest(Context context, File procRoot) {
+        // TODO: eventually connect with mock netd
+        return new NetworkManagementService(context, procRoot);
+    }
+
     public void registerObserver(INetworkManagementEventObserver obs) {
         Slog.d(TAG, "Registering observer");
         mObservers.add(obs);
@@ -888,7 +919,7 @@
         mContext.enforceCallingOrSelfPermission(
                 android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
 
-        if (STATS_NETFILTER.exists()) {
+        if (mProcStatsNetfilter.exists()) {
             return getNetworkStatsDetailNetfilter(UID_ALL);
         } else {
             return getNetworkStatsDetailUidstat(UID_ALL);
@@ -896,13 +927,91 @@
     }
 
     @Override
+    public void setInterfaceQuota(String iface, long quota) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        synchronized (mInterfaceQuota) {
+            if (mInterfaceQuota.contains(iface)) {
+                // TODO: eventually consider throwing
+                return;
+            }
+
+            final StringBuilder command = new StringBuilder();
+            command.append("bandwidth setiquota ").append(iface).append(" ").append(quota);
+
+            try {
+                // TODO: add support for quota shared across interfaces
+                mConnector.doCommand(command.toString());
+                mInterfaceQuota.add(iface);
+            } catch (NativeDaemonConnectorException e) {
+                throw new IllegalStateException("Error communicating to native daemon", e);
+            }
+        }
+    }
+
+    @Override
+    public void removeInterfaceQuota(String iface) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        synchronized (mInterfaceQuota) {
+            if (!mInterfaceQuota.contains(iface)) {
+                // TODO: eventually consider throwing
+                return;
+            }
+
+            final StringBuilder command = new StringBuilder();
+            command.append("bandwidth removeiquota ").append(iface);
+
+            try {
+                // TODO: add support for quota shared across interfaces
+                mConnector.doCommand(command.toString());
+                mInterfaceQuota.remove(iface);
+            } catch (NativeDaemonConnectorException e) {
+                throw new IllegalStateException("Error communicating to native daemon", e);
+            }
+        }
+    }
+
+    @Override
+    public void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+        mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG);
+
+        synchronized (mUidRejectOnQuota) {
+            final boolean oldRejectOnQuota = mUidRejectOnQuota.get(uid, false);
+            if (oldRejectOnQuota == rejectOnQuotaInterfaces) {
+                // TODO: eventually consider throwing
+                return;
+            }
+
+            final StringBuilder command = new StringBuilder();
+            command.append("bandwidth");
+            if (rejectOnQuotaInterfaces) {
+                command.append(" addnaughtyapps");
+            } else {
+                command.append(" removenaughtyapps");
+            }
+            command.append(" ").append(uid);
+
+            try {
+                mConnector.doCommand(command.toString());
+                if (rejectOnQuotaInterfaces) {
+                    mUidRejectOnQuota.put(uid, true);
+                } else {
+                    mUidRejectOnQuota.delete(uid);
+                }
+            } catch (NativeDaemonConnectorException e) {
+                throw new IllegalStateException("Error communicating to native daemon", e);
+            }
+        }
+    }
+
     public NetworkStats getNetworkStatsUidDetail(int uid) {
         if (Binder.getCallingUid() != uid) {
             mContext.enforceCallingOrSelfPermission(
                     android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService");
         }
 
-        if (STATS_NETFILTER.exists()) {
+        if (mProcStatsNetfilter.exists()) {
             return getNetworkStatsDetailNetfilter(uid);
         } else {
             return getNetworkStatsDetailUidstat(uid);
@@ -914,35 +1023,36 @@
      */
     private NetworkStats getNetworkStatsDetailNetfilter(int limitUid) {
         final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 24);
+        final ArrayList<String> keys = Lists.newArrayList();
+        final ArrayList<String> values = Lists.newArrayList();
+        final HashMap<String, String> parsed = Maps.newHashMap();
 
         BufferedReader reader = null;
         try {
-            reader = new BufferedReader(new FileReader(STATS_NETFILTER));
+            reader = new BufferedReader(new FileReader(mProcStatsNetfilter));
 
-            // assumes format from kernel:
-            // idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes
-
-            // skip first line, which is legend
+            // parse first line as header
             String line = reader.readLine();
-            while ((line = reader.readLine()) != null) {
-                final StringTokenizer t = new StringTokenizer(line);
+            splitLine(line, keys);
 
-                final String idx = t.nextToken();
-                final String iface = t.nextToken();
+            // parse remaining lines
+            while ((line = reader.readLine()) != null) {
+                splitLine(line, values);
+                parseLine(keys, values, parsed);
 
                 try {
-                    // TODO: kernel currently emits tag in upper half of long;
-                    // eventually switch to directly using int.
-                    final int tag = (int) (Long.parseLong(t.nextToken().substring(2), 16) >> 32);
-                    final int uid = Integer.parseInt(t.nextToken());
-                    final long rx = Long.parseLong(t.nextToken());
-                    final long tx = Long.parseLong(t.nextToken());
+                    final String iface = parsed.get(KEY_IFACE);
+                    final int tag = NetworkManagementSocketTagger.kernelToTag(
+                            parsed.get(KEY_TAG_HEX));
+                    final int uid = Integer.parseInt(parsed.get(KEY_UID));
+                    final long rx = Long.parseLong(parsed.get(KEY_RX));
+                    final long tx = Long.parseLong(parsed.get(KEY_TX));
 
                     if (limitUid == UID_ALL || limitUid == uid) {
                         stats.addEntry(iface, uid, tag, rx, tx);
                     }
                 } catch (NumberFormatException e) {
-                    Slog.w(TAG, "problem parsing stats for idx " + idx + ": " + e);
+                    Slog.w(TAG, "problem parsing stats row '" + line + "': " + e);
                 }
             }
         } catch (IOException e) {
@@ -964,7 +1074,7 @@
     private NetworkStats getNetworkStatsDetailUidstat(int limitUid) {
         final String[] knownUids;
         if (limitUid == UID_ALL) {
-            knownUids = STATS_UIDSTAT.list();
+            knownUids = mProcStatsUidstat.list();
         } else {
             knownUids = new String[] { String.valueOf(limitUid) };
         }
@@ -973,7 +1083,7 @@
                 SystemClock.elapsedRealtime(), knownUids.length);
         for (String uid : knownUids) {
             final int uidInt = Integer.parseInt(uid);
-            final File uidPath = new File(STATS_UIDSTAT, uid);
+            final File uidPath = new File(mProcStatsUidstat, uid);
             final long rx = readSingleLongFromFile(new File(uidPath, "tcp_rcv"));
             final long tx = readSingleLongFromFile(new File(uidPath, "tcp_snd"));
             stats.addEntry(IFACE_ALL, uidInt, TAG_NONE, rx, tx);
@@ -1048,6 +1158,32 @@
     }
 
     /**
+     * Split given line into {@link ArrayList}.
+     */
+    private static void splitLine(String line, ArrayList<String> outSplit) {
+        outSplit.clear();
+
+        final StringTokenizer t = new StringTokenizer(line);
+        while (t.hasMoreTokens()) {
+            outSplit.add(t.nextToken());
+        }
+    }
+
+    /**
+     * Zip the two given {@link ArrayList} as key and value pairs into
+     * {@link HashMap}.
+     */
+    private static void parseLine(
+            ArrayList<String> keys, ArrayList<String> values, HashMap<String, String> outParsed) {
+        outParsed.clear();
+
+        final int size = Math.min(keys.size(), values.size());
+        for (int i = 0; i < size; i++) {
+            outParsed.put(keys.get(i), values.get(i));
+        }
+    }
+
+    /**
      * Utility method to read a single plain-text {@link Long} from the given
      * {@link File}, usually from a {@code /proc/} filesystem.
      */
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index cd68c68..dbfd145 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -283,7 +283,8 @@
             try {
                 Slog.i(TAG, "NetworkPolicy Service");
                 networkPolicy = new NetworkPolicyManagerService(
-                        context, ActivityManagerService.self(), power, networkStats);
+                        context, ActivityManagerService.self(), power,
+                        networkStats, networkManagement);
                 ServiceManager.addService(Context.NETWORK_POLICY_SERVICE, networkPolicy);
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting NetworkPolicy Service", e);
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 3ba9c14..1356e2a 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -95,14 +95,14 @@
     private static final long MIN_SINGLE_DNS_CHECK_INTERVAL = 10 * 60 * 1000;
     private static final long MIN_WALLED_GARDEN_INTERVAL = 15 * 60 * 1000;
 
-    private static final int MAX_CHECKS_PER_SSID = 7;
-    private static final int NUM_DNS_PINGS = 5;
+    private static final int MAX_CHECKS_PER_SSID = 9;
+    private static final int NUM_DNS_PINGS = 7;
     private static double MIN_RESPONSE_RATE = 0.50;
 
     // TODO : Adjust multiple DNS downward to 250 on repeated failure
     // private static final int MULTI_DNS_PING_TIMEOUT_MS = 250;
 
-    private static final int DNS_PING_TIMEOUT_MS = 1000;
+    private static final int DNS_PING_TIMEOUT_MS = 800;
     private static final long DNS_PING_INTERVAL = 250;
 
     private static final long BLACKLIST_FOLLOWUP_INTERVAL = 15 * 1000;
diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java
index b4fdc9f..293702d 100644
--- a/services/java/com/android/server/am/BatteryStatsService.java
+++ b/services/java/com/android/server/am/BatteryStatsService.java
@@ -446,6 +446,15 @@
                 Binder.getCallingPid(), Binder.getCallingUid(), null);
     }
     
+    private void dumpHelp(PrintWriter pw) {
+        pw.println("Battery stats (batteryinfo) dump options:");
+        pw.println("  [--checkin] [--reset] [--write] [-h]");
+        pw.println("  --checkin: format output for a checkin report.");
+        pw.println("  --reset: reset the stats, clearing all current data.");
+        pw.println("  --write: force write current collected stats to disk.");
+        pw.println("  -h: print this help text.");
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         boolean isCheckin = false;
@@ -466,8 +475,12 @@
                         pw.println("Battery stats written.");
                         noOutput = true;
                     }
+                } else if ("-h".equals(arg)) {
+                    dumpHelp(pw);
+                    return;
                 } else {
                     pw.println("Unknown option: " + arg);
+                    dumpHelp(pw);
                 }
             }
         }
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
index db3b61e..f5efda9 100644
--- a/services/java/com/android/server/connectivity/Vpn.java
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -27,15 +27,23 @@
 import android.graphics.Canvas;
 import android.graphics.drawable.Drawable;
 import android.net.INetworkManagementEventObserver;
+import android.net.LocalSocket;
+import android.net.LocalSocketAddress;
 import android.os.Binder;
 import android.os.ParcelFileDescriptor;
-import android.os.RemoteException;
+import android.os.Process;
+import android.os.SystemClock;
+import android.os.SystemProperties;
 import android.util.Log;
 
 import com.android.internal.R;
 import com.android.internal.net.VpnConfig;
 import com.android.server.ConnectivityService.VpnCallback;
 
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.charset.Charsets;
+
 /**
  * @hide
  */
@@ -49,7 +57,8 @@
 
     private String mPackageName;
     private String mInterfaceName;
-    private String mDnsPropertyPrefix;
+
+    private LegacyVpnRunner mLegacyVpnRunner;
 
     public Vpn(Context context, VpnCallback callback) {
         mContext = context;
@@ -249,4 +258,195 @@
     private native void nativeReset(String name);
     private native int nativeCheck(String name);
     private native void nativeProtect(int fd, String name);
+
+    /**
+     * Handle legacy VPN requests. This method stops the services and restart
+     * them if their arguments are not null. Heavy things are offloaded to
+     * another thread, so callers will not be blocked too long.
+     *
+     * @param raoocn The arguments to be passed to racoon.
+     * @param mtpd The arguments to be passed to mtpd.
+     */
+    public synchronized void doLegacyVpn(String[] racoon, String[] mtpd) {
+        // Currently only system user is allowed.
+        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+            throw new SecurityException("Unauthorized Caller");
+        }
+
+        // If the previous runner is still alive, interrupt it.
+        if (mLegacyVpnRunner != null && mLegacyVpnRunner.isAlive()) {
+            mLegacyVpnRunner.interrupt();
+        }
+
+        // Start a new runner and we are done!
+        mLegacyVpnRunner = new LegacyVpnRunner(
+                new String[] {"racoon", "mtpd"}, racoon, mtpd);
+        mLegacyVpnRunner.start();
+    }
+
+    /**
+     * Bringing up a VPN connection takes time, and that is all this thread
+     * does. Here we have plenty of time. The only thing we need to take
+     * care of is responding to interruptions as soon as possible. Otherwise
+     * requests will be piled up. This can be done in a Handler as a state
+     * machine, but it is much easier to read in the current form.
+     */
+    private class LegacyVpnRunner extends Thread {
+        private static final String TAG = "LegacyVpnRunner";
+
+        private static final String NONE = "--";
+
+        private final String[] mServices;
+        private final String[][] mArguments;
+        private long mTimer = -1;
+
+        public LegacyVpnRunner(String[] services, String[]... arguments) {
+            super(TAG);
+            mServices = services;
+            mArguments = arguments;
+        }
+
+        @Override
+        public void run() {
+            // Wait for the previous thread since it has been interrupted.
+            Log.v(TAG, "wait");
+            synchronized (TAG) {
+                Log.v(TAG, "run");
+                execute();
+                Log.v(TAG, "exit");
+            }
+        }
+
+        private void checkpoint(boolean yield) throws InterruptedException {
+            long now = SystemClock.elapsedRealtime();
+            if (mTimer == -1) {
+                mTimer = now;
+                Thread.sleep(1);
+            } else if (now - mTimer <= 30000) {
+                Thread.sleep(yield ? 200 : 1);
+            } else {
+                throw new InterruptedException("timeout");
+            }
+        }
+
+        private void execute() {
+            // Catch all exceptions so we can clean up few things.
+            try {
+                // Initialize the timer.
+                checkpoint(false);
+
+                // First stop the services.
+                for (String service : mServices) {
+                    SystemProperties.set("ctl.stop", service);
+                }
+
+                // Wait for the services to stop.
+                for (String service : mServices) {
+                    String key = "init.svc." + service;
+                    while (!"stopped".equals(SystemProperties.get(key))) {
+                        checkpoint(true);
+                    }
+                }
+
+                // Reset the properties.
+                SystemProperties.set("vpn.dns", NONE);
+                SystemProperties.set("vpn.via", NONE);
+                while (!NONE.equals(SystemProperties.get("vpn.dns")) ||
+                        !NONE.equals(SystemProperties.get("vpn.via"))) {
+                    checkpoint(true);
+                }
+
+                // Check if we need to restart some services.
+                boolean restart = false;
+                for (String[] arguments : mArguments) {
+                    restart = restart || (arguments != null);
+                }
+                if (!restart) {
+                    return;
+                }
+
+                // Start the service with arguments.
+                for (int i = 0; i < mServices.length; ++i) {
+                    String[] arguments = mArguments[i];
+                    if (arguments == null) {
+                        continue;
+                    }
+
+                    // Start the service.
+                    String service = mServices[i];
+                    SystemProperties.set("ctl.start", service);
+
+                    // Wait for the service to start.
+                    String key = "init.svc." + service;
+                    while (!"running".equals(SystemProperties.get(key))) {
+                        checkpoint(true);
+                    }
+
+                    // Create the control socket.
+                    LocalSocket socket = new LocalSocket();
+                    LocalSocketAddress address = new LocalSocketAddress(
+                            service, LocalSocketAddress.Namespace.RESERVED);
+
+                    // Wait for the socket to connect.
+                    while (true) {
+                        try {
+                            socket.connect(address);
+                            break;
+                        } catch (Exception e) {
+                            // ignore
+                        }
+                        checkpoint(true);
+                    }
+                    socket.setSoTimeout(500);
+
+                    // Send over the arguments.
+                    OutputStream output = socket.getOutputStream();
+                    for (String argument : arguments) {
+                        byte[] bytes = argument.getBytes(Charsets.UTF_8);
+                        if (bytes.length >= 0xFFFF) {
+                            throw new IllegalArgumentException("argument too large");
+                        }
+                        output.write(bytes.length >> 8);
+                        output.write(bytes.length);
+                        output.write(bytes);
+                        checkpoint(false);
+                    }
+
+                    // Send End-Of-Arguments.
+                    output.write(0xFF);
+                    output.write(0xFF);
+                    output.flush();
+                    socket.close();
+                }
+
+                // Now here is the beast from the old days. We check few
+                // properties to figure out the current status. Ideally we
+                // can read things back from the sockets and get rid of the
+                // properties, but we have no time...
+                while (NONE.equals(SystemProperties.get("vpn.dns")) ||
+                        NONE.equals(SystemProperties.get("vpn.via"))) {
+
+                    // Check if a running service is dead.
+                    for (int i = 0; i < mServices.length; ++i) {
+                        String service = mServices[i];
+                        if (mArguments[i] != null && !"running".equals(
+                                SystemProperties.get("init.svc." + service))) {
+                            throw new IllegalArgumentException(service + " is dead");
+                        }
+                    }
+                    checkpoint(true);
+                }
+
+                // Great! Now we are connected!
+                Log.i(TAG, "connected!");
+                // TODO:
+
+            } catch (Exception e) {
+                Log.i(TAG, e.getMessage());
+                for (String service : mServices) {
+                    SystemProperties.set("ctl.stop", service);
+                }
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index 67e73f5f..4fa3bda 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -132,7 +132,7 @@
     private static final int GPS_CAPABILITY_MSB = 0x0000002;
     private static final int GPS_CAPABILITY_MSA = 0x0000004;
     private static final int GPS_CAPABILITY_SINGLE_SHOT = 0x0000008;
-
+    private static final int GPS_CAPABILITY_ON_DEMAND_TIME = 0x0000010;
 
     // these need to match AGpsType enum in gps.h
     private static final int AGPS_TYPE_SUPL = 1;
@@ -200,6 +200,9 @@
     private boolean mInjectNtpTimePending = true;
     private boolean mDownloadXtraDataPending = true;
 
+    // set to true if the GPS engine does not do on-demand NTP time requests
+    private boolean mPeriodicTimeInjection;
+
     // true if GPS is navigating
     private boolean mNavigating;
 
@@ -549,10 +552,12 @@
             delay = RETRY_INTERVAL;
         }
 
-        // send delayed message for next NTP injection
-        // since this is delayed and not urgent we do not hold a wake lock here
-        mHandler.removeMessages(INJECT_NTP_TIME);
-        mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+        if (mPeriodicTimeInjection) {
+            // send delayed message for next NTP injection
+            // since this is delayed and not urgent we do not hold a wake lock here
+            mHandler.removeMessages(INJECT_NTP_TIME);
+            mHandler.sendMessageDelayed(Message.obtain(mHandler, INJECT_NTP_TIME), delay);
+        }
     }
 
     private void handleDownloadXtraData() {
@@ -1305,6 +1310,11 @@
      */
     private void setEngineCapabilities(int capabilities) {
         mEngineCapabilities = capabilities;
+
+        if (!hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME) && !mPeriodicTimeInjection) {
+            mPeriodicTimeInjection = true;
+            requestUtcTime();
+        }
     }
 
     /**
@@ -1438,6 +1448,14 @@
     }
 
     /**
+     * Called from native code to request utc time info
+     */
+
+    private void requestUtcTime() {
+        sendMessage(INJECT_NTP_TIME, 0, null);
+    }
+
+    /**
      * Called from native code to request reference location info
      */
 
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 584cd03..1f2ec2c 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -25,6 +25,7 @@
 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.*;
 import static android.net.ConnectivityManager.TYPE_MOBILE;
 import static android.net.NetworkPolicy.LIMIT_DISABLED;
 import static android.net.NetworkPolicy.WARNING_DISABLED;
@@ -57,6 +58,8 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
 import android.content.res.Resources;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
@@ -71,7 +74,9 @@
 import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
+import android.os.INetworkManagementService;
 import android.os.IPowerManager;
+import android.os.Message;
 import android.os.RemoteCallbackList;
 import android.os.RemoteException;
 import android.telephony.TelephonyManager;
@@ -108,6 +113,7 @@
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.HashSet;
+import java.util.List;
 
 import libcore.io.IoUtils;
 
@@ -148,10 +154,14 @@
 
     private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
 
+    private static final int MSG_RULES_CHANGED = 0x1;
+    private static final int MSG_METERED_IFACES_CHANGED = 0x2;
+
     private final Context mContext;
     private final IActivityManager mActivityManager;
     private final IPowerManager mPowerManager;
     private final INetworkStatsService mNetworkStats;
+    private final INetworkManagementService mNetworkManagement;
     private final TrustedTime mTime;
 
     private IConnectivityManager mConnManager;
@@ -160,6 +170,7 @@
     private final Object mRulesLock = new Object();
 
     private boolean mScreenOn;
+    private boolean mBackgroundData;
 
     /** Current policy for network templates. */
     private ArrayList<NetworkPolicy> mNetworkPolicy = Lists.newArrayList();
@@ -188,11 +199,14 @@
     // TODO: keep whitelist of system-critical services that should never have
     // rules enforced, such as system, phone, and radio UIDs.
 
+    // TODO: watch for package added broadcast to catch new UIDs.
+
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
-            IPowerManager powerManager, INetworkStatsService networkStats) {
+            IPowerManager powerManager, INetworkStatsService networkStats,
+            INetworkManagementService networkManagement) {
         // TODO: move to using cached NtpTrustedTime
-        this(context, activityManager, powerManager, networkStats, new NtpTrustedTime(),
-                getSystemDir());
+        this(context, activityManager, powerManager, networkStats, networkManagement,
+                new NtpTrustedTime(), getSystemDir());
     }
 
     private static File getSystemDir() {
@@ -200,17 +214,19 @@
     }
 
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
-            IPowerManager powerManager, INetworkStatsService networkStats, TrustedTime time,
-            File systemDir) {
+            IPowerManager powerManager, INetworkStatsService networkStats,
+            INetworkManagementService networkManagement,
+            TrustedTime time, File systemDir) {
         mContext = checkNotNull(context, "missing context");
         mActivityManager = checkNotNull(activityManager, "missing activityManager");
         mPowerManager = checkNotNull(powerManager, "missing powerManager");
         mNetworkStats = checkNotNull(networkStats, "missing networkStats");
+        mNetworkManagement = checkNotNull(networkManagement, "missing networkManagement");
         mTime = checkNotNull(time, "missing TrustedTime");
 
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
-        mHandler = new Handler(mHandlerThread.getLooper());
+        mHandler = new Handler(mHandlerThread.getLooper(), mHandlerCallback);
 
         mPolicyFile = new AtomicFile(new File(systemDir, "netpolicy.xml"));
     }
@@ -231,6 +247,7 @@
         }
 
         updateScreenOn();
+        updateBackgroundData(true);
 
         try {
             mActivityManager.registerProcessObserver(mProcessObserver);
@@ -256,11 +273,15 @@
         final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED);
         mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler);
 
-        // listen for warning polling events; currently dispatched by
+        // listen for stats update events
         final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED);
         mContext.registerReceiver(
                 mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler);
 
+        // listen for changes to background data flag
+        final IntentFilter bgFilter = new IntentFilter(ACTION_BACKGROUND_DATA_SETTING_CHANGED);
+        mContext.registerReceiver(mBgReceiver, bgFilter, CONNECTIVITY_INTERNAL, mHandler);
+
     }
 
     private IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
@@ -269,9 +290,6 @@
             // only someone like AMS should only be calling us
             mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
 
-            // skip when UID couldn't have any policy
-            if (!isUidValidForPolicy(mContext, uid)) return;
-
             synchronized (mRulesLock) {
                 // because a uid can have multiple pids running inside, we need to
                 // remember all pid states and summarize foreground at uid level.
@@ -292,9 +310,6 @@
             // only someone like AMS should only be calling us
             mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG);
 
-            // skip when UID couldn't have any policy
-            if (!isUidValidForPolicy(mContext, uid)) return;
-
             synchronized (mRulesLock) {
                 // clear records and recompute, when they exist
                 final SparseBooleanArray pidForeground = mUidPidForeground.get(uid);
@@ -349,6 +364,22 @@
     };
 
     /**
+     * Receiver that watches for
+     * {@link #ACTION_BACKGROUND_DATA_SETTING_CHANGED}.
+     */
+    private BroadcastReceiver mBgReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            // on background handler thread, and verified CONNECTIVITY_INTERNAL
+            // permission above.
+
+            synchronized (mRulesLock) {
+                updateBackgroundData(false);
+            }
+        }
+    };
+
+    /**
      * Check {@link NetworkPolicy} against current {@link INetworkStatsService}
      * to show visible notifications as needed.
      */
@@ -561,7 +592,7 @@
         final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
                 : System.currentTimeMillis();
 
-        mMeteredIfaces.clear();
+        final HashSet<String> newMeteredIfaces = Sets.newHashSet();
 
         // apply each policy that we found ifaces for; compute remaining data
         // based on current cycle and historical stats, and push to kernel.
@@ -591,28 +622,30 @@
             if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) {
                 // remaining "quota" is based on usage in current cycle
                 final long quota = Math.max(0, policy.limitBytes - total);
-                //kernelSetIfacesQuota(ifaces, quota);
+
+                if (ifaces.length > 1) {
+                    // TODO: switch to shared quota once NMS supports
+                    Slog.w(TAG, "shared quota unsupported; generating rule for each iface");
+                }
 
                 for (String iface : ifaces) {
-                    mMeteredIfaces.add(iface);
+                    removeInterfaceQuota(iface);
+                    setInterfaceQuota(iface, quota);
+                    newMeteredIfaces.add(iface);
                 }
             }
         }
 
-        // dispatch changed rule to existing listeners
-        // TODO: dispatch outside of holding lock
-        final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
-        final int length = mListeners.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-            if (listener != null) {
-                try {
-                    listener.onMeteredIfacesChanged(meteredIfaces);
-                } catch (RemoteException e) {
-                }
+        // remove quota on any trailing interfaces
+        for (String iface : mMeteredIfaces) {
+            if (!newMeteredIfaces.contains(iface)) {
+                removeInterfaceQuota(iface);
             }
         }
-        mListeners.finishBroadcast();
+        mMeteredIfaces = newMeteredIfaces;
+
+        final String[] meteredIfaces = mMeteredIfaces.toArray(new String[mMeteredIfaces.size()]);
+        mHandler.obtainMessage(MSG_METERED_IFACES_CHANGED, meteredIfaces).sendToTarget();
     }
 
     /**
@@ -804,32 +837,7 @@
 
         mListeners.register(listener);
 
-        synchronized (mRulesLock) {
-            // dispatch any existing rules to new listeners
-            // TODO: dispatch outside of holding lock
-            final int size = mUidRules.size();
-            for (int i = 0; i < size; i++) {
-                final int uid = mUidRules.keyAt(i);
-                final int uidRules = mUidRules.valueAt(i);
-                if (uidRules != RULE_ALLOW_ALL) {
-                    try {
-                        listener.onUidRulesChanged(uid, uidRules);
-                    } catch (RemoteException e) {
-                    }
-                }
-            }
-
-            // dispatch any metered ifaces to new listeners
-            // TODO: dispatch outside of holding lock
-            if (mMeteredIfaces.size() > 0) {
-                final String[] meteredIfaces = mMeteredIfaces.toArray(
-                        new String[mMeteredIfaces.size()]);
-                try {
-                    listener.onMeteredIfacesChanged(meteredIfaces);
-                } catch (RemoteException e) {
-                }
-            }
-        }
+        // TODO: consider dispatching existing rules to new listeners
     }
 
     @Override
@@ -963,6 +971,21 @@
         }
     }
 
+    private void updateBackgroundData(boolean systemReady) {
+        synchronized (mRulesLock) {
+            try {
+                mBackgroundData = mConnManager.getBackgroundDataSetting();
+            } catch (RemoteException e) {
+            }
+            if (systemReady && mBackgroundData) {
+                // typical behavior of background enabled during systemReady;
+                // no need to clear rules for all UIDs.
+            } else {
+                updateRulesForBackgroundDataLocked();
+            }
+        }
+    }
+
     /**
      * Update rules that might be changed by {@link #mScreenOn} value.
      */
@@ -977,9 +1000,34 @@
         }
     }
 
-    private void updateRulesForUidLocked(int uid) {
-        if (!isUidValidForPolicy(mContext, uid)) return;
+    /**
+     * Update rules that might be changed by {@link #mBackgroundData} value.
+     */
+    private void updateRulesForBackgroundDataLocked() {
+        // update rules for all installed applications
+        final PackageManager pm = mContext.getPackageManager();
+        final List<ApplicationInfo> apps = pm.getInstalledApplications(0);
+        for (ApplicationInfo app : apps) {
+            updateRulesForUidLocked(app.uid);
+        }
 
+        // and catch system UIDs
+        // TODO: keep in sync with android_filesystem_config.h
+        for (int uid = 1000; uid <= 1025; uid++) {
+            updateRulesForUidLocked(uid);
+        }
+        for (int uid = 2000; uid <= 2002; uid++) {
+            updateRulesForUidLocked(uid);
+        }
+        for (int uid = 3000; uid <= 3007; uid++) {
+            updateRulesForUidLocked(uid);
+        }
+        for (int uid = 9998; uid <= 9999; uid++) {
+            updateRulesForUidLocked(uid);
+        }
+    }
+
+    private void updateRulesForUidLocked(int uid) {
         final int uidPolicy = getUidPolicy(uid);
         final boolean uidForeground = isUidForeground(uid);
 
@@ -989,6 +1037,10 @@
             // uid in background, and policy says to block metered data
             uidRules = RULE_REJECT_METERED;
         }
+        if (!uidForeground && !mBackgroundData) {
+            // uid in background, and global background disabled
+            uidRules = RULE_REJECT_METERED;
+        }
 
         // TODO: only dispatch when rules actually change
 
@@ -996,21 +1048,82 @@
         mUidRules.put(uid, uidRules);
 
         final boolean rejectMetered = (uidRules & RULE_REJECT_METERED) != 0;
-        //kernelSetUidRejectPaid(uid, rejectPaid);
+        setUidNetworkRules(uid, rejectMetered);
 
         // dispatch changed rule to existing listeners
-        // TODO: dispatch outside of holding lock
-        final int length = mListeners.beginBroadcast();
-        for (int i = 0; i < length; i++) {
-            final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
-            if (listener != null) {
-                try {
-                    listener.onUidRulesChanged(uid, uidRules);
-                } catch (RemoteException e) {
+        mHandler.obtainMessage(MSG_RULES_CHANGED, uid, uidRules).sendToTarget();
+    }
+
+    private Handler.Callback mHandlerCallback = new Handler.Callback() {
+        /** {@inheritDoc} */
+        public boolean handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_RULES_CHANGED: {
+                    final int uid = msg.arg1;
+                    final int uidRules = msg.arg2;
+                    final int length = mListeners.beginBroadcast();
+                    for (int i = 0; i < length; i++) {
+                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+                        if (listener != null) {
+                            try {
+                                listener.onUidRulesChanged(uid, uidRules);
+                            } catch (RemoteException e) {
+                            }
+                        }
+                    }
+                    mListeners.finishBroadcast();
+                    return true;
+                }
+                case MSG_METERED_IFACES_CHANGED: {
+                    final String[] meteredIfaces = (String[]) msg.obj;
+                    final int length = mListeners.beginBroadcast();
+                    for (int i = 0; i < length; i++) {
+                        final INetworkPolicyListener listener = mListeners.getBroadcastItem(i);
+                        if (listener != null) {
+                            try {
+                                listener.onMeteredIfacesChanged(meteredIfaces);
+                            } catch (RemoteException e) {
+                            }
+                        }
+                    }
+                    mListeners.finishBroadcast();
+                    return true;
+                }
+                default: {
+                    return false;
                 }
             }
         }
-        mListeners.finishBroadcast();
+    };
+
+    private void setInterfaceQuota(String iface, long quota) {
+        try {
+            mNetworkManagement.setInterfaceQuota(iface, quota);
+        } catch (IllegalStateException e) {
+            Slog.e(TAG, "problem setting interface quota", e);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "problem setting interface quota", e);
+        }
+    }
+
+    private void removeInterfaceQuota(String iface) {
+        try {
+            mNetworkManagement.removeInterfaceQuota(iface);
+        } catch (IllegalStateException e) {
+            Slog.e(TAG, "problem removing interface quota", e);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "problem removing interface quota", e);
+        }
+    }
+
+    private void setUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces) {
+        try {
+            mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+        } catch (IllegalStateException e) {
+            Slog.e(TAG, "problem setting uid rules", e);
+        } catch (RemoteException e) {
+            Slog.e(TAG, "problem setting uid rules", e);
+        }
     }
 
     private String getActiveSubscriberId() {
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index 4a79d17..7610a11 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -124,8 +124,6 @@
     private PendingIntent mPollIntent;
 
     // TODO: listen for kernel push events through netd instead of polling
-    // TODO: watch for UID uninstall, and transfer stats into single bucket
-
     // TODO: trim empty history objects entirely
 
     private static final long KB_IN_BYTES = 1024;
@@ -506,8 +504,11 @@
         try {
             networkSnapshot = mNetworkManager.getNetworkStatsSummary();
             uidSnapshot = detailedPoll ? mNetworkManager.getNetworkStatsDetail() : null;
+        } catch (IllegalStateException e) {
+            Slog.w(TAG, "problem reading network stats: " + e);
+            return;
         } catch (RemoteException e) {
-            Slog.w(TAG, "problem reading network stats");
+            Slog.w(TAG, "problem reading network stats: " + e);
             return;
         }
 
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index d10aa97..11ccd60 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -307,7 +307,7 @@
     }
 
     public int getSizeInfo(String pkgName, String apkPath, String fwdLockApkPath,
-            PackageStats pStats) {
+            String asecPath, PackageStats pStats) {
         StringBuilder builder = new StringBuilder("getsize");
         builder.append(' ');
         builder.append(pkgName);
@@ -315,17 +315,20 @@
         builder.append(apkPath);
         builder.append(' ');
         builder.append(fwdLockApkPath != null ? fwdLockApkPath : "!");
+        builder.append(' ');
+        builder.append(asecPath != null ? asecPath : "!");
 
         String s = transaction(builder.toString());
         String res[] = s.split(" ");
 
-        if ((res == null) || (res.length != 4)) {
+        if ((res == null) || (res.length != 5)) {
             return -1;
         }
         try {
             pStats.codeSize = Long.parseLong(res[1]);
             pStats.dataSize = Long.parseLong(res[2]);
             pStats.cacheSize = Long.parseLong(res[3]);
+            pStats.externalCodeSize = Long.parseLong(res[4]);
             return Integer.parseInt(res[0]);
         } catch (NumberFormatException e) {
             return -1;
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 5a9dae9..22e2dde 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -75,6 +75,7 @@
 import android.os.Environment;
 import android.os.FileObserver;
 import android.os.FileUtils;
+import android.os.FileUtils.FileStatus;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -4887,8 +4888,7 @@
 
         private final IPackageStatsObserver mObserver;
 
-        public MeasureParams(PackageStats stats, boolean success,
-                IPackageStatsObserver observer) {
+        public MeasureParams(PackageStats stats, boolean success, IPackageStatsObserver observer) {
             mObserver = observer;
             mStats = stats;
             mSuccess = success;
@@ -5480,6 +5480,17 @@
         }
     }
 
+    /**
+     * Extract the MountService "container ID" from the full code path of an
+     * .apk.
+     */
+    static String cidFromCodePath(String fullCodePath) {
+        int eidx = fullCodePath.lastIndexOf("/");
+        String subStr1 = fullCodePath.substring(0, eidx);
+        int sidx = subStr1.lastIndexOf("/");
+        return subStr1.substring(sidx+1, eidx);
+    }
+
     class SdInstallArgs extends InstallArgs {
         static final String RES_FILE_NAME = "pkg.apk";
 
@@ -6831,6 +6842,7 @@
         }
         PackageParser.Package p;
         boolean dataOnly = false;
+        String asecPath = null;
         synchronized (mPackages) {
             p = mPackages.get(packageName);
             if(p == null) {
@@ -6842,6 +6854,12 @@
                 }
                 p = ps.pkg;
             }
+            if (p != null && isExternal(p)) {
+                String secureContainerId = cidFromCodePath(p.applicationInfo.sourceDir);
+                if (secureContainerId != null) {
+                    asecPath = PackageHelper.getSdFilesystem(secureContainerId);
+                }
+            }
         }
         String publicSrcDir = null;
         if(!dataOnly) {
@@ -6850,10 +6868,13 @@
                 Slog.w(TAG, "Package " + packageName + " has no applicationInfo.");
                 return false;
             }
-            publicSrcDir = isForwardLocked(p) ? applicationInfo.publicSourceDir : null;
+            if (isForwardLocked(p)) {
+                publicSrcDir = applicationInfo.publicSourceDir;
+            }
         }
         if (mInstaller != null) {
-            int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir, pStats);
+            int res = mInstaller.getSizeInfo(packageName, p.mPath, publicSrcDir,
+                    asecPath, pStats);
             if (res < 0) {
                 return false;
             } else {
diff --git a/services/jni/com_android_server_location_GpsLocationProvider.cpp b/services/jni/com_android_server_location_GpsLocationProvider.cpp
index 6d4ad9a..87ffcba 100755
--- a/services/jni/com_android_server_location_GpsLocationProvider.cpp
+++ b/services/jni/com_android_server_location_GpsLocationProvider.cpp
@@ -42,6 +42,7 @@
 static jmethodID method_reportNiNotification;
 static jmethodID method_requestRefLocation;
 static jmethodID method_requestSetID;
+static jmethodID method_requestUtcTime;
 
 static const GpsInterface* sGpsInterface = NULL;
 static const GpsXtraInterface* sGpsXtraInterface = NULL;
@@ -122,6 +123,13 @@
     release_wake_lock(WAKE_LOCK_NAME);
 }
 
+static void request_utc_time_callback()
+{
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    env->CallVoidMethod(mCallbacksObj, method_requestUtcTime);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
 static pthread_t create_thread_callback(const char* name, void (*start)(void *), void* arg)
 {
     return (pthread_t)AndroidRuntime::createJavaThread(name, start, arg);
@@ -137,6 +145,7 @@
     acquire_wakelock_callback,
     release_wakelock_callback,
     create_thread_callback,
+    request_utc_time_callback,
 };
 
 static void xtra_download_request_callback()
@@ -232,6 +241,7 @@
             "(IIIIILjava/lang/String;Ljava/lang/String;IILjava/lang/String;)V");
     method_requestRefLocation = env->GetMethodID(clazz,"requestRefLocation","(I)V");
     method_requestSetID = env->GetMethodID(clazz,"requestSetID","(I)V");
+    method_requestUtcTime = env->GetMethodID(clazz,"requestUtcTime","()V");
 
     err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module);
     if (err == 0) {
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 2bab6a8..35e29a6 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -101,9 +101,8 @@
 }
 
 void Layer::onFrameQueued() {
-    if (android_atomic_or(1, &mQueuedFrames) == 0) {
-        mFlinger->signalEvent();
-    }
+    android_atomic_inc(&mQueuedFrames);
+    mFlinger->signalEvent();
 }
 
 // called with SurfaceFlinger::mStateLock as soon as the layer is entered
@@ -406,20 +405,18 @@
 
 void Layer::lockPageFlip(bool& recomputeVisibleRegions)
 {
-    if (android_atomic_and(0, &mQueuedFrames)) {
+    if (mQueuedFrames > 0) {
+        // signal another event if we have more frames pending
+        if (android_atomic_dec(&mQueuedFrames) > 1) {
+            mFlinger->signalEvent();
+        }
+
         if (mSurfaceTexture->updateTexImage() < NO_ERROR) {
             // something happened!
             recomputeVisibleRegions = true;
             return;
         }
 
-        // signal another event if we have more frames waiting
-        if (mSurfaceTexture->getQueuedCount()) {
-            if (android_atomic_or(1, &mQueuedFrames) == 0) {
-                mFlinger->signalEvent();
-            }
-        }
-
         mActiveBuffer = mSurfaceTexture->getCurrentBuffer();
         mSurfaceTexture->getTransformMatrix(mTextureMatrix);
 
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index f0b19f2..1c57bc1 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -78,7 +78,6 @@
 SurfaceFlinger::SurfaceFlinger()
     :   BnSurfaceComposer(), Thread(false),
         mTransactionFlags(0),
-        mTransactionCount(0),
         mResizeTransationPending(false),
         mLayersRemoved(false),
         mBootTime(systemTime()),
@@ -385,13 +384,11 @@
         handleConsoleEvents();
     }
 
-    if (LIKELY(mTransactionCount == 0)) {
-        // if we're in a global transaction, don't do anything.
-        const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
-        uint32_t transactionFlags = peekTransactionFlags(mask);
-        if (LIKELY(transactionFlags)) {
-            handleTransaction(transactionFlags);
-        }
+    // if we're in a global transaction, don't do anything.
+    const uint32_t mask = eTransactionNeeded | eTraversalNeeded;
+    uint32_t transactionFlags = peekTransactionFlags(mask);
+    if (UNLIKELY(transactionFlags)) {
+        handleTransaction(transactionFlags);
     }
 
     // post surfaces (if needed)
@@ -1176,28 +1173,33 @@
     return old;
 }
 
-void SurfaceFlinger::openGlobalTransaction()
-{
-    android_atomic_inc(&mTransactionCount);
-}
 
-void SurfaceFlinger::closeGlobalTransaction()
-{
-    if (android_atomic_dec(&mTransactionCount) == 1) {
-        signalEvent();
+void SurfaceFlinger::setTransactionState(const Vector<ComposerState>& state) {
+    Mutex::Autolock _l(mStateLock);
 
-        // if there is a transaction with a resize, wait for it to
-        // take effect before returning.
-        Mutex::Autolock _l(mStateLock);
-        while (mResizeTransationPending) {
-            status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
-            if (CC_UNLIKELY(err != NO_ERROR)) {
-                // just in case something goes wrong in SF, return to the
-                // called after a few seconds.
-                LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!");
-                mResizeTransationPending = false;
-                break;
-            }
+    uint32_t flags = 0;
+    const size_t count = state.size();
+    for (size_t i=0 ; i<count ; i++) {
+        const ComposerState& s(state[i]);
+        sp<Client> client( static_cast<Client *>(s.client.get()) );
+        flags |= setClientStateLocked(client, s.state);
+    }
+    if (flags) {
+        setTransactionFlags(flags);
+    }
+
+    signalEvent();
+
+    // if there is a transaction with a resize, wait for it to
+    // take effect before returning.
+    while (mResizeTransationPending) {
+        status_t err = mTransactionCV.waitRelative(mStateLock, s2ns(5));
+        if (CC_UNLIKELY(err != NO_ERROR)) {
+            // just in case something goes wrong in SF, return to the
+            // called after a few seconds.
+            LOGW_IF(err == TIMED_OUT, "closeGlobalTransaction timed out!");
+            mResizeTransationPending = false;
+            break;
         }
     }
 }
@@ -1393,60 +1395,52 @@
     return err;
 }
 
-status_t SurfaceFlinger::setClientState(
+uint32_t SurfaceFlinger::setClientStateLocked(
         const sp<Client>& client,
-        int32_t count,
-        const layer_state_t* states)
+        const layer_state_t& s)
 {
-    Mutex::Autolock _l(mStateLock);
     uint32_t flags = 0;
-    for (int i=0 ; i<count ; i++) {
-        const layer_state_t& s(states[i]);
-        sp<LayerBaseClient> layer(client->getLayerUser(s.surface));
-        if (layer != 0) {
-            const uint32_t what = s.what;
-            if (what & ePositionChanged) {
-                if (layer->setPosition(s.x, s.y))
-                    flags |= eTraversalNeeded;
-            }
-            if (what & eLayerChanged) {
-                ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
-                if (layer->setLayer(s.z)) {
-                    mCurrentState.layersSortedByZ.removeAt(idx);
-                    mCurrentState.layersSortedByZ.add(layer);
-                    // we need traversal (state changed)
-                    // AND transaction (list changed)
-                    flags |= eTransactionNeeded|eTraversalNeeded;
-                }
-            }
-            if (what & eSizeChanged) {
-                if (layer->setSize(s.w, s.h)) {
-                    flags |= eTraversalNeeded;
-                    mResizeTransationPending = true;
-                }
-            }
-            if (what & eAlphaChanged) {
-                if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
-                    flags |= eTraversalNeeded;
-            }
-            if (what & eMatrixChanged) {
-                if (layer->setMatrix(s.matrix))
-                    flags |= eTraversalNeeded;
-            }
-            if (what & eTransparentRegionChanged) {
-                if (layer->setTransparentRegionHint(s.transparentRegion))
-                    flags |= eTraversalNeeded;
-            }
-            if (what & eVisibilityChanged) {
-                if (layer->setFlags(s.flags, s.mask))
-                    flags |= eTraversalNeeded;
+    sp<LayerBaseClient> layer(client->getLayerUser(s.surface));
+    if (layer != 0) {
+        const uint32_t what = s.what;
+        if (what & ePositionChanged) {
+            if (layer->setPosition(s.x, s.y))
+                flags |= eTraversalNeeded;
+        }
+        if (what & eLayerChanged) {
+            ssize_t idx = mCurrentState.layersSortedByZ.indexOf(layer);
+            if (layer->setLayer(s.z)) {
+                mCurrentState.layersSortedByZ.removeAt(idx);
+                mCurrentState.layersSortedByZ.add(layer);
+                // we need traversal (state changed)
+                // AND transaction (list changed)
+                flags |= eTransactionNeeded|eTraversalNeeded;
             }
         }
+        if (what & eSizeChanged) {
+            if (layer->setSize(s.w, s.h)) {
+                flags |= eTraversalNeeded;
+                mResizeTransationPending = true;
+            }
+        }
+        if (what & eAlphaChanged) {
+            if (layer->setAlpha(uint8_t(255.0f*s.alpha+0.5f)))
+                flags |= eTraversalNeeded;
+        }
+        if (what & eMatrixChanged) {
+            if (layer->setMatrix(s.matrix))
+                flags |= eTraversalNeeded;
+        }
+        if (what & eTransparentRegionChanged) {
+            if (layer->setTransparentRegionHint(s.transparentRegion))
+                flags |= eTraversalNeeded;
+        }
+        if (what & eVisibilityChanged) {
+            if (layer->setFlags(s.flags, s.mask))
+                flags |= eTraversalNeeded;
+        }
     }
-    if (flags) {
-        setTransactionFlags(flags);
-    }
-    return NO_ERROR;
+    return flags;
 }
 
 void SurfaceFlinger::screenReleased(int dpy)
@@ -1588,8 +1582,7 @@
 {
     switch (code) {
         case CREATE_CONNECTION:
-        case OPEN_GLOBAL_TRANSACTION:
-        case CLOSE_GLOBAL_TRANSACTION:
+        case SET_TRANSACTION_STATE:
         case SET_ORIENTATION:
         case FREEZE_DISPLAY:
         case UNFREEZE_DISPLAY:
@@ -2469,9 +2462,6 @@
 status_t Client::destroySurface(SurfaceID sid) {
     return mFlinger->removeSurface(this, sid);
 }
-status_t Client::setState(int32_t count, const layer_state_t* states) {
-    return mFlinger->setClientState(this, count, states);
-}
 
 // ---------------------------------------------------------------------------
 
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 45f80ae..b49fa36 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -70,14 +70,12 @@
     sp<LayerBaseClient> getLayerUser(int32_t i) const;
 
 private:
-
     // ISurfaceComposerClient interface
     virtual sp<ISurface> createSurface(
             surface_data_t* params, const String8& name,
             DisplayID display, uint32_t w, uint32_t h,PixelFormat format,
             uint32_t flags);
     virtual status_t destroySurface(SurfaceID surfaceId);
-    virtual status_t setState(int32_t count, const layer_state_t* states);
     virtual status_t onTransact(
         uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
 
@@ -168,8 +166,7 @@
     virtual sp<IGraphicBufferAlloc>     createGraphicBufferAlloc();
     virtual sp<IMemoryHeap>             getCblk() const;
     virtual void                        bootFinished();
-    virtual void                        openGlobalTransaction();
-    virtual void                        closeGlobalTransaction();
+    virtual void                        setTransactionState(const Vector<ComposerState>& state);
     virtual status_t                    freezeDisplay(DisplayID dpy, uint32_t flags);
     virtual status_t                    unfreezeDisplay(DisplayID dpy, uint32_t flags);
     virtual int                         setOrientation(DisplayID dpy, int orientation, uint32_t flags);
@@ -220,8 +217,7 @@
 
     status_t removeSurface(const sp<Client>& client, SurfaceID sid);
     status_t destroySurface(const wp<LayerBaseClient>& layer);
-    status_t setClientState(const sp<Client>& client,
-            int32_t count, const layer_state_t* states);
+    uint32_t setClientStateLocked(const sp<Client>& client, const layer_state_t& s);
 
     class LayerVector : public SortedVector< sp<LayerBase> > {
     public:
@@ -337,7 +333,6 @@
     mutable     Mutex                   mStateLock;
                 State                   mCurrentState;
     volatile    int32_t                 mTransactionFlags;
-    volatile    int32_t                 mTransactionCount;
                 Condition               mTransactionCV;
                 SortedVector< sp<LayerBase> > mLayerPurgatory;
                 bool                    mResizeTransationPending;
diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp
index 18c54b3..56b2a8f 100644
--- a/services/surfaceflinger/tests/resize/resize.cpp
+++ b/services/surfaceflinger/tests/resize/resize.cpp
@@ -43,9 +43,9 @@
             PIXEL_FORMAT_RGB_565);
 
 
-    client->openTransaction();
+    SurfaceComposerClient::openGlobalTransaction();
     surface->setLayer(100000);
-    client->closeTransaction();
+    SurfaceComposerClient::closeGlobalTransaction();
 
     Surface::SurfaceInfo info;
     surface->lock(&info);
@@ -57,9 +57,9 @@
     android_memset16((uint16_t*)info.bits, 0x07E0, bpr*info.h);
     surface->unlockAndPost();
 
-    client->openTransaction();
+    SurfaceComposerClient::openGlobalTransaction();
     surface->setSize(320, 240);
-    client->closeTransaction();
+    SurfaceComposerClient::closeGlobalTransaction();
 
     
     IPCThreadState::self()->joinThreadPool();
diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp
index 5265f91..8e1c3fe 100644
--- a/services/surfaceflinger/tests/surface/surface.cpp
+++ b/services/surfaceflinger/tests/surface/surface.cpp
@@ -39,9 +39,9 @@
     
     sp<SurfaceControl> surfaceControl = client->createSurface(
             getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565);
-    client->openTransaction();
+    SurfaceComposerClient::openGlobalTransaction();
     surfaceControl->setLayer(100000);
-    client->closeTransaction();
+    SurfaceComposerClient::closeGlobalTransaction();
 
     // pretend it went cross-process
     Parcel parcel;
diff --git a/services/tests/servicestests/res/raw/xt_qtaguid_extended b/services/tests/servicestests/res/raw/xt_qtaguid_extended
new file mode 100644
index 0000000..5bef3dd
--- /dev/null
+++ b/services/tests/servicestests/res/raw/xt_qtaguid_extended
@@ -0,0 +1,3 @@
+acct_tag_hex uid_tag_int iface rx_bytes rx_packets tx_bytes tx_packets teleported_goats
+0x0 1000 test0 1024 10 2048 20 2716057
+0x0000F00D00000000 1000 test0 512 5 512 5 3370318
diff --git a/services/tests/servicestests/res/raw/xt_qtaguid_typical b/services/tests/servicestests/res/raw/xt_qtaguid_typical
new file mode 100644
index 0000000..7c4f04e
--- /dev/null
+++ b/services/tests/servicestests/res/raw/xt_qtaguid_typical
@@ -0,0 +1,32 @@
+idx iface acct_tag_hex uid_tag_int rx_bytes tx_bytes
+1 wlan0 0x0 0 14615 4270
+2 wlan0 0x0 1000 5175 915
+3 wlan0 0x0 1021 3381 903
+4 wlan0 0x0 10004 333821 53558
+5 wlan0 0x0 10010 4888 37363
+6 wlan0 0x0 10013 52 104
+7 wlan0 0x74182ada00000000 10004 18725 1066
+8 rmnet0 0x0 0 301274 30244
+9 rmnet0 0x0 1000 304 441
+10 rmnet0 0x0 1013 2880 2272
+11 rmnet0 0x0 1021 31407 8430
+12 rmnet0 0x0 10003 32665 3814
+13 rmnet0 0x0 10004 2373141 420112
+14 rmnet0 0x0 10010 870370 1111727
+15 rmnet0 0x0 10013 240 240
+16 rmnet0 0x0 10016 16703 13512
+17 rmnet0 0x0 10017 3990 3269
+18 rmnet0 0x0 10018 474504 14516062
+19 rmnet0 0x0 10019 782804 71077
+20 rmnet0 0x0 10022 70671 49684
+21 rmnet0 0x0 10029 5785354 397159
+22 rmnet0 0x0 10033 2102 1686
+23 rmnet0 0x0 10034 15495464 227694
+24 rmnet0 0x0 10037 31184994 684122
+25 rmnet0 0x0 10051 298687 113485
+26 rmnet0 0x0 10056 29504 20669
+27 rmnet0 0x0 10069 683 596
+28 rmnet0 0x0 10072 34051 12453
+29 rmnet0 0x0 10077 7025393 213866
+30 rmnet0 0x0 10081 354 1178
+31 rmnet0 0x74182ada00000000 10037 28507378 437004
diff --git a/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
new file mode 100644
index 0000000..ac7cb5a
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/NetworkManagementServiceTest.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server;
+
+import static com.android.server.NetworkManagementSocketTagger.kernelToTag;
+import static com.android.server.NetworkManagementSocketTagger.tagToKernel;
+
+import android.content.res.Resources;
+import android.net.NetworkStats;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.frameworks.servicestests.R;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+/**
+ * Tests for {@link NetworkManagementService}.
+ */
+@LargeTest
+public class NetworkManagementServiceTest extends AndroidTestCase {
+    private File mTestProc;
+    private NetworkManagementService mService;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mTestProc = getContext().getFilesDir();
+        mService = NetworkManagementService.createForTest(mContext, mTestProc);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        mService = null;
+
+        super.tearDown();
+    }
+
+    public void testNetworkStatsDetail() throws Exception {
+        stageFile(R.raw.xt_qtaguid_typical, new File(mTestProc, "net/xt_qtaguid/stats"));
+
+        final NetworkStats stats = mService.getNetworkStatsDetail();
+        assertEquals(31, stats.size);
+        assertStatsEntry(stats, "wlan0", 0, 0, 14615L, 4270L);
+        assertStatsEntry(stats, "wlan0", 10004, 0, 333821L, 53558L);
+        assertStatsEntry(stats, "wlan0", 10004, 1947740890, 18725L, 1066L);
+        assertStatsEntry(stats, "rmnet0", 10037, 0, 31184994L, 684122L);
+        assertStatsEntry(stats, "rmnet0", 10037, 1947740890, 28507378L, 437004L);
+    }
+
+    public void testNetworkStatsDetailExtended() throws Exception {
+        stageFile(R.raw.xt_qtaguid_extended, new File(mTestProc, "net/xt_qtaguid/stats"));
+
+        final NetworkStats stats = mService.getNetworkStatsDetail();
+        assertEquals(2, stats.size);
+        assertStatsEntry(stats, "test0", 1000, 0, 1024L, 2048L);
+        assertStatsEntry(stats, "test0", 1000, 0xF00D, 512L, 512L);
+    }
+
+    public void testKernelTags() throws Exception {
+        assertEquals("0", tagToKernel(0x0));
+        assertEquals("214748364800", tagToKernel(0x32));
+        assertEquals("9223372032559808512", tagToKernel(Integer.MAX_VALUE));
+        assertEquals("0", tagToKernel(Integer.MIN_VALUE));
+        assertEquals("9223369837831520256", tagToKernel(Integer.MIN_VALUE - 512));
+
+        assertEquals(0, kernelToTag("0x0000000000000000"));
+        assertEquals(0x32, kernelToTag("0x0000003200000000"));
+        assertEquals(2147483647, kernelToTag("0x7fffffff00000000"));
+        assertEquals(0, kernelToTag("0x0000000000000000"));
+        assertEquals(2147483136, kernelToTag("0x7FFFFE0000000000"));
+
+    }
+
+    /**
+     * Copy a {@link Resources#openRawResource(int)} into {@link File} for
+     * testing purposes.
+     */
+    private void stageFile(int rawId, File file) throws Exception {
+        new File(file.getParent()).mkdirs();
+        InputStream in = null;
+        OutputStream out = null;
+        try {
+            in = getContext().getResources().openRawResource(rawId);
+            out = new FileOutputStream(file);
+            Streams.copy(in, out);
+        } finally {
+            IoUtils.closeQuietly(in);
+            IoUtils.closeQuietly(out);
+        }
+    }
+
+    private static void assertStatsEntry(
+            NetworkStats stats, String iface, int uid, int tag, long rx, long tx) {
+        final int i = stats.findIndex(iface, uid, tag);
+        assertEquals(rx, stats.rx[i]);
+        assertEquals(tx, stats.tx[i]);
+    }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
index 07e5425..b4ac987 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java
@@ -41,7 +41,9 @@
 import android.app.INotificationManager;
 import android.app.IProcessObserver;
 import android.content.Intent;
+import android.content.pm.PackageInfo;
 import android.content.pm.PackageManager;
+import android.content.pm.Signature;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.INetworkPolicyListener;
@@ -54,6 +56,7 @@
 import android.net.NetworkStats;
 import android.net.NetworkTemplate;
 import android.os.Binder;
+import android.os.INetworkManagementService;
 import android.os.IPowerManager;
 import android.test.AndroidTestCase;
 import android.test.mock.MockPackageManager;
@@ -63,12 +66,17 @@
 import android.util.TrustedTime;
 
 import com.android.server.net.NetworkPolicyManagerService;
+import com.google.common.util.concurrent.AbstractFuture;
 
 import org.easymock.Capture;
 import org.easymock.EasyMock;
+import org.easymock.IAnswer;
 
 import java.io.File;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Tests for {@link NetworkPolicyManagerService}.
@@ -88,6 +96,7 @@
     private IActivityManager mActivityManager;
     private IPowerManager mPowerManager;
     private INetworkStatsService mStatsService;
+    private INetworkManagementService mNetworkManagement;
     private INetworkPolicyListener mPolicyListener;
     private TrustedTime mTime;
     private IConnectivityManager mConnManager;
@@ -118,22 +127,40 @@
                     public String[] getPackagesForUid(int uid) {
                         return new String[] { "com.example" };
                     }
+
+                    @Override
+                    public PackageInfo getPackageInfo(String packageName, int flags) {
+                        final PackageInfo info = new PackageInfo();
+                        final Signature signature;
+                        if ("android".equals(packageName)) {
+                            signature = new Signature("F00D");
+                        } else {
+                            signature = new Signature("DEAD");
+                        }
+                        info.signatures = new Signature[] { signature };
+                        return info;
+                    }
                 };
             }
         };
 
         mPolicyDir = getContext().getFilesDir();
+        for (File file : mPolicyDir.listFiles()) {
+            file.delete();
+        }
 
         mActivityManager = createMock(IActivityManager.class);
         mPowerManager = createMock(IPowerManager.class);
         mStatsService = createMock(INetworkStatsService.class);
+        mNetworkManagement = createMock(INetworkManagementService.class);
         mPolicyListener = createMock(INetworkPolicyListener.class);
         mTime = createMock(TrustedTime.class);
         mConnManager = createMock(IConnectivityManager.class);
         mNotifManager = createMock(INotificationManager.class);
 
         mService = new NetworkPolicyManagerService(
-                mServiceContext, mActivityManager, mPowerManager, mStatsService, mTime, mPolicyDir);
+                mServiceContext, mActivityManager, mPowerManager, mStatsService,
+                mNetworkManagement, mTime, mPolicyDir);
         mService.bindConnectivityManager(mConnManager);
         mService.bindNotificationManager(mNotifManager);
 
@@ -152,6 +179,9 @@
         expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
         expectTime(System.currentTimeMillis());
 
+        // default behavior is background data enabled
+        expect(mConnManager.getBackgroundDataSetting()).andReturn(true);
+
         replay();
         mService.systemReady();
         verifyAndReset();
@@ -228,81 +258,123 @@
     }
 
     public void testScreenChangesRules() throws Exception {
-        // push strict policy for foreground uid, verify ALLOW rule
-        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        Future<Void> future;
+
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        future.get();
+        verifyAndReset();
+
+        // push strict policy for foreground uid, verify ALLOW rule
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
         mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
+        future.get();
         verifyAndReset();
 
         // now turn screen off and verify REJECT rule
         expect(mPowerManager.isScreenOn()).andReturn(false).atLeastOnce();
-        expectRulesChanged(UID_A, RULE_REJECT_METERED);
+        expectSetUidNetworkRules(UID_A, true);
+        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
         mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_OFF));
+        future.get();
         verifyAndReset();
 
         // and turn screen back on, verify ALLOW rule restored
         expect(mPowerManager.isScreenOn()).andReturn(true).atLeastOnce();
-        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
         mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SCREEN_ON));
+        future.get();
         verifyAndReset();
     }
 
     public void testPolicyNone() throws Exception {
+        Future<Void> future;
+
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        replay();
+        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        future.get();
+        verifyAndReset();
+
         // POLICY_NONE should RULE_ALLOW in foreground
-        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
         mService.setUidPolicy(UID_A, POLICY_NONE);
-        mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        future.get();
         verifyAndReset();
 
         // POLICY_NONE should RULE_ALLOW in background
-        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        future.get();
         verifyAndReset();
     }
 
     public void testPolicyReject() throws Exception {
+        Future<Void> future;
+
         // POLICY_REJECT should RULE_ALLOW in background
-        expectRulesChanged(UID_A, RULE_REJECT_METERED);
+        expectSetUidNetworkRules(UID_A, true);
+        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
         mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
+        future.get();
         verifyAndReset();
 
         // POLICY_REJECT should RULE_ALLOW in foreground
-        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, true);
+        future.get();
         verifyAndReset();
 
         // POLICY_REJECT should RULE_REJECT in background
-        expectRulesChanged(UID_A, RULE_REJECT_METERED);
+        expectSetUidNetworkRules(UID_A, true);
+        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        future.get();
         verifyAndReset();
     }
 
     public void testPolicyRejectAddRemove() throws Exception {
+        Future<Void> future;
+
         // POLICY_NONE should have RULE_ALLOW in background
-        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
-        mService.setUidPolicy(UID_A, POLICY_NONE);
         mProcessObserver.onForegroundActivitiesChanged(PID_1, UID_A, false);
+        mService.setUidPolicy(UID_A, POLICY_NONE);
+        future.get();
         verifyAndReset();
 
         // adding POLICY_REJECT should cause RULE_REJECT
-        expectRulesChanged(UID_A, RULE_REJECT_METERED);
+        expectSetUidNetworkRules(UID_A, true);
+        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
         mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
+        future.get();
         verifyAndReset();
 
         // removing POLICY_REJECT should return us to RULE_ALLOW
-        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
         mService.setUidPolicy(UID_A, POLICY_NONE);
+        future.get();
         verifyAndReset();
     }
 
@@ -350,6 +422,7 @@
         long elapsedRealtime = 0;
         NetworkState[] state = null;
         NetworkStats stats = null;
+        Future<Void> future;
 
         final long TIME_FEB_15 = 1171497600000L;
         final long TIME_MAR_10 = 1173484800000L;
@@ -360,10 +433,11 @@
         state = new NetworkState[] { buildWifi() };
         expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
         expectTime(TIME_MAR_10 + elapsedRealtime);
-        expectMeteredIfacesChanged();
+        future = expectMeteredIfacesChanged();
 
         replay();
         mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        future.get();
         verifyAndReset();
 
         // now change cycle to be on 15th, and test in early march, to verify we
@@ -377,30 +451,38 @@
         expect(mStatsService.getSummaryForNetwork(sTemplateWifi, TIME_FEB_15, TIME_MAR_10))
                 .andReturn(stats).atLeastOnce();
 
-        // expect that quota remaining should be 1536 bytes
-        // TODO: write up NetworkManagementService mock
+        // TODO: consider making strongly ordered mock
+        expectRemoveInterfaceQuota(TEST_IFACE);
+        expectSetInterfaceQuota(TEST_IFACE, 1536L);
 
         expectClearNotifications();
-        expectMeteredIfacesChanged(TEST_IFACE);
+        future = expectMeteredIfacesChanged(TEST_IFACE);
 
         replay();
         setNetworkPolicies(new NetworkPolicy(sTemplateWifi, CYCLE_DAY, 1024L, 2048L));
+        future.get();
         verifyAndReset();
     }
 
     public void testUidRemovedPolicyCleared() throws Exception {
+        Future<Void> future;
+
         // POLICY_REJECT should RULE_REJECT in background
-        expectRulesChanged(UID_A, RULE_REJECT_METERED);
+        expectSetUidNetworkRules(UID_A, true);
+        future = expectRulesChanged(UID_A, RULE_REJECT_METERED);
         replay();
         mService.setUidPolicy(UID_A, POLICY_REJECT_METERED_BACKGROUND);
+        future.get();
         verifyAndReset();
 
         // uninstall should clear RULE_REJECT
-        expectRulesChanged(UID_A, RULE_ALLOW_ALL);
+        expectSetUidNetworkRules(UID_A, false);
+        future = expectRulesChanged(UID_A, RULE_ALLOW_ALL);
         replay();
         final Intent intent = new Intent(ACTION_UID_REMOVED);
         intent.putExtra(EXTRA_UID, UID_A);
         mServiceContext.sendBroadcast(intent);
+        future.get();
         verifyAndReset();
     }
 
@@ -435,25 +517,62 @@
         expectLastCall().anyTimes();
     }
 
-    private void expectRulesChanged(int uid, int policy) throws Exception {
-        mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));
+    private void expectSetInterfaceQuota(String iface, long quota) throws Exception {
+        mNetworkManagement.setInterfaceQuota(iface, quota);
         expectLastCall().atLeastOnce();
     }
 
-    private void expectMeteredIfacesChanged(String... ifaces) throws Exception {
-        mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces));
+    private void expectRemoveInterfaceQuota(String iface) throws Exception {
+        mNetworkManagement.removeInterfaceQuota(iface);
         expectLastCall().atLeastOnce();
     }
 
+    private void expectSetUidNetworkRules(int uid, boolean rejectOnQuotaInterfaces)
+            throws Exception {
+        mNetworkManagement.setUidNetworkRules(uid, rejectOnQuotaInterfaces);
+        expectLastCall().atLeastOnce();
+    }
+
+    private Future<Void> expectRulesChanged(int uid, int policy) throws Exception {
+        final FutureAnswer future = new FutureAnswer();
+        mPolicyListener.onUidRulesChanged(eq(uid), eq(policy));
+        expectLastCall().andAnswer(future);
+        return future;
+    }
+
+    private Future<Void> expectMeteredIfacesChanged(String... ifaces) throws Exception {
+        final FutureAnswer future = new FutureAnswer();
+        mPolicyListener.onMeteredIfacesChanged(aryEq(ifaces));
+        expectLastCall().andAnswer(future);
+        return future;
+    }
+
+    private static class FutureAnswer extends AbstractFuture<Void> implements IAnswer<Void> {
+        @Override
+        public Void get() throws InterruptedException, ExecutionException {
+            try {
+                return get(5, TimeUnit.SECONDS);
+            } catch (TimeoutException e) {
+                throw new RuntimeException(e);
+            }
+        }
+
+        @Override
+        public Void answer() {
+            set(null);
+            return null;
+        }
+    }
+
     private void replay() {
-        EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime,
-                mConnManager, mNotifManager);
+        EasyMock.replay(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
+                mNetworkManagement, mTime, mConnManager, mNotifManager);
     }
 
     private void verifyAndReset() {
-        EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime,
-                mConnManager, mNotifManager);
-        EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener, mTime,
-                mConnManager, mNotifManager);
+        EasyMock.verify(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
+                mNetworkManagement, mTime, mConnManager, mNotifManager);
+        EasyMock.reset(mActivityManager, mPowerManager, mStatsService, mPolicyListener,
+                mNetworkManagement, mTime, mConnManager, mNotifManager);
     }
 }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
index 636d059..903f2b0 100644
--- a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -59,7 +59,6 @@
 import android.telephony.TelephonyManager;
 import android.test.AndroidTestCase;
 import android.test.suitebuilder.annotation.LargeTest;
-import android.util.Log;
 import android.util.TrustedTime;
 
 import com.android.server.net.NetworkStatsService;
@@ -611,6 +610,9 @@
         mAlarmManager.setInexactRepeating(
                 eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class));
         expectLastCall().atLeastOnce();
+
+        mNetManager.setBandwidthControlEnabled(true);
+        expectLastCall().atLeastOnce();
     }
 
     private void expectNetworkState(NetworkState... state) throws Exception {
@@ -631,6 +633,7 @@
 
     private void expectSettings(long persistThreshold, long bucketDuration, long maxHistory)
             throws Exception {
+        expect(mSettings.getEnabled()).andReturn(true).anyTimes();
         expect(mSettings.getPollInterval()).andReturn(HOUR_IN_MILLIS).anyTimes();
         expect(mSettings.getPersistThreshold()).andReturn(persistThreshold).anyTimes();
         expect(mSettings.getNetworkBucketDuration()).andReturn(bucketDuration).anyTimes();
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 184d665..66120a1 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -191,10 +191,6 @@
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
     public String getDeviceId() {
-        if (!isVoiceCapable()) {
-            return null;
-        }
-
         try {
             return getSubscriberInfo().getDeviceId();
         } catch (RemoteException ex) {
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
index c4a6f53..6e9d0f9d 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/PhoneNumberUtilsTest.java
@@ -502,16 +502,17 @@
 
     @SmallTest
     public void testFormatNumberToE164() {
-        assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "us"));
-        assertNull(PhoneNumberUtils.formatNumberToE164("1234567", "us"));
-        assertEquals("+18004664114", PhoneNumberUtils.formatNumberToE164("800-GOOG-114", "us"));
+        // Note: ISO 3166-1 only allows upper case country codes.
+        assertEquals("+16502910000", PhoneNumberUtils.formatNumberToE164("650 2910000", "US"));
+        assertNull(PhoneNumberUtils.formatNumberToE164("1234567", "US"));
+        assertEquals("+18004664114", PhoneNumberUtils.formatNumberToE164("800-GOOG-114", "US"));
     }
 
     @SmallTest
     public void testFormatNumber() {
-        assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "us"));
-        assertEquals("123-4567", PhoneNumberUtils.formatNumber("1234567", "us"));
-        assertEquals("(800) 466-4114", PhoneNumberUtils.formatNumber("800-GOOG-114", "us"));
+        assertEquals("(650) 291-0000", PhoneNumberUtils.formatNumber("650 2910000", "US"));
+        assertEquals("123-4567", PhoneNumberUtils.formatNumber("1234567", "US"));
+        assertEquals("(800) 466-4114", PhoneNumberUtils.formatNumber("800-GOOG-114", "US"));
 
     }
 
diff --git a/tests/BiDiTests/res/layout/textview_direction_ltr.xml b/tests/BiDiTests/res/layout/textview_direction_ltr.xml
new file mode 100644
index 0000000..f7b7b8e
--- /dev/null
+++ b/tests/BiDiTests/res/layout/textview_direction_ltr.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/textview_direction_ltr"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layoutDirection="ltr">
+
+    <LinearLayout android:orientation="vertical"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:textDirection="ltr">
+
+        <LinearLayout android:orientation="vertical"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content">
+
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="inherit"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="firstStrong"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="anyRtl"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="ltr"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="rtl"
+                    />
+        </LinearLayout>
+
+        <LinearLayout android:orientation="vertical"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content">
+
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="inherit"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="firstStrong"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="anyRtl"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="ltr"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="rtl"
+                    />
+        </LinearLayout>
+
+    </LinearLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/res/layout/textview_direction_rtl.xml b/tests/BiDiTests/res/layout/textview_direction_rtl.xml
new file mode 100644
index 0000000..81c5411
--- /dev/null
+++ b/tests/BiDiTests/res/layout/textview_direction_rtl.xml
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2011 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:id="@+id/textview_direction_rtl"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        android:layoutDirection="rtl">
+
+    <LinearLayout android:orientation="vertical"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:textDirection="rtl">
+
+        <LinearLayout android:orientation="vertical"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content">
+
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="inherit"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="firstStrong"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="anyRtl"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="ltr"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_text"
+                      android:textDirection="rtl"
+                    />
+        </LinearLayout>
+
+        <LinearLayout android:orientation="vertical"
+                      android:layout_width="wrap_content"
+                      android:layout_height="wrap_content">
+
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="inherit"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="firstStrong"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="anyRtl"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="ltr"
+                    />
+            <TextView android:layout_height="wrap_content"
+                      android:layout_width="wrap_content"
+                      android:textSize="24dip"
+                      android:text="@string/textview_hebrew_text"
+                      android:textDirection="rtl"
+                    />
+        </LinearLayout>
+
+    </LinearLayout>
+
+</FrameLayout>
\ No newline at end of file
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
index b1e494a..c033879 100644
--- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java
@@ -122,9 +122,6 @@
         addItem(result, "Table RTL", BiDiTestTableLayoutRtl.class, R.id.table_layout_rtl);
         addItem(result, "Table LOC", BiDiTestTableLayoutLocale.class, R.id.table_layout_locale);
 
-        addItem(result, "ViewPadding", BiDiTestViewPadding.class, R.id.view_padding);
-        addItem(result, "ViewPadding MIXED", BiDiTestViewPaddingMixed.class, R.id.view_padding_mixed);
-
         addItem(result, "Padding", BiDiTestViewPadding.class, R.id.view_padding);
         addItem(result, "Padding MIXED", BiDiTestViewPaddingMixed.class, R.id.view_padding_mixed);
 
@@ -134,6 +131,9 @@
         addItem(result, "TextView RTL", BiDiTestTextViewRtl.class, R.id.textview_rtl);
         addItem(result, "TextView LOC", BiDiTestTextViewLocale.class, R.id.textview_locale);
 
+        addItem(result, "TextDirection LTR", BiDiTestTextViewDirectionLtr.class, R.id.textview_direction_ltr);
+        addItem(result, "TextDirection RTL", BiDiTestTextViewDirectionRtl.class, R.id.textview_direction_rtl);
+
         return result;
     }
 
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionLtr.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionLtr.java
new file mode 100644
index 0000000..882ed1f
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionLtr.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestTextViewDirectionLtr extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.textview_direction_ltr, container, false);
+    }
+}
diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionRtl.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionRtl.java
new file mode 100644
index 0000000..e63ee35
--- /dev/null
+++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestTextViewDirectionRtl.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.bidi;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class BiDiTestTextViewDirectionRtl extends Fragment {
+
+    @Override
+    public View onCreateView(LayoutInflater inflater, ViewGroup container,
+            Bundle savedInstanceState) {
+        return inflater.inflate(R.layout.textview_direction_rtl, container, false);
+    }
+}
diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml
index 7aa0617..8734143 100644
--- a/tests/HwAccelerationTest/AndroidManifest.xml
+++ b/tests/HwAccelerationTest/AndroidManifest.xml
@@ -31,6 +31,15 @@
         android:hardwareAccelerated="true">
 
         <activity
+                android:name="TimeDialogActivity"
+                android:label="_TimeDialog">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        
+        <activity
                 android:name="OpaqueActivity"
                 android:label="_Opaque">
             <intent-filter>
@@ -494,8 +503,7 @@
 
         <activity
                 android:name="Animated3dActivity"
-                android:label="_Animated 3d"
-                android:theme="@android:style/Theme.Translucent.NoTitleBar">
+                android:label="_Animated 3d">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java
new file mode 100644
index 0000000..9e3e950
--- /dev/null
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TimeDialogActivity.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.test.hwui;
+
+import android.app.Activity;
+import android.app.TimePickerDialog;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+@SuppressWarnings({"UnusedDeclaration"})
+public class TimeDialogActivity extends Activity {
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        
+        FrameLayout layout = new FrameLayout(this);
+        Button b = new Button(this);
+        b.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT,
+                FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER));
+        b.setText("Show dialog");
+        b.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                new TimePickerDialog(TimeDialogActivity.this, null, 12, 12, true).show();
+            }
+        });
+        layout.addView(b);
+        
+        setContentView(layout);
+    }
+}
diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java
index 47950e3..c031bc1 100644
--- a/voip/java/com/android/server/sip/SipHelper.java
+++ b/voip/java/com/android/server/sip/SipHelper.java
@@ -150,9 +150,17 @@
 
     private ContactHeader createContactHeader(SipProfile profile)
             throws ParseException, SipException {
-        ListeningPoint lp = getListeningPoint();
-        SipURI contactURI =
-                createSipUri(profile.getUserName(), profile.getProtocol(), lp);
+        return createContactHeader(profile, null, 0);
+    }
+
+    private ContactHeader createContactHeader(SipProfile profile,
+            String ip, int port) throws ParseException,
+            SipException {
+        SipURI contactURI = (ip == null)
+                ? createSipUri(profile.getUserName(), profile.getProtocol(),
+                        getListeningPoint())
+                : createSipUri(profile.getUserName(), profile.getProtocol(),
+                        ip, port);
 
         Address contactAddress = mAddressFactory.createAddress(contactURI);
         contactAddress.setDisplayName(profile.getDisplayName());
@@ -168,9 +176,14 @@
 
     private SipURI createSipUri(String username, String transport,
             ListeningPoint lp) throws ParseException {
-        SipURI uri = mAddressFactory.createSipURI(username, lp.getIPAddress());
+        return createSipUri(username, transport, lp.getIPAddress(), lp.getPort());
+    }
+
+    private SipURI createSipUri(String username, String transport,
+            String ip, int port) throws ParseException {
+        SipURI uri = mAddressFactory.createSipURI(username, ip);
         try {
-            uri.setPort(lp.getPort());
+            uri.setPort(port);
             uri.setTransportParam(transport);
         } catch (InvalidArgumentException e) {
             throw new RuntimeException(e);
@@ -353,13 +366,14 @@
      */
     public ServerTransaction sendInviteOk(RequestEvent event,
             SipProfile localProfile, String sessionDescription,
-            ServerTransaction inviteTransaction)
-            throws SipException {
+            ServerTransaction inviteTransaction, String externalIp,
+            int externalPort) throws SipException {
         try {
             Request request = event.getRequest();
             Response response = mMessageFactory.createResponse(Response.OK,
                     request);
-            response.addHeader(createContactHeader(localProfile));
+            response.addHeader(createContactHeader(localProfile, externalIp,
+                    externalPort));
             response.setContent(sessionDescription,
                     mHeaderFactory.createContentTypeHeader(
                             "application", "sdp"));
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 3b0f5460..47863bd 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -756,21 +756,20 @@
         }
     }
 
-    private class IntervalMeasurementProcess implements
+    private class IntervalMeasurementProcess implements Runnable,
             SipSessionGroup.KeepAliveProcessCallback {
         private static final String TAG = "SipKeepAliveInterval";
         private static final int MAX_INTERVAL = 120; // in seconds
-        private static final int MIN_INTERVAL = 10; // in seconds
+        private static final int MIN_INTERVAL = 5; // in seconds
         private static final int PASS_THRESHOLD = 10;
         private static final int MAX_RETRY_COUNT = 5;
+        private static final int NAT_MEASUREMENT_RETRY_INTERVAL = 120; // in seconds
         private SipSessionGroupExt mGroup;
         private SipSessionGroup.SipSessionImpl mSession;
-        private boolean mRunning;
-        private int mMinInterval = 10; // in seconds
+        private int mMinInterval = DEFAULT_KEEPALIVE_INTERVAL; // in seconds
         private int mMaxInterval;
         private int mInterval;
         private int mPassCount = 0;
-        private int mErrorCount = 0;
 
         public IntervalMeasurementProcess(SipProfile localProfile, int maxInterval) {
             mMaxInterval = (maxInterval < 0) ? MAX_INTERVAL : maxInterval;
@@ -788,8 +787,6 @@
                 // TODO: remove this line once SipWakeupTimer can better handle
                 // variety of timeout values
                 mGroup.setWakeupTimer(new SipWakeupTimer(mContext, mExecutor));
-                mSession = (SipSessionGroup.SipSessionImpl)
-                        mGroup.createSession(null);
             } catch (Exception e) {
                 Log.w(TAG, "start interval measurement error: " + e);
             }
@@ -797,6 +794,11 @@
 
         public void start() {
             synchronized (SipService.this) {
+                Log.d(TAG, "start measurement w interval=" + mInterval);
+                if (mSession == null) {
+                    mSession = (SipSessionGroup.SipSessionImpl)
+                            mGroup.createSession(null);
+                }
                 try {
                     mSession.startKeepAliveProcess(mInterval, this);
                 } catch (SipException e) {
@@ -807,14 +809,23 @@
 
         public void stop() {
             synchronized (SipService.this) {
-                mSession.stopKeepAliveProcess();
+                if (mSession != null) {
+                    mSession.stopKeepAliveProcess();
+                    mSession = null;
+                }
+                mTimer.cancel(this);
             }
         }
 
         private void restart() {
             synchronized (SipService.this) {
+                // Return immediately if the measurement process is stopped
+                if (mSession == null) return;
+
+                Log.d(TAG, "restart measurement w interval=" + mInterval);
                 try {
                     mSession.stopKeepAliveProcess();
+                    mPassCount = 0;
                     mSession.startKeepAliveProcess(mInterval, this);
                 } catch (SipException e) {
                     Log.e(TAG, "restart()", e);
@@ -826,8 +837,6 @@
         @Override
         public void onResponse(boolean portChanged) {
             synchronized (SipService.this) {
-                mErrorCount = 0;
-
                 if (!portChanged) {
                     if (++mPassCount != PASS_THRESHOLD) return;
                     // update the interval, since the current interval is good to
@@ -853,7 +862,6 @@
                 } else {
                     // calculate the new interval and continue.
                     mInterval = (mMaxInterval + mMinInterval) / 2;
-                    mPassCount = 0;
                     if (DEBUG) {
                         Log.d(TAG, "current interval: " + mKeepAliveInterval
                                 + ", test new interval: " + mInterval);
@@ -866,22 +874,32 @@
         // SipSessionGroup.KeepAliveProcessCallback
         @Override
         public void onError(int errorCode, String description) {
+            Log.w(TAG, "interval measurement error: " + description);
+            restartLater();
+        }
+
+        // timeout handler
+        @Override
+        public void run() {
+            mTimer.cancel(this);
+            restart();
+        }
+
+        private void restartLater() {
             synchronized (SipService.this) {
-                Log.w(TAG, "interval measurement error: " + description);
-                if (++mErrorCount < MAX_RETRY_COUNT) {
-                    Log.w(TAG, "  retry count = " + mErrorCount);
-                    mPassCount = 0;
-                    restart();
-                } else {
-                    Log.w(TAG, "  max retry count reached; measurement aborted");
-                }
+                int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
+                Log.d(TAG, "Retry measurement " + interval + "s later.");
+                mTimer.cancel(this);
+                mTimer.set(interval * 1000, this);
             }
         }
     }
 
     private class AutoRegistrationProcess extends SipSessionAdapter
             implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
+        private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
         private String TAG = "SipAudoReg";
+
         private SipSessionGroup.SipSessionImpl mSession;
         private SipSessionGroup.SipSessionImpl mKeepAliveSession;
         private SipSessionListenerProxy mProxy = new SipSessionListenerProxy();
@@ -892,6 +910,8 @@
         private String mErrorMessage;
         private boolean mRunning = false;
 
+        private int mKeepAliveSuccessCount = 0;
+
         private String getAction() {
             return toString();
         }
@@ -915,18 +935,56 @@
             }
         }
 
+        private void startKeepAliveProcess(int interval) {
+            Log.d(TAG, "start keepalive w interval=" + interval);
+            if (mKeepAliveSession == null) {
+                mKeepAliveSession = mSession.duplicate();
+            } else {
+                mKeepAliveSession.stopKeepAliveProcess();
+            }
+            try {
+                mKeepAliveSession.startKeepAliveProcess(interval, this);
+            } catch (SipException e) {
+                Log.e(TAG, "failed to start keepalive w interval=" + interval,
+                        e);
+            }
+        }
+
+        private void stopKeepAliveProcess() {
+            if (mKeepAliveSession != null) {
+                mKeepAliveSession.stopKeepAliveProcess();
+                mKeepAliveSession = null;
+            }
+            mKeepAliveSuccessCount = 0;
+        }
+
         // SipSessionGroup.KeepAliveProcessCallback
         @Override
         public void onResponse(boolean portChanged) {
             synchronized (SipService.this) {
                 if (portChanged) {
-                    restartPortMappingLifetimeMeasurement(
-                            mSession.getLocalProfile(), getKeepAliveInterval());
+                    int interval = getKeepAliveInterval();
+                    if (mKeepAliveSuccessCount < MIN_KEEPALIVE_SUCCESS_COUNT) {
+                        Log.i(TAG, "keepalive doesn't work with interval "
+                                + interval + ", past success count="
+                                + mKeepAliveSuccessCount);
+                        if (interval > DEFAULT_KEEPALIVE_INTERVAL) {
+                            restartPortMappingLifetimeMeasurement(
+                                    mSession.getLocalProfile(), interval);
+                            mKeepAliveSuccessCount = 0;
+                        }
+                    } else {
+                        Log.i(TAG, "keep keepalive going with interval "
+                                + interval + ", past success count="
+                                + mKeepAliveSuccessCount);
+                        mKeepAliveSuccessCount /= 2;
+                    }
                 } else {
                     // Start keep-alive interval measurement on the first
                     // successfully kept-alive SipSessionGroup
                     startPortMappingLifetimeMeasurement(
                             mSession.getLocalProfile());
+                    mKeepAliveSuccessCount++;
                 }
 
                 if (!mRunning || !portChanged) return;
@@ -960,10 +1018,7 @@
             }
 
             mTimer.cancel(this);
-            if (mKeepAliveSession != null) {
-                mKeepAliveSession.stopKeepAliveProcess();
-                mKeepAliveSession = null;
-            }
+            stopKeepAliveProcess();
 
             mRegistered = false;
             setListener(mProxy.getListener());
@@ -975,12 +1030,8 @@
                 if (DEBUGV) {
                     Log.v(TAG, "restart keepalive w interval=" + newInterval);
                 }
-                mKeepAliveSession.stopKeepAliveProcess();
-                try {
-                    mKeepAliveSession.startKeepAliveProcess(newInterval, this);
-                } catch (SipException e) {
-                    Log.e(TAG, "onKeepAliveIntervalChanged()", e);
-                }
+                mKeepAliveSuccessCount = 0;
+                startKeepAliveProcess(newInterval);
             }
         }
 
@@ -1105,14 +1156,7 @@
                         SipProfile localProfile = mSession.getLocalProfile();
                         if ((mKeepAliveSession == null) && (isBehindNAT(mLocalIp)
                                 || localProfile.getSendKeepAlive())) {
-                            mKeepAliveSession = mSession.duplicate();
-                            Log.d(TAG, "start keepalive");
-                            try {
-                                mKeepAliveSession.startKeepAliveProcess(
-                                        getKeepAliveInterval(), this);
-                            } catch (SipException e) {
-                                Log.e(TAG, "AutoRegistrationProcess", e);
-                            }
+                            startKeepAliveProcess(getKeepAliveInterval());
                         }
                     }
                     mMyWakeLock.release(session);
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index c0c1b28..047eb8d 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -119,6 +119,10 @@
     private Map<String, SipSessionImpl> mSessionMap =
             new HashMap<String, SipSessionImpl>();
 
+    // external address observed from any response
+    private String mExternalIp;
+    private int mExternalPort;
+
     /**
      * @param myself the local profile with password crossed out
      * @param password the password of the profile
@@ -175,6 +179,8 @@
 
         mCallReceiverSession = null;
         mSessionMap.clear();
+
+        resetExternalAddress();
     }
 
     synchronized void onConnectivityChanged() {
@@ -190,6 +196,12 @@
         }
     }
 
+    synchronized void resetExternalAddress() {
+        Log.d(TAG, " reset external addr on " + mSipStack);
+        mExternalIp = null;
+        mExternalPort = 0;
+    }
+
     public SipProfile getLocalProfile() {
         return mLocalProfile;
     }
@@ -363,6 +375,21 @@
         return null;
     }
 
+    private void extractExternalAddress(ResponseEvent evt) {
+        Response response = evt.getResponse();
+        ViaHeader viaHeader = (ViaHeader)(response.getHeader(
+                SIPHeaderNames.VIA));
+        if (viaHeader == null) return;
+        int rport = viaHeader.getRPort();
+        String externalIp = viaHeader.getReceived();
+        if ((rport > 0) && (externalIp != null)) {
+            mExternalIp = externalIp;
+            mExternalPort = rport;
+            Log.d(TAG, " got external addr " + externalIp + ":" + rport
+                    + " on " + mSipStack);
+        }
+    }
+
     private class SipSessionCallReceiverImpl extends SipSessionImpl {
         public SipSessionCallReceiverImpl(ISipSessionListener listener) {
             super(listener);
@@ -682,6 +709,7 @@
                     dialog = ((RequestEvent) evt).getDialog();
                 } else if (evt instanceof ResponseEvent) {
                     dialog = ((ResponseEvent) evt).getDialog();
+                    extractExternalAddress((ResponseEvent) evt);
                 }
                 if (dialog != null) mDialog = dialog;
 
@@ -984,7 +1012,8 @@
                 mServerTransaction = mSipHelper.sendInviteOk(mInviteReceived,
                         mLocalProfile,
                         ((MakeCallCommand) evt).getSessionDescription(),
-                        mServerTransaction);
+                        mServerTransaction,
+                        mExternalIp, mExternalPort);
                 startSessionTimer(((MakeCallCommand) evt).getTimeout());
                 return true;
             } else if (END_CALL == evt) {
@@ -1376,6 +1405,7 @@
                     if (evt instanceof ResponseEvent) {
                         if (parseOptionsResult(evt)) {
                             if (mPortChanged) {
+                                resetExternalAddress();
                                 stop();
                             } else {
                                 cancelSessionTimer();
@@ -1405,8 +1435,11 @@
                     if (!mRunning) return;
 
                     if (DEBUG_PING) {
+                        String peerUri = (mPeerProfile == null)
+                                ? "null"
+                                : mPeerProfile.getUriString();
                         Log.d(TAG, "keepalive: " + mLocalProfile.getUriString()
-                                + " --> " + mPeerProfile + ", interval=" + mInterval);
+                                + " --> " + peerUri + ", interval=" + mInterval);
                     }
                     try {
                         sendKeepAlive();