Merge "DropBoxManager.Entry implements Closeable" into gingerbread
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 9e7eedf..912180b 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -195,7 +195,8 @@
     public static final int KEYCODE_TAB             = 61;
     /** Key code constant: Space key. */
     public static final int KEYCODE_SPACE           = 62;
-    /** Key code constant: Symbol modifier key. */
+    /** Key code constant: Symbol modifier key.
+     * Used to enter alternate symbols. */
     public static final int KEYCODE_SYM             = 63;
     /** Key code constant: Explorer special function key.
      * Used to launch a browser application. */
@@ -205,7 +206,8 @@
     public static final int KEYCODE_ENVELOPE        = 65;
     /** Key code constant: Enter key. */
     public static final int KEYCODE_ENTER           = 66;
-    /** Key code constant: Delete key. */
+    /** Key code constant: Backspace key.
+     * Deletes characters before the insertion point. */
     public static final int KEYCODE_DEL             = 67;
     /** Key code constant: '`' (backtick) key. */
     public static final int KEYCODE_GRAVE           = 68;
@@ -227,7 +229,10 @@
     public static final int KEYCODE_SLASH           = 76;
     /** Key code constant: '@' key. */
     public static final int KEYCODE_AT              = 77;
-    /** Key code constant: Number Lock modifier key. */
+    /** Key code constant: Number modifier key.
+     * Used to enter numeric symbols.
+     * This key is not Num Lock; it is more like {@link #KEYCODE_ALT_LEFT} and is
+     * interpreted as an ALT key by {@link android.text.method.MetaKeyKeyListener}. */
     public static final int KEYCODE_NUM             = 78;
     /** Key code constant: Headset Hook key.
      * Used to hang up calls and stop media. */
diff --git a/core/java/android/widget/EdgeGlow.java b/core/java/android/widget/EdgeGlow.java
index 1f7daab..7a990ad 100644
--- a/core/java/android/widget/EdgeGlow.java
+++ b/core/java/android/widget/EdgeGlow.java
@@ -91,6 +91,7 @@
     // How much dragging should effect the height of the glow image.
     // Number determined by user testing.
     private static final int PULL_DISTANCE_GLOW_FACTOR = 5;
+    private static final float PULL_DISTANCE_ALPHA_GLOW_FACTOR = 0.8f;
 
     private static final int VELOCITY_EDGE_FACTOR = 8;
     private static final int VELOCITY_GLOW_FACTOR = 16;
@@ -144,8 +145,10 @@
         mEdgeScaleY = mEdgeScaleYStart = Math.max(
                 HELD_EDGE_SCALE_Y, Math.min(distance * PULL_DISTANCE_EDGE_FACTOR, 1.f));
 
-        mGlowAlpha = mGlowAlphaStart = Math.max(
-                0.5f, Math.min(mGlowAlpha + Math.abs(deltaDistance), MAX_ALPHA));
+        mGlowAlpha = mGlowAlphaStart = Math.min(
+                mGlowAlpha +
+                (Math.abs(deltaDistance) * PULL_DISTANCE_ALPHA_GLOW_FACTOR),
+                MAX_ALPHA);
 
         float glowChange = Math.abs(deltaDistance);
         if (deltaDistance > 0 && mPullDistance < 0) {
@@ -202,8 +205,8 @@
 
         // The edge should always be at least partially visible, regardless
         // of velocity.
-        mEdgeAlphaStart = 0.5f;
-        mEdgeScaleYStart = 0.2f;
+        mEdgeAlphaStart = 0.f;
+        mEdgeScaleY = mEdgeScaleYStart = 0.f;
         // The glow depends more on the velocity, and therefore starts out
         // nearly invisible.
         mGlowAlphaStart = 0.5f;
@@ -213,7 +216,8 @@
         // reflect the strength of the user's scrolling.
         mEdgeAlphaFinish = Math.max(0, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1));
         // Edge should never get larger than the size of its asset.
-        mEdgeScaleYFinish = 1.f;
+        mEdgeScaleYFinish = Math.max(
+                HELD_EDGE_SCALE_Y, Math.min(velocity * VELOCITY_EDGE_FACTOR, 1.f));
 
         // Growth for the size of the glow should be quadratic to properly
         // respond
@@ -281,10 +285,11 @@
                     mGlowAlphaStart = mGlowAlpha;
                     mGlowScaleYStart = mGlowScaleY;
 
+                    // After absorb, the glow and edge should fade to nothing.
                     mEdgeAlphaFinish = 0.f;
-                    mEdgeScaleYFinish = mEdgeScaleY;
+                    mEdgeScaleYFinish = 0.f;
                     mGlowAlphaFinish = 0.f;
-                    mGlowScaleYFinish = mGlowScaleY;
+                    mGlowScaleYFinish = 0.f;
                     break;
                 case STATE_PULL:
                     mState = STATE_PULL_DECAY;
@@ -296,14 +301,21 @@
                     mGlowAlphaStart = mGlowAlpha;
                     mGlowScaleYStart = mGlowScaleY;
 
-                    // After a pull, the glow should fade to nothing.
+                    // After pull, the glow and edge should fade to nothing.
                     mEdgeAlphaFinish = 0.f;
                     mEdgeScaleYFinish = 0.f;
                     mGlowAlphaFinish = 0.f;
                     mGlowScaleYFinish = 0.f;
                     break;
                 case STATE_PULL_DECAY:
-                    // Do nothing; wait for release
+                    // When receding, we want edge to decrease more slowly
+                    // than the glow.
+                    float factor = mGlowScaleYFinish != 0 ? 1
+                            / (mGlowScaleYFinish * mGlowScaleYFinish)
+                            : Float.MAX_VALUE;
+                    mEdgeScaleY = mEdgeScaleYStart +
+                        (mEdgeScaleYFinish - mEdgeScaleYStart) *
+                            interp * factor;
                     break;
                 case STATE_RECEDE:
                     mState = STATE_IDLE;
diff --git a/include/media/Metadata.h b/include/media/Metadata.h
index 241868a..9c915ce 100644
--- a/include/media/Metadata.h
+++ b/include/media/Metadata.h
@@ -91,6 +91,7 @@
     static const Type kPauseAvailable = 29;        // Boolean
     static const Type kSeekBackwardAvailable = 30; // Boolean
     static const Type kSeekForwardAvailable = 31;  // Boolean
+    static const Type kSeekAvailable = 32;         // Boolean
 
     // @param p[inout] The parcel to append the metadata records
     // to. The global metadata header should have been set already.
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
index 21338ca..16b0a4c 100644
--- a/include/media/stagefright/MediaExtractor.h
+++ b/include/media/stagefright/MediaExtractor.h
@@ -45,13 +45,14 @@
     virtual sp<MetaData> getMetaData();
 
     enum Flags {
-        CAN_SEEK_BACKWARD  = 1,
-        CAN_SEEK_FORWARD   = 2,
+        CAN_SEEK_BACKWARD  = 1,  // the "seek 10secs back button"
+        CAN_SEEK_FORWARD   = 2,  // the "seek 10secs forward button"
         CAN_PAUSE          = 4,
+        CAN_SEEK           = 8,  // the "seek bar"
     };
 
     // If subclasses do _not_ override this, the default is
-    // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE
+    // CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_SEEK | CAN_PAUSE
     virtual uint32_t flags() const;
 
 protected:
diff --git a/media/java/android/media/Metadata.java b/media/java/android/media/Metadata.java
index bd25da2..8d408c2 100644
--- a/media/java/android/media/Metadata.java
+++ b/media/java/android/media/Metadata.java
@@ -102,8 +102,9 @@
     public static final int PAUSE_AVAILABLE = 29;         // Boolean
     public static final int SEEK_BACKWARD_AVAILABLE = 30; // Boolean
     public static final int SEEK_FORWARD_AVAILABLE = 31;  // Boolean
+    public static final int SEEK_AVAILABLE = 32;          // Boolean
 
-    private static final int LAST_SYSTEM = 31;
+    private static final int LAST_SYSTEM = 32;
     private static final int FIRST_CUSTOM = 8192;
 
     // Shorthands to set the MediaPlayer's metadata filter.
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
index 2c96d6d..6bded09 100644
--- a/media/libmediaplayerservice/StagefrightPlayer.cpp
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -180,6 +180,10 @@
             Metadata::kSeekForwardAvailable,
             flags & MediaExtractor::CAN_SEEK_FORWARD);
 
+    metadata.appendBool(
+            Metadata::kSeekAvailable,
+            flags & MediaExtractor::CAN_SEEK);
+
     return OK;
 }
 
diff --git a/media/libstagefright/AMRWriter.cpp b/media/libstagefright/AMRWriter.cpp
index 71d48b3..c0b1abe 100644
--- a/media/libstagefright/AMRWriter.cpp
+++ b/media/libstagefright/AMRWriter.cpp
@@ -15,7 +15,6 @@
  */
 
 #include <media/stagefright/AMRWriter.h>
-
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
@@ -23,6 +22,8 @@
 #include <media/stagefright/MediaSource.h>
 #include <media/stagefright/MetaData.h>
 #include <media/mediarecorder.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
 
 namespace android {
 
@@ -194,6 +195,7 @@
     int64_t maxTimestampUs = 0;
     status_t err = OK;
 
+    prctl(PR_SET_NAME, (unsigned long)"AMRWriter", 0, 0, 0);
     while (!mDone) {
         MediaBuffer *buffer;
         err = mSource->read(&buffer);
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 8c17aab..53543b3 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -868,9 +868,7 @@
 }
 
 status_t AwesomePlayer::seekTo(int64_t timeUs) {
-    if (mExtractorFlags
-            & (MediaExtractor::CAN_SEEK_FORWARD
-                | MediaExtractor::CAN_SEEK_BACKWARD)) {
+    if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
         Mutex::Autolock autoLock(mLock);
         return seekTo_l(timeUs);
     }
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 6d00d7c..e53b0a0 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -20,8 +20,9 @@
 
 #include <arpa/inet.h>
 
-#include <ctype.h>
 #include <pthread.h>
+#include <sys/prctl.h>
+#include <sys/resource.h>
 
 #include <media/stagefright/MPEG4Writer.h>
 #include <media/stagefright/MediaBuffer.h>
@@ -1104,6 +1105,7 @@
 void MPEG4Writer::threadFunc() {
     LOGV("threadFunc");
 
+    prctl(PR_SET_NAME, (unsigned long)"MPEG4Writer", 0, 0, 0);
     while (!mDone) {
         {
             Mutex::Autolock autolock(mLock);
@@ -1632,6 +1634,11 @@
     int64_t previousPausedDurationUs = 0;
     int64_t timestampUs;
 
+    if (mIsAudio) {
+        prctl(PR_SET_NAME, (unsigned long)"AudioTrackEncoding", 0, 0, 0);
+    } else {
+        prctl(PR_SET_NAME, (unsigned long)"VideoTrackEncoding", 0, 0, 0);
+    }
     sp<MetaData> meta_data;
 
     mNumSamples = 0;
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
index 9bc94de..8a5fb11 100644
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -41,7 +41,7 @@
 }
 
 uint32_t MediaExtractor::flags() const {
-    return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE;
+    return CAN_SEEK_BACKWARD | CAN_SEEK_FORWARD | CAN_PAUSE | CAN_SEEK;
 }
 
 // static
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index a8f1104..478e40c 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -18,6 +18,9 @@
 
 #include <media/stagefright/foundation/ABitReader.h>
 #include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
 
 namespace android {
 
@@ -41,10 +44,12 @@
     br.skipBits(16);
     parseUE(&br);  // seq_parameter_set_id
 
+    unsigned chroma_format_idc = 1;  // 4:2:0 chroma format
+
     if (profile_idc == 100 || profile_idc == 110
             || profile_idc == 122 || profile_idc == 244
             || profile_idc == 44 || profile_idc == 83 || profile_idc == 86) {
-        unsigned chroma_format_idc = parseUE(&br);
+        chroma_format_idc = parseUE(&br);
         if (chroma_format_idc == 3) {
             br.skipBits(1);  // residual_colour_transform_flag
         }
@@ -85,6 +90,212 @@
 
     *height = (2 - frame_mbs_only_flag)
         * (pic_height_in_map_units_minus1 * 16 + 16);
+
+    if (!frame_mbs_only_flag) {
+        br.getBits(1);  // mb_adaptive_frame_field_flag
+    }
+
+    br.getBits(1);  // direct_8x8_inference_flag
+
+    if (br.getBits(1)) {  // frame_cropping_flag
+        unsigned frame_crop_left_offset = parseUE(&br);
+        unsigned frame_crop_right_offset = parseUE(&br);
+        unsigned frame_crop_top_offset = parseUE(&br);
+        unsigned frame_crop_bottom_offset = parseUE(&br);
+
+        unsigned cropUnitX, cropUnitY;
+        if (chroma_format_idc == 0  /* monochrome */) {
+            cropUnitX = 1;
+            cropUnitY = 2 - frame_mbs_only_flag;
+        } else {
+            unsigned subWidthC = (chroma_format_idc == 3) ? 1 : 2;
+            unsigned subHeightC = (chroma_format_idc == 1) ? 2 : 1;
+
+            cropUnitX = subWidthC;
+            cropUnitY = subHeightC * (2 - frame_mbs_only_flag);
+        }
+
+        LOGV("frame_crop = (%u, %u, %u, %u), cropUnitX = %u, cropUnitY = %u",
+             frame_crop_left_offset, frame_crop_right_offset,
+             frame_crop_top_offset, frame_crop_bottom_offset,
+             cropUnitX, cropUnitY);
+
+        *width -=
+            (frame_crop_left_offset + frame_crop_right_offset) * cropUnitX;
+        *height -=
+            (frame_crop_top_offset + frame_crop_bottom_offset) * cropUnitY;
+    }
+}
+
+status_t getNextNALUnit(
+        const uint8_t **_data, size_t *_size,
+        const uint8_t **nalStart, size_t *nalSize,
+        bool startCodeFollows) {
+    const uint8_t *data = *_data;
+    size_t size = *_size;
+
+    *nalStart = NULL;
+    *nalSize = 0;
+
+    if (size == 0) {
+        return -EAGAIN;
+    }
+
+    // Skip any number of leading 0x00.
+
+    size_t offset = 0;
+    while (offset < size && data[offset] == 0x00) {
+        ++offset;
+    }
+
+    if (offset == size) {
+        return -EAGAIN;
+    }
+
+    // A valid startcode consists of at least two 0x00 bytes followed by 0x01.
+
+    if (offset < 2 || data[offset] != 0x01) {
+        return ERROR_MALFORMED;
+    }
+
+    ++offset;
+
+    size_t startOffset = offset;
+
+    for (;;) {
+        while (offset < size && data[offset] != 0x01) {
+            ++offset;
+        }
+
+        if (offset == size) {
+            if (startCodeFollows) {
+                offset = size + 2;
+                break;
+            }
+
+            return -EAGAIN;
+        }
+
+        if (data[offset - 1] == 0x00 && data[offset - 2] == 0x00) {
+            break;
+        }
+
+        ++offset;
+    }
+
+    size_t endOffset = offset - 2;
+    while (data[endOffset - 1] == 0x00) {
+        --endOffset;
+    }
+
+    *nalStart = &data[startOffset];
+    *nalSize = endOffset - startOffset;
+
+    if (offset + 2 < size) {
+        *_data = &data[offset - 2];
+        *_size = size - offset + 2;
+    } else {
+        *_data = NULL;
+        *_size = 0;
+    }
+
+    return OK;
+}
+
+static sp<ABuffer> FindNAL(
+        const uint8_t *data, size_t size, unsigned nalType,
+        size_t *stopOffset) {
+    const uint8_t *nalStart;
+    size_t nalSize;
+    while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+        if ((nalStart[0] & 0x1f) == nalType) {
+            sp<ABuffer> buffer = new ABuffer(nalSize);
+            memcpy(buffer->data(), nalStart, nalSize);
+            return buffer;
+        }
+    }
+
+    return NULL;
+}
+
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit) {
+    const uint8_t *data = accessUnit->data();
+    size_t size = accessUnit->size();
+
+    sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
+    if (seqParamSet == NULL) {
+        return NULL;
+    }
+
+    int32_t width, height;
+    FindAVCDimensions(seqParamSet, &width, &height);
+
+    size_t stopOffset;
+    sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
+    CHECK(picParamSet != NULL);
+
+    size_t csdSize =
+        1 + 3 + 1 + 1
+        + 2 * 1 + seqParamSet->size()
+        + 1 + 2 * 1 + picParamSet->size();
+
+    sp<ABuffer> csd = new ABuffer(csdSize);
+    uint8_t *out = csd->data();
+
+    *out++ = 0x01;  // configurationVersion
+    memcpy(out, seqParamSet->data() + 1, 3);  // profile/level...
+    out += 3;
+    *out++ = (0x3f << 2) | 1;  // lengthSize == 2 bytes
+    *out++ = 0xe0 | 1;
+
+    *out++ = seqParamSet->size() >> 8;
+    *out++ = seqParamSet->size() & 0xff;
+    memcpy(out, seqParamSet->data(), seqParamSet->size());
+    out += seqParamSet->size();
+
+    *out++ = 1;
+
+    *out++ = picParamSet->size() >> 8;
+    *out++ = picParamSet->size() & 0xff;
+    memcpy(out, picParamSet->data(), picParamSet->size());
+
+#if 0
+    LOGI("AVC seq param set");
+    hexdump(seqParamSet->data(), seqParamSet->size());
+#endif
+
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+    meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
+    meta->setInt32(kKeyWidth, width);
+    meta->setInt32(kKeyHeight, height);
+
+    LOGI("found AVC codec config (%d x %d)", width, height);
+
+    return meta;
+}
+
+bool IsIDR(const sp<ABuffer> &buffer) {
+    const uint8_t *data = buffer->data();
+    size_t size = buffer->size();
+
+    bool foundIDR = false;
+
+    const uint8_t *nalStart;
+    size_t nalSize;
+    while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
+        CHECK_GT(nalSize, 0u);
+
+        unsigned nalType = nalStart[0] & 0x1f;
+
+        if (nalType == 5) {
+            foundIDR = true;
+            break;
+        }
+    }
+
+    return foundIDR;
 }
 
 }  // namespace android
diff --git a/media/libstagefright/include/ARTSPController.h b/media/libstagefright/include/ARTSPController.h
index 300d8f7..c2f3090 100644
--- a/media/libstagefright/include/ARTSPController.h
+++ b/media/libstagefright/include/ARTSPController.h
@@ -46,6 +46,14 @@
 
     void onMessageReceived(const sp<AMessage> &msg);
 
+    virtual uint32_t flags() const {
+        // Seeking 10secs forward or backward is a very expensive operation
+        // for rtsp, so let's not enable that.
+        // The user can always use the seek bar.
+
+        return CAN_PAUSE | CAN_SEEK;
+    }
+
 protected:
     virtual ~ARTSPController();
 
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
index 6602852..62cfc36 100644
--- a/media/libstagefright/include/avc_utils.h
+++ b/media/libstagefright/include/avc_utils.h
@@ -29,6 +29,16 @@
 
 unsigned parseUE(ABitReader *br);
 
+status_t getNextNALUnit(
+        const uint8_t **_data, size_t *_size,
+        const uint8_t **nalStart, size_t *nalSize,
+        bool startCodeFollows = false);
+
+struct MetaData;
+sp<MetaData> MakeAVCCodecSpecificData(const sp<ABuffer> &accessUnit);
+
+bool IsIDR(const sp<ABuffer> &accessUnit);
+
 }  // namespace android
 
 #endif  // AVC_UTILS_H_
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 4a75ee4..a13287e 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -40,81 +40,6 @@
     return mFormat;
 }
 
-static status_t getNextNALUnit(
-        const uint8_t **_data, size_t *_size,
-        const uint8_t **nalStart, size_t *nalSize,
-        bool startCodeFollows = false) {
-    const uint8_t *data = *_data;
-    size_t size = *_size;
-
-    *nalStart = NULL;
-    *nalSize = 0;
-
-    if (size == 0) {
-        return -EAGAIN;
-    }
-
-    // Skip any number of leading 0x00.
-
-    size_t offset = 0;
-    while (offset < size && data[offset] == 0x00) {
-        ++offset;
-    }
-
-    if (offset == size) {
-        return -EAGAIN;
-    }
-
-    // A valid startcode consists of at least two 0x00 bytes followed by 0x01.
-
-    if (offset < 2 || data[offset] != 0x01) {
-        return ERROR_MALFORMED;
-    }
-
-    ++offset;
-
-    size_t startOffset = offset;
-
-    for (;;) {
-        while (offset < size && data[offset] != 0x01) {
-            ++offset;
-        }
-
-        if (offset == size) {
-            if (startCodeFollows) {
-                offset = size + 2;
-                break;
-            }
-
-            return -EAGAIN;
-        }
-
-        if (data[offset - 1] == 0x00 && data[offset - 2] == 0x00) {
-            break;
-        }
-
-        ++offset;
-    }
-
-    size_t endOffset = offset - 2;
-    while (data[endOffset - 1] == 0x00) {
-        --endOffset;
-    }
-
-    *nalStart = &data[startOffset];
-    *nalSize = endOffset - startOffset;
-
-    if (offset + 2 < size) {
-        *_data = &data[offset - 2];
-        *_size = size - offset + 2;
-    } else {
-        *_data = NULL;
-        *_size = 0;
-    }
-
-    return OK;
-}
-
 void ElementaryStreamQueue::clear() {
     mBuffer->setRange(0, 0);
     mFormat.clear();
@@ -433,79 +358,4 @@
     return NULL;
 }
 
-static sp<ABuffer> FindNAL(
-        const uint8_t *data, size_t size, unsigned nalType,
-        size_t *stopOffset) {
-    const uint8_t *nalStart;
-    size_t nalSize;
-    while (getNextNALUnit(&data, &size, &nalStart, &nalSize, true) == OK) {
-        if ((nalStart[0] & 0x1f) == nalType) {
-            sp<ABuffer> buffer = new ABuffer(nalSize);
-            memcpy(buffer->data(), nalStart, nalSize);
-            return buffer;
-        }
-    }
-
-    return NULL;
-}
-
-sp<MetaData> ElementaryStreamQueue::MakeAVCCodecSpecificData(
-        const sp<ABuffer> &accessUnit) {
-    const uint8_t *data = accessUnit->data();
-    size_t size = accessUnit->size();
-
-    sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
-    if (seqParamSet == NULL) {
-        return NULL;
-    }
-
-    int32_t width, height;
-    FindAVCDimensions(seqParamSet, &width, &height);
-
-    size_t stopOffset;
-    sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
-    CHECK(picParamSet != NULL);
-
-    size_t csdSize =
-        1 + 3 + 1 + 1
-        + 2 * 1 + seqParamSet->size()
-        + 1 + 2 * 1 + picParamSet->size();
-
-    sp<ABuffer> csd = new ABuffer(csdSize);
-    uint8_t *out = csd->data();
-
-    *out++ = 0x01;  // configurationVersion
-    memcpy(out, seqParamSet->data() + 1, 3);  // profile/level...
-    out += 3;
-    *out++ = (0x3f << 2) | 1;  // lengthSize == 2 bytes
-    *out++ = 0xe0 | 1;
-
-    *out++ = seqParamSet->size() >> 8;
-    *out++ = seqParamSet->size() & 0xff;
-    memcpy(out, seqParamSet->data(), seqParamSet->size());
-    out += seqParamSet->size();
-
-    *out++ = 1;
-
-    *out++ = picParamSet->size() >> 8;
-    *out++ = picParamSet->size() & 0xff;
-    memcpy(out, picParamSet->data(), picParamSet->size());
-
-#if 0
-    LOGI("AVC seq param set");
-    hexdump(seqParamSet->data(), seqParamSet->size());
-#endif
-
-    sp<MetaData> meta = new MetaData;
-    meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
-
-    meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
-    meta->setInt32(kKeyWidth, width);
-    meta->setInt32(kKeyHeight, height);
-
-    LOGI("found AVC codec config (%d x %d)", width, height);
-
-    return meta;
-}
-
 }  // namespace android
diff --git a/media/libstagefright/mpeg2ts/ESQueue.h b/media/libstagefright/mpeg2ts/ESQueue.h
index 246c390..9eaf834 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.h
+++ b/media/libstagefright/mpeg2ts/ESQueue.h
@@ -56,9 +56,6 @@
             unsigned profile, unsigned sampling_freq_index,
             unsigned channel_configuration);
 
-    static sp<MetaData> MakeAVCCodecSpecificData(
-            const sp<ABuffer> &accessUnit);
-
     DISALLOW_EVIL_CONSTRUCTORS(ElementaryStreamQueue);
 };
 
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 78754e6..10cc88b 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -490,6 +490,8 @@
     : mInitCheck(NO_INIT),
       mFormat(new MetaData),
       mEOSResult(OK),
+      mIsAVC(false),
+      mScanForIDR(true),
       mRTPTimeBase(0),
       mNormalPlayTimeBaseUs(0),
       mLastNormalPlayTimeUs(0) {
@@ -509,6 +511,8 @@
 
     mInitCheck = OK;
     if (!strncmp(desc.c_str(), "H264/", 5)) {
+        mIsAVC = true;
+
         mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
 
         int32_t width, height;
@@ -719,6 +723,20 @@
         return;
     }
 
+    if (mScanForIDR && mIsAVC) {
+        // This pretty piece of code ensures that the first access unit
+        // fed to the decoder after stream-start or seek is guaranteed to
+        // be an IDR frame. This is to workaround limitations of a certain
+        // hardware h.264 decoder that requires this to be the case.
+
+        if (!IsIDR(buffer)) {
+            LOGV("skipping AU while scanning for next IDR frame.");
+            return;
+        }
+
+        mScanForIDR = false;
+    }
+
     Mutex::Autolock autoLock(mLock);
     mBuffers.push_back(buffer);
     mCondition.signal();
@@ -735,6 +753,8 @@
 void APacketSource::flushQueue() {
     Mutex::Autolock autoLock(mLock);
     mBuffers.clear();
+
+    mScanForIDR = true;
 }
 
 int64_t APacketSource::getNormalPlayTimeUs() {
diff --git a/media/libstagefright/rtsp/APacketSource.h b/media/libstagefright/rtsp/APacketSource.h
index 076ddc47..7a77fc6 100644
--- a/media/libstagefright/rtsp/APacketSource.h
+++ b/media/libstagefright/rtsp/APacketSource.h
@@ -65,6 +65,9 @@
     List<sp<ABuffer> > mBuffers;
     status_t mEOSResult;
 
+    bool mIsAVC;
+    bool mScanForIDR;
+
     uint32_t mClockRate;
 
     uint32_t mRTPTimeBase;
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index a31b2b2..2c9cfd3 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -86,6 +86,7 @@
           mFirstAccessUnitNTP(0),
           mNumAccessUnitsReceived(0),
           mCheckPending(false),
+          mCheckGeneration(0),
           mTryTCPInterleaving(false),
           mReceivedFirstRTCPPacket(false) {
         mNetLooper->setName("rtsp net");
@@ -434,6 +435,13 @@
 
             case 'chek':
             {
+                int32_t generation;
+                CHECK(msg->findInt32("generation", &generation));
+                if (generation != mCheckGeneration) {
+                    // This is an outdated message. Ignore.
+                    break;
+                }
+
                 if (mNumAccessUnitsReceived == 0) {
                     LOGI("stream ended? aborting.");
                     (new AMessage('abor', id()))->post();
@@ -454,12 +462,7 @@
                 }
 
                 ++mNumAccessUnitsReceived;
-
-                if (!mCheckPending) {
-                    mCheckPending = true;
-                    sp<AMessage> check = new AMessage('chek', id());
-                    check->post(kAccessUnitTimeoutUs);
-                }
+                postAccessUnitTimeoutCheck();
 
                 size_t trackIndex;
                 CHECK(msg->findSize("track-index", &trackIndex));
@@ -557,6 +560,11 @@
 
                 mSeekPending = true;
 
+                // Disable the access unit timeout until we resumed
+                // playback again.
+                mCheckPending = true;
+                ++mCheckGeneration;
+
                 AString request = "PAUSE ";
                 request.append(mSessionURL);
                 request.append(" RTSP/1.0\r\n");
@@ -612,6 +620,9 @@
                 LOGI("PLAY completed with result %d (%s)",
                      result, strerror(-result));
 
+                mCheckPending = false;
+                postAccessUnitTimeoutCheck();
+
                 if (result == OK) {
                     sp<RefBase> obj;
                     CHECK(msg->findObject("response", &obj));
@@ -674,6 +685,17 @@
         }
     }
 
+    void postAccessUnitTimeoutCheck() {
+        if (mCheckPending) {
+            return;
+        }
+
+        mCheckPending = true;
+        sp<AMessage> check = new AMessage('chek', id());
+        check->setInt32("generation", mCheckGeneration);
+        check->post(kAccessUnitTimeoutUs);
+    }
+
     static void SplitString(
             const AString &s, const char *separator, List<AString> *items) {
         items->clear();
@@ -783,6 +805,7 @@
     uint64_t mFirstAccessUnitNTP;
     int64_t mNumAccessUnitsReceived;
     bool mCheckPending;
+    int32_t mCheckGeneration;
     bool mTryTCPInterleaving;
     bool mReceivedFirstRTCPPacket;
 
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 361cd3b..84024b8 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -130,6 +130,7 @@
 
     // Handler messages
     private static final int MESSAGE_LOCATION_CHANGED = 1;
+    private static final int MESSAGE_PACKAGE_UPDATED = 2;
 
     // wakelock variables
     private final static String WAKELOCK_KEY = "LocationManagerService";
@@ -1826,6 +1827,19 @@
                             handleLocationChangedLocked(location, passive);
                         }
                     }
+                } else if (msg.what == MESSAGE_PACKAGE_UPDATED) {
+                    String packageName = (String) msg.obj;
+                    String packageDot = packageName + ".";
+
+                    // reconnect to external providers after their packages have been updated
+                    if (mNetworkLocationProvider != null &&
+                        mNetworkLocationProviderPackageName.startsWith(packageDot)) {
+                        mNetworkLocationProvider.reconnect();
+                    }
+                    if (mGeocodeProvider != null &&
+                        mGeocodeProviderPackageName.startsWith(packageDot)) {
+                        mGeocodeProvider.reconnect();
+                    }
                 }
             } catch (Exception e) {
                 // Log, don't crash!
@@ -1928,17 +1942,8 @@
     private final PackageMonitor mPackageMonitor = new PackageMonitor() {
         @Override
         public void onPackageUpdateFinished(String packageName, int uid) {
-            String packageDot = packageName + ".";
-
-            // reconnect to external providers after their packages have been updated
-            if (mNetworkLocationProvider != null &&
-                    mNetworkLocationProviderPackageName.startsWith(packageDot)) {
-                mNetworkLocationProvider.reconnect();
-            }
-            if (mGeocodeProvider != null &&
-                    mGeocodeProviderPackageName.startsWith(packageDot)) {
-                mGeocodeProvider.reconnect();
-            }
+            // Called by main thread; divert work to LocationWorker.
+            Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
         }
     };
 
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index d9b49fd..e3131fe 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -41,8 +41,8 @@
 
     private final Context mContext;
     private final Intent mIntent;
-    private final Connection mServiceConnection = new Connection();
-    private IGeocodeProvider mProvider;
+    private final Object mMutex = new Object();  // synchronizes access to mServiceConnection
+    private Connection mServiceConnection = new Connection();  // never null
 
     public GeocoderProxy(Context context, String serviceName) {
         mContext = context;
@@ -50,34 +50,48 @@
         mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
     }
 
+    /**
+     * When unbundled NetworkLocationService package is updated, we
+     * need to unbind from the old version and re-bind to the new one.
+     */
     public void reconnect() {
-        synchronized (mServiceConnection) {
+        synchronized (mMutex) {
             mContext.unbindService(mServiceConnection);
+            mServiceConnection = new Connection();
             mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
         }
     }
 
     private class Connection implements ServiceConnection {
+
+        private IGeocodeProvider mProvider;
+
         public void onServiceConnected(ComponentName className, IBinder service) {
             Log.d(TAG, "onServiceConnected " + className);
-            synchronized (mServiceConnection) {
+            synchronized (this) {
                 mProvider = IGeocodeProvider.Stub.asInterface(service);
             }
         }
 
         public void onServiceDisconnected(ComponentName className) {
             Log.d(TAG, "onServiceDisconnected " + className);
-            synchronized (mServiceConnection) {
+            synchronized (this) {
                 mProvider = null;
             }
         }
+
+        public IGeocodeProvider getProvider() {
+            synchronized (this) {
+                return mProvider;
+            }
+        }
     }
 
     public String getFromLocation(double latitude, double longitude, int maxResults,
             GeocoderParams params, List<Address> addrs) {
         IGeocodeProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
+        synchronized (mMutex) {
+            provider = mServiceConnection.getProvider();
         }
         if (provider != null) {
             try {
@@ -95,8 +109,8 @@
             double upperRightLatitude, double upperRightLongitude, int maxResults,
             GeocoderParams params, List<Address> addrs) {
         IGeocodeProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
+        synchronized (mMutex) {
+            provider = mServiceConnection.getProvider();
         }
         if (provider != null) {
             try {
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index ef2056b..1a1a170 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -45,10 +45,10 @@
 
     private final Context mContext;
     private final String mName;
-    private final String mServiceName;
-    private ILocationProvider mProvider;
-    private Handler mHandler;
-    private final Connection mServiceConnection = new Connection();
+    private final Intent mIntent;
+    private final Handler mHandler;
+    private final Object mMutex = new Object();  // synchronizes access to non-final members
+    private Connection mServiceConnection = new Connection();  // never null
 
     // cached values set by the location manager
     private boolean mLocationTracking = false;
@@ -58,89 +58,105 @@
     private int mNetworkState;
     private NetworkInfo mNetworkInfo;
 
-    // for caching requiresNetwork, requiresSatellite, etc.
-    private DummyLocationProvider mCachedAttributes;
-
     // constructor for proxying location providers implemented in a separate service
     public LocationProviderProxy(Context context, String name, String serviceName,
             Handler handler) {
         mContext = context;
         mName = name;
-        mServiceName = serviceName;
+        mIntent = new Intent(serviceName);
         mHandler = handler;
-        mContext.bindService(new Intent(serviceName), mServiceConnection, Context.BIND_AUTO_CREATE);
+        mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
     }
 
+    /**
+     * When unbundled NetworkLocationService package is updated, we
+     * need to unbind from the old version and re-bind to the new one.
+     */
     public void reconnect() {
-        synchronized (mServiceConnection) {
-            // unbind first
+        synchronized (mMutex) {
             mContext.unbindService(mServiceConnection);
-            mContext.bindService(new Intent(mServiceName), mServiceConnection,
-                Context.BIND_AUTO_CREATE);
+            mServiceConnection = new Connection();
+            mContext.bindService(mIntent, mServiceConnection, Context.BIND_AUTO_CREATE);
         }
     }
 
-    private class Connection implements ServiceConnection {
+    private class Connection implements ServiceConnection, Runnable {
+
+        private ILocationProvider mProvider;
+
+        // for caching requiresNetwork, requiresSatellite, etc.
+        private DummyLocationProvider mCachedAttributes;  // synchronized by mMutex
+
         public void onServiceConnected(ComponentName className, IBinder service) {
             Log.d(TAG, "LocationProviderProxy.onServiceConnected " + className);
-            synchronized (mServiceConnection) {
+            synchronized (this) {
                 mProvider = ILocationProvider.Stub.asInterface(service);
                 if (mProvider != null) {
-                    mHandler.post(mServiceConnectedTask);
+                    mHandler.post(this);
                 }
             }
         }
 
         public void onServiceDisconnected(ComponentName className) {
             Log.d(TAG, "LocationProviderProxy.onServiceDisconnected " + className);
-            synchronized (mServiceConnection) {
+            synchronized (this) {
                 mProvider = null;
             }
         }
-    }
 
-    private Runnable mServiceConnectedTask = new Runnable() {
+        public synchronized ILocationProvider getProvider() {
+            return mProvider;
+        }
+
+        public synchronized DummyLocationProvider getCachedAttributes() {
+            return mCachedAttributes;
+        }
+
         public void run() {
-            ILocationProvider provider;
-            synchronized (mServiceConnection) {
-                provider = mProvider;
+            synchronized (mMutex) {
+                if (mServiceConnection != this) {
+                    // This ServiceConnection no longer the one we want to bind to.
+                    return;
+                }
+                ILocationProvider provider = getProvider();
                 if (provider == null) {
                     return;
                 }
-            }
 
-            if (mCachedAttributes == null) {
+                // resend previous values from the location manager if the service has restarted
                 try {
-                    mCachedAttributes = new DummyLocationProvider(mName, null);
-                    mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
-                    mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
-                    mCachedAttributes.setRequiresCell(provider.requiresCell());
-                    mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
-                    mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
-                    mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
-                    mCachedAttributes.setSupportsBearing(provider.supportsBearing());
-                    mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
-                    mCachedAttributes.setAccuracy(provider.getAccuracy());
+                    if (mEnabled) {
+                        provider.enable();
+                    }
+                    if (mLocationTracking) {
+                        provider.enableLocationTracking(true);
+                    }
+                    if (mMinTime >= 0) {
+                        provider.setMinTime(mMinTime, mMinTimeSource);
+                    }
+                    if (mNetworkInfo != null) {
+                        provider.updateNetworkState(mNetworkState, mNetworkInfo);
+                    }
                 } catch (RemoteException e) {
-                    mCachedAttributes = null;
                 }
-            }
 
-            // resend previous values from the location manager if the service has restarted
-            try {
-                if (mEnabled) {
-                    provider.enable();
+                // init cache of parameters
+                if (mCachedAttributes == null) {
+                    try {
+                        mCachedAttributes = new DummyLocationProvider(mName, null);
+                        mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
+                        mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
+                        mCachedAttributes.setRequiresCell(provider.requiresCell());
+                        mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
+                        mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
+                        mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
+                        mCachedAttributes.setSupportsBearing(provider.supportsBearing());
+                        mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
+                        mCachedAttributes.setAccuracy(provider.getAccuracy());
+                    } catch (RemoteException e) {
+                        mCachedAttributes = null;
+                    }
                 }
-                if (mLocationTracking) {
-                    provider.enableLocationTracking(true);
-                }
-                if (mMinTime >= 0) {
-                    provider.setMinTime(mMinTime, mMinTimeSource);
-                }
-                if (mNetworkInfo != null) {
-                    provider.updateNetworkState(mNetworkState, mNetworkInfo);
-                }
-            } catch (RemoteException e) {
             }
         }
     };
@@ -149,79 +165,101 @@
         return mName;
     }
 
+    private DummyLocationProvider getCachedAttributes() {
+        synchronized (mMutex) {
+            return mServiceConnection.getCachedAttributes();
+        }
+    }
+
     public boolean requiresNetwork() {
-        if (mCachedAttributes != null) {
-            return mCachedAttributes.requiresNetwork();
+        DummyLocationProvider cachedAttributes = getCachedAttributes();
+        if (cachedAttributes != null) {
+            return cachedAttributes.requiresNetwork();
         } else {
             return false;
         }
     }
 
     public boolean requiresSatellite() {
-        if (mCachedAttributes != null) {
-            return mCachedAttributes.requiresSatellite();
+        DummyLocationProvider cachedAttributes = getCachedAttributes();
+        if (cachedAttributes != null) {
+            return cachedAttributes.requiresSatellite();
         } else {
             return false;
         }
     }
 
     public boolean requiresCell() {
-        if (mCachedAttributes != null) {
-            return mCachedAttributes.requiresCell();
+        DummyLocationProvider cachedAttributes = getCachedAttributes();
+        if (cachedAttributes != null) {
+            return cachedAttributes.requiresCell();
         } else {
             return false;
         }
     }
 
     public boolean hasMonetaryCost() {
-        if (mCachedAttributes != null) {
-            return mCachedAttributes.hasMonetaryCost();
+        DummyLocationProvider cachedAttributes = getCachedAttributes();
+        if (cachedAttributes != null) {
+            return cachedAttributes.hasMonetaryCost();
         } else {
             return false;
         }
     }
 
     public boolean supportsAltitude() {
-        if (mCachedAttributes != null) {
-            return mCachedAttributes.supportsAltitude();
+        DummyLocationProvider cachedAttributes = getCachedAttributes();
+        if (cachedAttributes != null) {
+            return cachedAttributes.supportsAltitude();
         } else {
             return false;
         }
     }
 
     public boolean supportsSpeed() {
-        if (mCachedAttributes != null) {
-            return mCachedAttributes.supportsSpeed();
+        DummyLocationProvider cachedAttributes = getCachedAttributes();
+        if (cachedAttributes != null) {
+            return cachedAttributes.supportsSpeed();
         } else {
             return false;
         }
     }
 
      public boolean supportsBearing() {
-        if (mCachedAttributes != null) {
-            return mCachedAttributes.supportsBearing();
+        DummyLocationProvider cachedAttributes = getCachedAttributes();
+        if (cachedAttributes != null) {
+            return cachedAttributes.supportsBearing();
         } else {
             return false;
         }
     }
 
     public int getPowerRequirement() {
-        if (mCachedAttributes != null) {
-            return mCachedAttributes.getPowerRequirement();
+        DummyLocationProvider cachedAttributes = getCachedAttributes();
+        if (cachedAttributes != null) {
+            return cachedAttributes.getPowerRequirement();
+        } else {
+            return -1;
+        }
+    }
+
+    public int getAccuracy() {
+        DummyLocationProvider cachedAttributes = getCachedAttributes();
+        if (cachedAttributes != null) {
+            return cachedAttributes.getAccuracy();
         } else {
             return -1;
         }
     }
 
     public boolean meetsCriteria(Criteria criteria) {
-       ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
-        }
-        if (provider != null) {
-            try {
-                return provider.meetsCriteria(criteria);
-            } catch (RemoteException e) {
+        synchronized (mMutex) {
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    return provider.meetsCriteria(criteria);
+                } catch (RemoteException e) {
+                }
             }
         }
         // default implementation if we lost connection to the provider
@@ -246,50 +284,42 @@
         return true;
     }
 
-    public int getAccuracy() {
-        if (mCachedAttributes != null) {
-            return mCachedAttributes.getAccuracy();
-        } else {
-            return -1;
-        }
-    }
-
     public void enable() {
-        mEnabled = true;
-        ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
-        }
-        if (provider != null) {
-            try {
-                provider.enable();
-            } catch (RemoteException e) {
+        synchronized (mMutex) {
+            mEnabled = true;
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    provider.enable();
+                } catch (RemoteException e) {
+                }
             }
         }
     }
 
     public void disable() {
-        mEnabled = false;
-        ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
-        }
-        if (provider != null) {
-            try {
-                provider.disable();
-            } catch (RemoteException e) {
+        synchronized (mMutex) {
+            mEnabled = false;
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    provider.disable();
+                } catch (RemoteException e) {
+                }
             }
         }
     }
 
     public boolean isEnabled() {
-        return mEnabled;
+        synchronized (mMutex) {
+            return mEnabled;
+        }
     }
 
     public int getStatus(Bundle extras) {
         ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
+        synchronized (mMutex) {
+            provider = mServiceConnection.getProvider();
         }
         if (provider != null) {
             try {
@@ -301,9 +331,9 @@
     }
 
     public long getStatusUpdateTime() {
-         ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
+        ILocationProvider provider;
+        synchronized (mMutex) {
+            provider = mServiceConnection.getProvider();
         }
         if (provider != null) {
             try {
@@ -315,32 +345,39 @@
      }
 
     public String getInternalState() {
-        try {
-            return mProvider.getInternalState();
-        } catch (RemoteException e) {
-            Log.e(TAG, "getInternalState failed", e);
-            return null;
-        }
-    }
-
-    public boolean isLocationTracking() {
-        return mLocationTracking;
-    }
-
-    public void enableLocationTracking(boolean enable) {
-        mLocationTracking = enable;
-        if (!enable) {
-            mMinTime = -1;
-            mMinTimeSource.clear();
-        }
         ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
+        synchronized (mMutex) {
+            provider = mServiceConnection.getProvider();
         }
         if (provider != null) {
             try {
-                provider.enableLocationTracking(enable);
+                return provider.getInternalState();
             } catch (RemoteException e) {
+                Log.e(TAG, "getInternalState failed", e);
+            }
+        }
+        return null;
+    }
+
+    public boolean isLocationTracking() {
+        synchronized (mMutex) {
+            return mLocationTracking;
+        }
+    }
+
+    public void enableLocationTracking(boolean enable) {
+        synchronized (mMutex) {
+            mLocationTracking = enable;
+            if (!enable) {
+                mMinTime = -1;
+                mMinTimeSource.clear();
+            }
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    provider.enableLocationTracking(enable);
+                } catch (RemoteException e) {
+                }
             }
         }
     }
@@ -350,88 +387,84 @@
     }
 
     public long getMinTime() {
-        return mMinTime;
+        synchronized (mMutex) {
+            return mMinTime;
+        }
     }
 
     public void setMinTime(long minTime, WorkSource ws) {
-        mMinTime = minTime;
-        mMinTimeSource.set(ws);
-        ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
-        }
-        if (provider != null) {
-            try {
-                provider.setMinTime(minTime, ws);
-            } catch (RemoteException e) {
+        synchronized (mMutex) {
+            mMinTime = minTime;
+            mMinTimeSource.set(ws);
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    provider.setMinTime(minTime, ws);
+                } catch (RemoteException e) {
+                }
             }
         }
     }
 
     public void updateNetworkState(int state, NetworkInfo info) {
-        mNetworkState = state;
-        mNetworkInfo = info;
-        ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
-        }
-        if (provider != null) {
-            try {
-                provider.updateNetworkState(state, info);
-            } catch (RemoteException e) {
+        synchronized (mMutex) {
+            mNetworkState = state;
+            mNetworkInfo = info;
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    provider.updateNetworkState(state, info);
+                } catch (RemoteException e) {
+                }
             }
         }
     }
 
     public void updateLocation(Location location) {
-        ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
-        }
-        if (provider != null) {
-            try {
-                provider.updateLocation(location);
-            } catch (RemoteException e) {
+        synchronized (mMutex) {
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    provider.updateLocation(location);
+                } catch (RemoteException e) {
+                }
             }
         }
     }
 
     public boolean sendExtraCommand(String command, Bundle extras) {
-        ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
-        }
-        if (provider != null) {
-            try {
-                provider.sendExtraCommand(command, extras);
-            } catch (RemoteException e) {
+        synchronized (mMutex) {
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    return provider.sendExtraCommand(command, extras);
+                } catch (RemoteException e) {
+                }
             }
         }
         return false;
     }
 
     public void addListener(int uid) {
-        ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
-        }
-        if (provider != null) {
-            try {
-                provider.addListener(uid);
-            } catch (RemoteException e) {
+        synchronized (mMutex) {
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    provider.addListener(uid);
+                } catch (RemoteException e) {
+                }
             }
         }
     }
 
     public void removeListener(int uid) {
-        ILocationProvider provider;
-        synchronized (mServiceConnection) {
-            provider = mProvider;
-        }
-        if (provider != null) {
-            try {
-                provider.removeListener(uid);
-            } catch (RemoteException e) {
+        synchronized (mMutex) {
+            ILocationProvider provider = mServiceConnection.getProvider();
+            if (provider != null) {
+                try {
+                    provider.removeListener(uid);
+                } catch (RemoteException e) {
+                }
             }
         }
     }