Merge "Properly respect NALLengthSize in the AVC Configuration header to determine how many bits encode the nal unit size."
diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl
index 1e79030..e73569a 100644
--- a/core/java/android/os/IMountService.aidl
+++ b/core/java/android/os/IMountService.aidl
@@ -42,17 +42,17 @@
     /**
      * Mount external storage at given mount point.
      */
-    void mountMedia(String mountPoint);
+    void mountVolume(String mountPoint);
 
     /**
      * Safely unmount external storage at given mount point.
      */
-    void unmountMedia(String mountPoint);
+    void unmountVolume(String mountPoint);
 
     /**
      * Format external storage given a mount point.
      */
-    void formatMedia(String mountPoint);
+    void formatVolume(String mountPoint);
 
     /**
      * Returns true if media notification sounds are enabled.
diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java
index e7c19cf..c2928cb 100644
--- a/core/java/android/pim/vcard/VCardParser_V21.java
+++ b/core/java/android/pim/vcard/VCardParser_V21.java
@@ -110,11 +110,11 @@
     private long mTimeHandleBase64;
 
     public VCardParser_V21() {
-        this(VCardConfig.PARSE_TYPE_UNKNOWN);
+        this(null);
     }
 
     public VCardParser_V21(VCardSourceDetector detector) {
-        this(detector.getEstimatedType());
+        this(detector != null ? detector.getEstimatedType() : VCardConfig.PARSE_TYPE_UNKNOWN);
     }
 
     public VCardParser_V21(int parseType) {
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 7f947e9..509aac5 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -583,14 +583,14 @@
          * {@link #ACCOUNT_TYPE} identifies a specific account.
          * <P>Type: TEXT</P>
          */
-        public static final String ACCOUNT_NAME = "account_name";
+        public static final String ACCOUNT_NAME = "_sync_account";
 
         /**
          * The type of account to which this row belongs, which when paired with
          * {@link #ACCOUNT_NAME} identifies a specific account.
          * <P>Type: TEXT</P>
          */
-        public static final String ACCOUNT_TYPE = "account_type";
+        public static final String ACCOUNT_TYPE = "_sync_account_type";
 
         public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
             return new EntityIteratorImpl(cursor, resolver);
diff --git a/core/java/android/view/TransformGestureDetector.java b/core/java/android/view/TransformGestureDetector.java
new file mode 100644
index 0000000..196716a
--- /dev/null
+++ b/core/java/android/view/TransformGestureDetector.java
@@ -0,0 +1,316 @@
+/*
+ * 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 android.view;
+
+import android.content.Context;
+import android.util.Log;
+import android.view.GestureDetector.SimpleOnGestureListener;
+
+/**
+ * Detects transformation gestures involving more than one pointer ("multitouch")
+ * using the supplied {@link MotionEvent}s. The {@link OnGestureListener} callback
+ * will notify users when a particular gesture event has occurred. This class
+ * should only be used with {@link MotionEvent}s reported via touch.
+ * 
+ * To use this class:
+ * <ul>
+ *  <li>Create an instance of the {@code TransformGestureDetector} for your
+ *      {@link View}
+ *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
+ *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
+ *          callback will be executed when the events occur.
+ * </ul>
+ * @hide Pending API approval
+ */
+public class TransformGestureDetector {
+    /**
+     * The listener for receiving notifications when gestures occur.
+     * If you want to listen for all the different gestures then implement
+     * this interface. If you only want to listen for a subset it might
+     * be easier to extend {@link SimpleOnGestureListener}.
+     * 
+     * An application will receive events in the following order:
+     * One onTransformBegin()
+     * Zero or more onTransform()
+     * One onTransformEnd() or onTransformFling()
+     */
+    public interface OnTransformGestureListener {
+        /**
+         * Responds to transformation events for a gesture in progress.
+         * Reported by pointer motion.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return true if the event was handled, false otherwise.
+         */
+        public boolean onTransform(TransformGestureDetector detector);
+        
+        /**
+         * Responds to the beginning of a transformation gesture. Reported by
+         * new pointers going down.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return true if the event was handled, false otherwise.
+         */
+        public boolean onTransformBegin(TransformGestureDetector detector);
+ 
+        /**
+         * Responds to the end of a transformation gesture. Reported by existing
+         * pointers going up. If the end of a gesture would result in a fling,
+         * onTransformFling is called instead.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return true if the event was handled, false otherwise.
+         */
+        public boolean onTransformEnd(TransformGestureDetector detector);
+
+        /**
+         * Responds to the end of a transformation gesture that begins a fling.
+         * Reported by existing pointers going up. If the end of a gesture 
+         * would not result in a fling, onTransformEnd is called instead.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return true if the event was handled, false otherwise.
+         */
+        public boolean onTransformFling(TransformGestureDetector detector);
+    }
+    
+    private static final boolean DEBUG = false;
+    
+    private static final int INITIAL_EVENT_IGNORES = 2;
+    
+    private Context mContext;
+    private float mTouchSizeScale;
+    private OnTransformGestureListener mListener;
+    private int mVelocityTimeUnits;
+    private MotionEvent mInitialEvent;
+    
+    private MotionEvent mPrevEvent;
+    private MotionEvent mCurrEvent;
+    private VelocityTracker mVelocityTracker;
+
+    private float mCenterX;
+    private float mCenterY;
+    private float mTransX;
+    private float mTransY;
+    private float mPrevFingerDiffX;
+    private float mPrevFingerDiffY;
+    private float mCurrFingerDiffX;
+    private float mCurrFingerDiffY;
+    private float mRotateDegrees;
+    private float mCurrLen;
+    private float mPrevLen;
+    private float mScaleFactor;
+    
+    // Units in pixels. Current value is pulled out of thin air for debugging only.
+    private float mPointerJumpLimit = 30;
+    
+    private int mEventIgnoreCount;
+    
+   public TransformGestureDetector(Context context, OnTransformGestureListener listener,
+            int velocityTimeUnits) {
+        mContext = context;
+        mListener = listener;
+        mTouchSizeScale = context.getResources().getDisplayMetrics().widthPixels/3;
+        mVelocityTimeUnits = velocityTimeUnits;
+        mEventIgnoreCount = INITIAL_EVENT_IGNORES;
+    }
+    
+    public TransformGestureDetector(Context context, OnTransformGestureListener listener) {
+        this(context, listener, 1000);
+    }
+    
+    public boolean onTouchEvent(MotionEvent event) {
+        final int action = event.getAction();
+        boolean handled = true;
+
+        if (mInitialEvent == null) {
+            // No transform gesture in progress
+            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
+                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
+                    event.getPointerCount() >= 2) {
+                // We have a new multi-finger gesture
+                mInitialEvent = MotionEvent.obtain(event);
+                mPrevEvent = MotionEvent.obtain(event);
+                mVelocityTracker = VelocityTracker.obtain();
+                handled = mListener.onTransformBegin(this);
+            }
+        } else {
+            // Transform gesture in progress - attempt to handle it
+            switch (action) {
+                case MotionEvent.ACTION_POINTER_1_UP:
+                case MotionEvent.ACTION_POINTER_2_UP:
+                    // Gesture ended
+                    handled = mListener.onTransformEnd(this);
+
+                    reset();
+                    break;
+                    
+                case MotionEvent.ACTION_CANCEL:
+                    handled = mListener.onTransformEnd(this);
+                    
+                    reset();
+                    break;
+                    
+                case MotionEvent.ACTION_MOVE:
+                    setContext(event);
+
+                    // Our first few events can be crazy from some touchscreens - drop them.
+                    if (mEventIgnoreCount == 0) {
+                        mVelocityTracker.addMovement(event);
+                        handled = mListener.onTransform(this);
+                    } else {
+                        mEventIgnoreCount--;
+                    }
+                    
+                    mPrevEvent.recycle();
+                    mPrevEvent = MotionEvent.obtain(event);
+                    break;
+            }
+        }
+        return handled;
+    }
+    
+    private void setContext(MotionEvent curr) {
+        mCurrEvent = MotionEvent.obtain(curr);
+
+        mRotateDegrees = -1;
+        mCurrLen = -1;
+        mPrevLen = -1;
+        mScaleFactor = -1;
+
+        final MotionEvent prev = mPrevEvent;
+        
+        float px0 = prev.getX(0);
+        float py0 = prev.getY(0);
+        float px1 = prev.getX(1);
+        float py1 = prev.getY(1);
+        float cx0 = curr.getX(0);
+        float cy0 = curr.getY(0);
+        float cx1 = curr.getX(1);
+        float cy1 = curr.getY(1);
+
+        // Some touchscreens do weird things with pointer values where points are
+        // too close along one axis. Try to detect this here and smooth things out.
+        // The main indicator is that we get the X or Y value from the other pointer.
+        final float dx0 = cx0 - px0;
+        final float dy0 = cy0 - py0;
+        final float dx1 = cx1 - px1;
+        final float dy1 = cy1 - py1;
+
+        if (cx0 == cx1) {
+            if (Math.abs(dx0) > mPointerJumpLimit) {
+                 cx0 = px0;
+            } else if (Math.abs(dx1) > mPointerJumpLimit) {
+                cx1 = px1;
+            }
+        } else if (cy0 == cy1) {
+            if (Math.abs(dy0) > mPointerJumpLimit) {
+                cy0 = py0;
+            } else if (Math.abs(dy1) > mPointerJumpLimit) {
+                cy1 = py1;
+            }
+        }
+        
+        final float pvx = px1 - px0;
+        final float pvy = py1 - py0;
+        final float cvx = cx1 - cx0;
+        final float cvy = cy1 - cy0;
+        mPrevFingerDiffX = pvx;
+        mPrevFingerDiffY = pvy;
+        mCurrFingerDiffX = cvx;
+        mCurrFingerDiffY = cvy;
+
+        final float pmidx = px0 + pvx * 0.5f;
+        final float pmidy = py0 + pvy * 0.5f;
+        final float cmidx = cx0 + cvx * 0.5f;
+        final float cmidy = cy0 + cvy * 0.5f;
+
+        mCenterX = cmidx;
+        mCenterY = cmidy;
+        mTransX = cmidx - pmidx;
+        mTransY = cmidy - pmidy;
+    }
+    
+    private void reset() {
+        if (mInitialEvent != null) {
+            mInitialEvent.recycle();
+            mInitialEvent = null;
+        }
+        if (mPrevEvent != null) {
+            mPrevEvent.recycle();
+            mPrevEvent = null;
+        }
+        if (mCurrEvent != null) {
+            mCurrEvent.recycle();
+            mCurrEvent = null;
+        }
+        if (mVelocityTracker != null) {
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+        }
+        mEventIgnoreCount = INITIAL_EVENT_IGNORES;
+    }
+    
+    public float getCenterX() {
+        return mCenterX;
+    }
+
+    public float getCenterY() {
+        return mCenterY;
+    }
+
+    public float getTranslateX() {
+        return mTransX;
+    }
+
+    public float getTranslateY() {
+        return mTransY;
+    }
+
+    public float getCurrentSpan() {
+        if (mCurrLen == -1) {
+            final float cvx = mCurrFingerDiffX;
+            final float cvy = mCurrFingerDiffY;
+            mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
+        }
+        return mCurrLen;
+    }
+
+    public float getPreviousSpan() {
+        if (mPrevLen == -1) {
+            final float pvx = mPrevFingerDiffX;
+            final float pvy = mPrevFingerDiffY;
+            mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
+        }
+        return mPrevLen;
+    }
+
+    public float getScaleFactor() {
+        if (mScaleFactor == -1) {
+            mScaleFactor = getCurrentSpan() / getPreviousSpan();
+        }
+        return mScaleFactor;
+    }
+
+    public float getRotation() {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index 5d89c46..9581080 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -55,12 +55,12 @@
                 }
             }, 2));
 
-    final float mPastX[] = new float[NUM_PAST];
-    final float mPastY[] = new float[NUM_PAST];
-    final long mPastTime[] = new long[NUM_PAST];
+    final float mPastX[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
+    final float mPastY[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
+    final long mPastTime[][] = new long[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST];
 
-    float mYVelocity;
-    float mXVelocity;
+    float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
+    float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS];
 
     private VelocityTracker mNext;
 
@@ -105,7 +105,9 @@
      * Reset the velocity tracker back to its initial state.
      */
     public void clear() {
-        mPastTime[0] = 0;
+        for (int i = 0; i < MotionEvent.BASE_AVAIL_POINTERS; i++) {
+            mPastTime[i][0] = 0;
+        }
     }
     
     /**
@@ -120,18 +122,21 @@
     public void addMovement(MotionEvent ev) {
         long time = ev.getEventTime();
         final int N = ev.getHistorySize();
-        for (int i=0; i<N; i++) {
-            addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
-                    ev.getHistoricalEventTime(i));
+        final int pointerCount = ev.getPointerCount();
+        for (int p = 0; p < pointerCount; p++) {
+            for (int i=0; i<N; i++) {
+                addPoint(p, ev.getHistoricalX(p, i), ev.getHistoricalY(p, i),
+                        ev.getHistoricalEventTime(i));
+            }
+            addPoint(p, ev.getX(p), ev.getY(p), time);
         }
-        addPoint(ev.getX(), ev.getY(), time);
     }
 
-    private void addPoint(float x, float y, long time) {
+    private void addPoint(int pos, float x, float y, long time) {
         int drop = -1;
         int i;
         if (localLOGV) Log.v(TAG, "Adding past y=" + y + " time=" + time);
-        final long[] pastTime = mPastTime;
+        final long[] pastTime = mPastTime[pos];
         for (i=0; i<NUM_PAST; i++) {
             if (pastTime[i] == 0) {
                 break;
@@ -146,8 +151,8 @@
             drop = 0;
         }
         if (drop == i) drop--;
-        final float[] pastX = mPastX;
-        final float[] pastY = mPastY;
+        final float[] pastX = mPastX[pos];
+        final float[] pastY = mPastY[pos];
         if (drop >= 0) {
             if (localLOGV) Log.v(TAG, "Dropping up to #" + drop);
             final int start = drop+1;
@@ -190,44 +195,48 @@
      * must be positive.
      */
     public void computeCurrentVelocity(int units, float maxVelocity) {
-        final float[] pastX = mPastX;
-        final float[] pastY = mPastY;
-        final long[] pastTime = mPastTime;
-        
-        // Kind-of stupid.
-        final float oldestX = pastX[0];
-        final float oldestY = pastY[0];
-        final long oldestTime = pastTime[0];
-        float accumX = 0;
-        float accumY = 0;
-        int N=0;
-        while (N < NUM_PAST) {
-            if (pastTime[N] == 0) {
-                break;
+        for (int pos = 0; pos < MotionEvent.BASE_AVAIL_POINTERS; pos++) {
+            final float[] pastX = mPastX[pos];
+            final float[] pastY = mPastY[pos];
+            final long[] pastTime = mPastTime[pos];
+
+            // Kind-of stupid.
+            final float oldestX = pastX[0];
+            final float oldestY = pastY[0];
+            final long oldestTime = pastTime[0];
+            float accumX = 0;
+            float accumY = 0;
+            int N=0;
+            while (N < NUM_PAST) {
+                if (pastTime[N] == 0) {
+                    break;
+                }
+                N++;
             }
-            N++;
+            // Skip the last received event, since it is probably pretty noisy.
+            if (N > 3) N--;
+
+            for (int i=1; i < N; i++) {
+                final int dur = (int)(pastTime[i] - oldestTime);
+                if (dur == 0) continue;
+                float dist = pastX[i] - oldestX;
+                float vel = (dist/dur) * units;   // pixels/frame.
+                if (accumX == 0) accumX = vel;
+                else accumX = (accumX + vel) * .5f;
+
+                dist = pastY[i] - oldestY;
+                vel = (dist/dur) * units;   // pixels/frame.
+                if (accumY == 0) accumY = vel;
+                else accumY = (accumY + vel) * .5f;
+            }
+            mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity)
+                    : Math.min(accumX, maxVelocity);
+            mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity)
+                    : Math.min(accumY, maxVelocity);
+
+            if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity="
+                    + mXVelocity + " N=" + N);
         }
-        // Skip the last received event, since it is probably pretty noisy.
-        if (N > 3) N--;
-        
-        for (int i=1; i < N; i++) {
-            final int dur = (int)(pastTime[i] - oldestTime);
-            if (dur == 0) continue;
-            float dist = pastX[i] - oldestX;
-            float vel = (dist/dur) * units;   // pixels/frame.
-            if (accumX == 0) accumX = vel;
-            else accumX = (accumX + vel) * .5f;
-            
-            dist = pastY[i] - oldestY;
-            vel = (dist/dur) * units;   // pixels/frame.
-            if (accumY == 0) accumY = vel;
-            else accumY = (accumY + vel) * .5f;
-        }
-        mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity);
-        mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) : Math.min(accumY, maxVelocity);
-        
-        if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity="
-                + mXVelocity + " N=" + N);
     }
     
     /**
@@ -237,7 +246,7 @@
      * @return The previously computed X velocity.
      */
     public float getXVelocity() {
-        return mXVelocity;
+        return mXVelocity[0];
     }
     
     /**
@@ -247,6 +256,32 @@
      * @return The previously computed Y velocity.
      */
     public float getYVelocity() {
-        return mYVelocity;
+        return mYVelocity[0];
+    }
+    
+    /**
+     * Retrieve the last computed X velocity.  You must first call
+     * {@link #computeCurrentVelocity(int)} before calling this function.
+     * 
+     * @param pos Which pointer's velocity to return.
+     * @return The previously computed X velocity.
+     * 
+     * @hide Pending API approval
+     */
+    public float getXVelocity(int pos) {
+        return mXVelocity[pos];
+    }
+    
+    /**
+     * Retrieve the last computed Y velocity.  You must first call
+     * {@link #computeCurrentVelocity(int)} before calling this function.
+     * 
+     * @param pos Which pointer's velocity to return.
+     * @return The previously computed Y velocity.
+     * 
+     * @hide Pending API approval
+     */
+    public float getYVelocity(int pos) {
+        return mYVelocity[pos];
     }
 }
diff --git a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
index 000f6c4..2b07ae6 100644
--- a/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
+++ b/core/java/com/android/internal/app/ExternalMediaFormatActivity.java
@@ -102,7 +102,7 @@
                 .getService("mount"));
             if (mountService != null) {
                 try {
-                    mountService.formatMedia(Environment.getExternalStorageDirectory().toString());
+                    mountService.formatVolume(Environment.getExternalStorageDirectory().toString());
                 } catch (RemoteException e) {
                 }
             }
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 115dd62..7d100eb 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -39,6 +39,10 @@
         mType = t;
     }
 
+    public Type getType() {
+        return mType;
+    }
+
     public void uploadToTexture(int baseMipLevel) {
         mRS.validate();
         mRS.validateSurface();
diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java
index 9d70cb2..1614ec5 100644
--- a/graphics/java/android/renderscript/Program.java
+++ b/graphics/java/android/renderscript/Program.java
@@ -111,12 +111,13 @@
             mOutputs[mOutputCount++] = e;
         }
 
-        public void addConstant(Type t) throws IllegalStateException {
+        public int addConstant(Type t) throws IllegalStateException {
             // Should check for consistant and non-conflicting names...
             if(mConstantCount >= MAX_CONSTANT) {
                 throw new IllegalArgumentException("Max input count exceeded.");
             }
-            mConstants[mConstantCount++] = t;
+            mConstants[mConstantCount] = t;
+            return mConstantCount++;
         }
 
         public void setTextureCount(int count) throws IllegalArgumentException {
diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp
index b7639be..b528c46 100644
--- a/libs/rs/rsProgram.cpp
+++ b/libs/rs/rsProgram.cpp
@@ -95,7 +95,9 @@
 
 Program::~Program()
 {
-    bindAllocation(NULL);
+    for (uint32_t ct=0; ct < MAX_UNIFORMS; ct++) {
+        bindAllocation(NULL, ct);
+    }
 
     delete[] mInputElements;
     delete[] mOutputElements;
@@ -106,15 +108,16 @@
 }
 
 
-void Program::bindAllocation(Allocation *alloc)
+void Program::bindAllocation(Allocation *alloc, uint32_t slot)
 {
-    if (mConstants.get() == alloc) {
+    LOGE("bind alloc %p %i", alloc, slot);
+    if (mConstants[slot].get() == alloc) {
         return;
     }
-    if (mConstants.get()) {
-        mConstants.get()->removeProgramToDirty(this);
+    if (mConstants[slot].get()) {
+        mConstants[slot].get()->removeProgramToDirty(this);
     }
-    mConstants.set(alloc);
+    mConstants[slot].set(alloc);
     if (alloc) {
         alloc->addProgramToDirty(this);
     }
@@ -239,7 +242,7 @@
 void rsi_ProgramBindConstants(Context *rsc, RsProgram vp, uint32_t slot, RsAllocation constants)
 {
     Program *p = static_cast<Program *>(vp);
-    p->bindAllocation(static_cast<Allocation *>(constants));
+    p->bindAllocation(static_cast<Allocation *>(constants), slot);
 }
 
 void rsi_ProgramBindTexture(Context *rsc, RsProgram vpf, uint32_t slot, RsAllocation a)
diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h
index 4bb7802..a34e89f 100644
--- a/libs/rs/rsProgram.h
+++ b/libs/rs/rsProgram.h
@@ -39,7 +39,7 @@
                        const uint32_t * params, uint32_t paramLength);
     virtual ~Program();
 
-    void bindAllocation(Allocation *);
+    void bindAllocation(Allocation *, uint32_t slot);
     virtual void createShader();
 
     bool isUserProgram() const {return mUserShader.size() > 0;}
@@ -69,7 +69,7 @@
     uint32_t mOutputCount;
     uint32_t mConstantCount;
 
-    ObjectBaseRef<Allocation> mConstants;
+    ObjectBaseRef<Allocation> mConstants[MAX_UNIFORMS];
 
     mutable bool mDirty;
     String8 mShader;
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index 8e59bc2..8849bda 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -69,7 +69,7 @@
     }
     state->mLast.set(this);
 
-    const float *f = static_cast<const float *>(mConstants->getPtr());
+    const float *f = static_cast<const float *>(mConstants[0]->getPtr());
 
     glMatrixMode(GL_TEXTURE);
     if (mTextureMatrixEnable) {
@@ -116,16 +116,37 @@
 {
     mShader.setTo("");
 
-    for (uint32_t ct=0; ct < mUniformCount; ct++) {
-        mShader.append("uniform mat4 ");
-        mShader.append(mUniformNames[ct]);
-        mShader.append(";\n");
-    }
-
     mShader.append("varying vec4 varColor;\n");
     mShader.append("varying vec4 varTex0;\n");
 
     if (mUserShader.length() > 1) {
+        mShader.append("uniform mat4 ");
+        mShader.append(mUniformNames[0]);
+        mShader.append(";\n");
+
+        LOGE("constant %i ", mConstantCount);
+        for (uint32_t ct=0; ct < mConstantCount; ct++) {
+            const Element *e = mConstantTypes[ct]->getElement();
+            for (uint32_t field=0; field < e->getFieldCount(); field++) {
+                const Element *f = e->getField(field);
+
+                // Cannot be complex
+                rsAssert(!f->getFieldCount());
+                switch(f->getComponent().getVectorSize()) {
+                case 1: mShader.append("uniform float UNI_"); break;
+                case 2: mShader.append("uniform vec2 UNI_"); break;
+                case 3: mShader.append("uniform vec3 UNI_"); break;
+                case 4: mShader.append("uniform vec4 UNI_"); break;
+                default:
+                    rsAssert(0);
+                }
+
+                mShader.append(e->getFieldName(field));
+                mShader.append(";\n");
+            }
+        }
+
+
         for (uint32_t ct=0; ct < mInputCount; ct++) {
             const Element *e = mInputElements[ct].get();
             for (uint32_t field=0; field < e->getFieldCount(); field++) {
@@ -148,6 +169,12 @@
         }
         mShader.append(mUserShader);
     } else {
+        for (uint32_t ct=0; ct < mUniformCount; ct++) {
+            mShader.append("uniform mat4 ");
+            mShader.append(mUniformNames[ct]);
+            mShader.append(";\n");
+        }
+
         for (uint32_t ct=VertexArray::POSITION; ct < mAttribCount; ct++) {
             mShader.append("attribute vec4 ");
             mShader.append(mAttribNames[ct]);
@@ -155,12 +182,12 @@
         }
 
         mShader.append("void main() {\n");
-        mShader.append("  gl_Position = uni_MVP * ATTRIB_Position;\n");
+        mShader.append("  gl_Position = UNI_MVP * ATTRIB_Position;\n");
         mShader.append("  gl_PointSize = ATTRIB_PointSize.x;\n");
 
         mShader.append("  varColor = ATTRIB_Color;\n");
         if (mTextureMatrixEnable) {
-            mShader.append("  varTex0 = uni_TexMatrix * ATTRIB_Texture;\n");
+            mShader.append("  varTex0 = UNI_TexMatrix * ATTRIB_Texture;\n");
         } else {
             mShader.append("  varTex0 = ATTRIB_Texture;\n");
         }
@@ -180,7 +207,7 @@
 
     glVertexAttrib4f(1, state->color[0], state->color[1], state->color[2], state->color[3]);
 
-    const float *f = static_cast<const float *>(mConstants->getPtr());
+    const float *f = static_cast<const float *>(mConstants[0]->getPtr());
 
     Matrix mvp;
     mvp.load(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
@@ -194,6 +221,54 @@
                            &f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET]);
     }
 
+    uint32_t uidx = 1;
+    for (uint32_t ct=0; ct < mConstantCount; ct++) {
+        Allocation *alloc = mConstants[ct+1].get();
+        if (!alloc) {
+            continue;
+        }
+
+        const uint8_t *data = static_cast<const uint8_t *>(alloc->getPtr());
+        const Element *e = mConstantTypes[ct]->getElement();
+        for (uint32_t field=0; field < e->getFieldCount(); field++) {
+            const Element *f = e->getField(field);
+            uint32_t offset = e->getFieldOffsetBytes(field);
+            int32_t slot = sc->vtxUniformSlot(uidx);
+
+            const float *fd = reinterpret_cast<const float *>(&data[offset]);
+
+            //LOGE("Uniform  slot=%i, offset=%i, constant=%i, field=%i, uidx=%i", slot, offset, ct, field, uidx);
+            if (slot >= 0) {
+                switch(f->getComponent().getVectorSize()) {
+                case 1:
+                    //LOGE("Uniform 1 = %f", fd[0]);
+                    glUniform1fv(slot, 1, fd);
+                    break;
+                case 2:
+                    //LOGE("Uniform 2 = %f %f", fd[0], fd[1]);
+                    glUniform2fv(slot, 1, fd);
+                    break;
+                case 3:
+                    //LOGE("Uniform 3 = %f %f %f", fd[0], fd[1], fd[2]);
+                    glUniform3fv(slot, 1, fd);
+                    break;
+                case 4:
+                    //LOGE("Uniform 4 = %f %f %f %f", fd[0], fd[1], fd[2], fd[3]);
+                    glUniform4fv(slot, 1, fd);
+                    break;
+                default:
+                    rsAssert(0);
+                }
+            }
+            uidx ++;
+        }
+    }
+
+    for (uint32_t ct=0; ct < mConstantCount; ct++) {
+        uint32_t glSlot = sc->vtxUniformSlot(ct + 1);
+
+    }
+
     state->mLast.set(this);
     rsc->checkError("ProgramVertex::setupGL2");
 }
@@ -208,46 +283,46 @@
 
 void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const
 {
-    float *f = static_cast<float *>(mConstants->getPtr());
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
 
 void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const
 {
-    float *f = static_cast<float *>(mConstants->getPtr());
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
 
 void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const
 {
-    float *f = static_cast<float *>(mConstants->getPtr());
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
     memcpy(&f[RS_PROGRAM_VERTEX_TEXTURE_OFFSET], m, sizeof(rsc_Matrix));
     mDirty = true;
 }
 
 void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const
 {
-    float *f = static_cast<float *>(mConstants->getPtr());
+    float *f = static_cast<float *>(mConstants[0]->getPtr());
     Matrix mvp;
     mvp.loadMultiply((Matrix *)&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET],
                      (Matrix *)&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]);
     mvp.vectorMultiply(v4out, v3in);
 }
 
-void ProgramVertex::initAddUserAttrib(const Element *e)
+void ProgramVertex::initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix)
 {
     rsAssert(e->getFieldCount());
     for (uint32_t ct=0; ct < e->getFieldCount(); ct++) {
         const Element *ce = e->getField(ct);
         if (ce->getFieldCount()) {
-            initAddUserAttrib(ce);
+            initAddUserElement(ce, names, count, prefix);
         } else {
-            String8 tmp("ATTRIB_");
+            String8 tmp(prefix);
             tmp.append(e->getFieldName(ct));
-            mAttribNames[mAttribCount].setTo(tmp.string());
-            mAttribCount++;
+            names[*count].setTo(tmp.string());
+            (*count)++;
         }
     }
 }
@@ -257,7 +332,13 @@
     if (mUserShader.size() > 0) {
         mAttribCount = 0;
         for (uint32_t ct=0; ct < mInputCount; ct++) {
-            initAddUserAttrib(mInputElements[ct].get());
+            initAddUserElement(mInputElements[ct].get(), mAttribNames, &mAttribCount, "ATTRIB_");
+        }
+
+        mUniformCount = 1;
+        mUniformNames[0].setTo("UNI_MVP");
+        for (uint32_t ct=0; ct < mInputCount; ct++) {
+            initAddUserElement(mConstantTypes[ct]->getElement(), mUniformNames, &mUniformCount, "UNI_");
         }
     } else {
         mAttribCount = 5;
@@ -266,11 +347,11 @@
         mAttribNames[2].setTo("ATTRIB_Normal");
         mAttribNames[3].setTo("ATTRIB_PointSize");
         mAttribNames[4].setTo("ATTRIB_Texture");
-    }
 
-    mUniformCount = 2;
-    mUniformNames[0].setTo("uni_MVP");
-    mUniformNames[1].setTo("uni_TexMatrix");
+        mUniformCount = 2;
+        mUniformNames[0].setTo("UNI_MVP");
+        mUniformNames[1].setTo("UNI_TexMatrix");
+    }
 
     createShader();
 }
@@ -299,7 +380,7 @@
     mDefaultAlloc.set(alloc);
     mDefault.set(pv);
     pv->init(rsc);
-    pv->bindAllocation(alloc);
+    pv->bindAllocation(alloc, 0);
 
     color[0] = 1.f;
     color[1] = 1.f;
diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h
index dcb988c..28554cc 100644
--- a/libs/rs/rsProgramVertex.h
+++ b/libs/rs/rsProgramVertex.h
@@ -61,7 +61,7 @@
     bool mTextureMatrixEnable;
 
 private:
-    void initAddUserAttrib(const Element *e);
+    void initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix);
 };
 
 
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 8a0ab71..3ba9cee 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -973,6 +973,13 @@
     rsi_AllocationUploadToBufferObject(rsc, va);
 }
 
+static void SC_syncToGL(RsAllocation va)
+{
+    GET_TLS();
+    Allocation *a = static_cast<Allocation *>(va);
+
+}
+
 static void SC_ClearColor(float r, float g, float b, float a)
 {
     //LOGE("c %f %f %f %f", r, g, b, a);
@@ -1321,6 +1328,9 @@
     { "uploadToBufferObject", (void *)&SC_uploadToBufferObject,
         "void", "(int)" },
 
+    { "syncToGL", (void *)&SC_syncToGL,
+        "void", "(int)" },
+
     { "colorFloatRGBAtoUNorm8", (void *)&SC_colorFloatRGBAtoUNorm8,
         "int", "(float, float, float, float)" },
     { "colorFloatRGBto565", (void *)&SC_colorFloatRGBAto565,
diff --git a/libs/rs/rsShaderCache.cpp b/libs/rs/rsShaderCache.cpp
index 0d9863de..8ac2487 100644
--- a/libs/rs/rsShaderCache.cpp
+++ b/libs/rs/rsShaderCache.cpp
@@ -132,7 +132,7 @@
                 LOGV("vtx U, %s = %d\n", vtx->getUniformName(ct).string(), e->mVtxUniformSlots[ct]);
             }
         }
-        for (uint32_t ct=0; ct < vtx->getUniformCount(); ct++) {
+        for (uint32_t ct=0; ct < frag->getUniformCount(); ct++) {
             e->mFragUniformSlots[ct] = glGetUniformLocation(pgm, frag->getUniformName(ct));
             if (rsc->props.mLogShaders) {
                 LOGV("frag U, %s = %d\n", frag->getUniformName(ct).string(), e->mFragUniformSlots[ct]);
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index af57a4c..6563caa 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1258,22 +1258,24 @@
 
 static const int NUMVIZBUF = 32;
 static const int VIZBUFFRAMES = 1024;
-static const int TOTALBUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100;
+static const int BUFTIMEMSEC = NUMVIZBUF * VIZBUFFRAMES * 1000 / 44100;
+static const int TOTALBUFTIMEMSEC = NUMVIZBUF * BUFTIMEMSEC;
 
 static bool gotMem = false;
+static sp<MemoryHeapBase> heap; 
 static sp<MemoryBase> mem[NUMVIZBUF];
-static uint64_t timeStamp[NUMVIZBUF];
+static uint64_t endTime;
 static uint64_t lastReadTime;
 static uint64_t lastWriteTime;
 static int writeIdx = 0;
 
 static void allocVizBufs() {
     if (!gotMem) {
+        heap = new MemoryHeapBase(NUMVIZBUF * VIZBUFFRAMES * 2, 0, "snooper");
         for (int i=0;i<NUMVIZBUF;i++) {
-            sp<MemoryHeapBase> heap = new MemoryHeapBase(VIZBUFFRAMES*2, 0, "snooper");
-            mem[i] = new MemoryBase(heap, 0, heap->getSize());
-            timeStamp[i] = 0;
+            mem[i] = new MemoryBase(heap, VIZBUFFRAMES * 2 * i, VIZBUFFRAMES * 2);
         }
+        endTime = 0;
         gotMem = true;
     }
 }
@@ -1290,68 +1292,48 @@
 
     allocVizBufs();
 
-    lastReadTime = uptimeMillis() + 100; // account for renderer delay (we shouldn't be doing this here)
+    lastReadTime = uptimeMillis();
 
     // if there is no recent buffer (yet), just return empty handed
     if (lastWriteTime + TOTALBUFTIMEMSEC < lastReadTime) {
-        //LOGI("@@@@    no audio data to look at yet");
+        //LOGI("@@@@    no audio data to look at yet: %d + %d < %d", (int)lastWriteTime, TOTALBUFTIMEMSEC, (int)lastReadTime);
         return NULL;
     }
 
-    char buf[200];
-
-    int closestIdx = -1;
-    uint32_t closestTime = 0x7ffffff;
-
-    for (int i = 0; i < NUMVIZBUF; i++) {
-        uint64_t tsi = timeStamp[i];
-        uint64_t diff = tsi > lastReadTime ? tsi - lastReadTime : lastReadTime - tsi;
-        if (diff < closestTime) {
-            closestIdx = i;
-            closestTime = diff;
-        }
+    int timedelta = endTime - lastReadTime;
+    if (timedelta < 0) timedelta = 0;
+    int framedelta = timedelta * 44100 / 1000;
+    int headIdx = (writeIdx - framedelta) / VIZBUFFRAMES - 1;
+    while (headIdx < 0) {
+        headIdx += NUMVIZBUF;
     }
-
-
-    if (closestIdx >= 0) {
-        //LOGI("@@@ return buffer %d, %d/%d", closestIdx, uint32_t(lastReadTime), uint32_t(timeStamp[closestIdx]));
-        return mem[closestIdx];
-    }
-
-    // we won't get here, since we either bailed out early, or got a buffer
-    LOGD("Didn't expect to be here");
-    return NULL;
+    return mem[headIdx];
 }
 
-static void storeVizBuf(const void *data, int len, uint64_t time) {
-    // Copy the data in to the visualizer buffer
-    // Assume a 16 bit stereo source for now.
-    short *viz = (short*)mem[writeIdx]->pointer();
-    short *src = (short*)data;
-    for (int i = 0; i < VIZBUFFRAMES; i++) {
-        // Degrade quality by mixing to mono and clearing the lowest 3 bits.
-        // This should still be good enough for a visualization
-        *viz++ = ((int(src[0]) + int(src[1])) >> 1) & ~0x7;
-        src += 2;
-    }
-    timeStamp[writeIdx++] = time;
-    if (writeIdx >= NUMVIZBUF) {
-        writeIdx = 0;
-    }
-}
-
+// Append the data to the vizualization buffer
 static void makeVizBuffers(const char *data, int len, uint64_t time) {
 
     allocVizBufs();
 
     uint64_t startTime = time;
     const int frameSize = 4; // 16 bit stereo sample is 4 bytes
-    while (len >= VIZBUFFRAMES * frameSize) {
-        storeVizBuf(data, len, time);
-        data += VIZBUFFRAMES * frameSize;
-        len -= VIZBUFFRAMES * frameSize;
-        time += 1000 * VIZBUFFRAMES / 44100;
+    int offset = writeIdx;
+    int maxoff = heap->getSize() / 2; // in shorts
+    short *base = (short*)heap->getBase();
+    short *src = (short*)data;
+    while (len > 0) {
+        
+        // Degrade quality by mixing to mono and clearing the lowest 3 bits.
+        // This should still be good enough for a visualization
+        base[offset++] = ((int(src[0]) + int(src[1])) >> 1) & ~0x7;
+        src += 2;
+        len -= frameSize;
+        if (offset >= maxoff) {
+            offset = 0;
+        }
     }
+    writeIdx = offset;
+    endTime = time + (len / frameSize) / 44;
     //LOGI("@@@ stored buffers from %d to %d", uint32_t(startTime), uint32_t(time));
 }
 
@@ -1509,30 +1491,35 @@
     }
 }
 
+void MediaPlayerService::AudioOutput::snoopWrite(const void* buffer, size_t size) {
+    // Only make visualization buffers if anyone recently requested visualization data
+    uint64_t now = uptimeMillis();
+    if (lastReadTime + TOTALBUFTIMEMSEC >= now) {
+        // Based on the current play counter, the number of frames written and
+        // the current real time we can calculate the approximate real start
+        // time of the buffer we're about to write.
+        uint32_t pos;
+        mTrack->getPosition(&pos);
+
+        // we're writing ahead by this many frames:
+        int ahead = mNumFramesWritten - pos;
+        //LOGI("@@@ written: %d, playpos: %d, latency: %d", mNumFramesWritten, pos, mTrack->latency());
+        // which is this many milliseconds, assuming 44100 Hz:
+        ahead /= 44;
+
+        makeVizBuffers((const char*)buffer, size, now + ahead + mTrack->latency());
+        lastWriteTime = now;
+    }
+}
+
+
 ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
 {
     LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
 
     //LOGV("write(%p, %u)", buffer, size);
     if (mTrack) {
-        // Only make visualization buffers if anyone recently requested visualization data
-        uint64_t now = uptimeMillis();
-        if (lastReadTime + TOTALBUFTIMEMSEC >= now) {
-            // Based on the current play counter, the number of frames written and
-            // the current real time we can calculate the approximate real start
-            // time of the buffer we're about to write.
-            uint32_t pos;
-            mTrack->getPosition(&pos);
-
-            // we're writing ahead by this many frames:
-            int ahead = mNumFramesWritten - pos;
-            //LOGI("@@@ written: %d, playpos: %d, latency: %d", mNumFramesWritten, pos, mTrack->latency());
-            // which is this many milliseconds, assuming 44100 Hz:
-            ahead /= 44;
-
-            makeVizBuffers((const char*)buffer, size, now + ahead + mTrack->latency());
-            lastWriteTime = now;
-        }
+        snoopWrite(buffer, size);
         ssize_t ret = mTrack->write(buffer, size);
         mNumFramesWritten += ret / 4; // assume 16 bit stereo
         return ret;
@@ -1580,6 +1567,7 @@
 // static
 void MediaPlayerService::AudioOutput::CallbackWrapper(
         int event, void *cookie, void *info) {
+    //LOGV("callbackwrapper");
     if (event != AudioTrack::EVENT_MORE_DATA) {
         return;
     }
@@ -1589,6 +1577,7 @@
 
     (*me->mCallback)(
             me, buffer->raw, buffer->size, me->mCallbackCookie);
+    me->snoopWrite(buffer->raw, buffer->size);
 }
 
 #undef LOG_TAG
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 1c90cf9..d1206b4 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -113,6 +113,7 @@
 
         public: // visualization hack support
         uint32_t                mNumFramesWritten;
+        void                    snoopWrite(const void*, size_t);
     };
 
     class AudioCache : public MediaPlayerBase::AudioSink
diff --git a/services/java/com/android/server/INativeDaemonConnectorCallbacks.java b/services/java/com/android/server/INativeDaemonConnectorCallbacks.java
new file mode 100644
index 0000000..6fbf713
--- /dev/null
+++ b/services/java/com/android/server/INativeDaemonConnectorCallbacks.java
@@ -0,0 +1,24 @@
+
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server;
+
+interface INativeDaemonConnectorCallbacks {
+
+    void onDaemonConnected();
+    boolean onEvent(int code, String raw, String[] cooked);
+}
diff --git a/services/java/com/android/server/MountListener.java b/services/java/com/android/server/MountListener.java
deleted file mode 100644
index 9443ff8..0000000
--- a/services/java/com/android/server/MountListener.java
+++ /dev/null
@@ -1,377 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package com.android.server;
-
-import android.net.LocalSocketAddress;
-import android.net.LocalSocket;
-import android.os.Environment;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.util.Config;
-import android.util.Log;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Socket;
-import java.lang.IllegalStateException;
-
-import java.util.List;
-import java.util.ArrayList;
-import java.util.ListIterator;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
-/**
- * Vold Connection class
- */
-final class MountListener implements Runnable {
-    private static final String TAG = "MountListener";
-    private static final String VOLD_SOCKET = "vold";
-    private static final int    RESPONSE_QUEUE_SIZE = 10;
-
-    private MountService          mService;
-    private BlockingQueue<String> mResponseQueue;
-    private OutputStream          mOutputStream;
-
-    class ResponseCode {
-        public static final int ActionInitiated                = 100;
-        public static final int VolumeListResult               = 110;
-        public static final int AsecListResult                 = 111;
-
-        public static final int CommandOkay                    = 200;
-        public static final int ShareAvailabilityResult        = 210;
-        public static final int AsecPathResult                 = 211;
-
-        public static final int UnsolicitedInformational       = 600;
-        public static final int VolumeStateChange              = 605;
-        public static final int VolumeMountFailedBlank         = 610;
-        public static final int VolumeMountFailedDamaged       = 611;
-        public static final int VolumeMountFailedNoMedia       = 612;
-        public static final int ShareAvailabilityChange        = 620;
-        public static final int VolumeDiskInserted             = 630;
-        public static final int VolumeDiskRemoved              = 631;
-        public static final int VolumeBadRemoval               = 632;
-    }
-
-    MountListener(MountService service) {
-        mService = service;
-        mResponseQueue = new LinkedBlockingQueue<String>(RESPONSE_QUEUE_SIZE);
-    }
-
-    public void run() {
-        // Vold does not run in the simulator, so fake out a mounted event to trigger the Media Scanner
-        if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
-            mService.notifyMediaMounted(Environment.getExternalStorageDirectory().getPath(), false);
-            return;
-        }
-
-        try {
-            while (true) {
-                listenToSocket();
-            }
-        } catch (Throwable t) {
-            Log.e(TAG, "Fatal error " + t + " in MountListener thread!");
-        }
-    }
-
-    private void listenToSocket() {
-       LocalSocket socket = null;
-
-        try {
-            socket = new LocalSocket();
-            LocalSocketAddress address = new LocalSocketAddress(VOLD_SOCKET, 
-                    LocalSocketAddress.Namespace.RESERVED);
-
-            socket.connect(address);
-            mService.onVoldConnected();
-
-            InputStream inputStream = socket.getInputStream();
-            mOutputStream = socket.getOutputStream();
-
-            byte[] buffer = new byte[4096];
-
-            while (true) {
-                int count = inputStream.read(buffer);
-                if (count < 0) break;
-
-                int start = 0;
-                for (int i = 0; i < count; i++) {
-                    if (buffer[i] == 0) {
-                        String event = new String(buffer, start, i - start);
-//                        Log.d(TAG, "Got packet {" + event + "}");
-
-                        String[] tokens = event.split(" ");
-                        try {
-                            int code = Integer.parseInt(tokens[0]);
-
-                            if (code >= ResponseCode.UnsolicitedInformational) {
-                                try {
-                                    handleUnsolicitedEvent(code, event, tokens);
-                                } catch (Exception ex) {
-                                    Log.e(TAG, String.format(
-                                            "Error handling '%s'", event), ex);
-                                }
-                            } else {
-                                try {
-                                    mResponseQueue.put(event);
-                                } catch (InterruptedException ex) {
-                                    Log.e(TAG, "InterruptedException");
-                                }
-                            }
-                        } catch (NumberFormatException nfe) {
-                            Log.w(TAG,
-                                  "Unknown msg from Vold '" + event + "'");
-                        }
-                        start = i + 1;
-                    }                   
-                }
-            }                
-        } catch (IOException ex) {
-            Log.e(TAG, "IOException in listenToSocket");
-        }
-        
-        synchronized (this) {
-            if (mOutputStream != null) {
-                try {
-                    mOutputStream.close();
-                } catch (IOException e) {
-                    Log.w(TAG, "IOException closing output stream");
-                }
-                
-                mOutputStream = null;
-            }
-        }
-        
-        try {
-            if (socket != null) {
-                socket.close();
-            }
-        } catch (IOException ex) {
-            Log.w(TAG, "IOException closing socket");
-        }
-       
-        Log.e(TAG, "Failed to connect to Vold", new IllegalStateException());
-        SystemClock.sleep(5000);
-    }
-
-    private void handleUnsolicitedEvent(int code, String raw,
-                                        String[] cooked) throws IllegalStateException {
-//        Log.d(TAG, "unsolicited {" + raw + "}");
-        if (code == ResponseCode.VolumeStateChange) {
-            // FMT: NNN Volume <label> <mountpoint> state changed from <old_#> (<old_str>) to <new_#> (<new_str>)
-            mService.notifyVolumeStateChange(cooked[2], cooked[3],
-                                             Integer.parseInt(cooked[7]),
-                                             Integer.parseInt(cooked[10]));
-        } else if (code == ResponseCode.VolumeMountFailedBlank) {
-            // FMT: NNN Volume <label> <mountpoint> mount failed - no supported file-systems
-            mService.notifyMediaNoFs(cooked[3]);
-            // FMT: NNN Volume <label> <mountpoint> mount failed - no media
-        } else if (code == ResponseCode.VolumeMountFailedNoMedia) {
-            mService.notifyMediaRemoved(cooked[3]);
-        } else if (code == ResponseCode.VolumeMountFailedDamaged) {
-            // FMT: NNN Volume <label> <mountpoint> mount failed - filesystem check failed
-            mService.notifyMediaUnmountable(cooked[3]);
-        } else if (code == ResponseCode.ShareAvailabilityChange) {
-            // FMT: NNN Share method <method> now <available|unavailable>
-            boolean avail = false;
-            if (cooked[5].equals("available")) {
-                avail = true;
-            }
-            mService.notifyShareAvailabilityChange(cooked[3], avail);
-        } else if (code == ResponseCode.VolumeDiskInserted) {
-            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
-            mService.notifyMediaInserted(cooked[3]);
-        } else if (code == ResponseCode.VolumeDiskRemoved) {
-            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
-            mService.notifyMediaRemoved(cooked[3]);
-        } else if (code == ResponseCode.VolumeBadRemoval) {
-            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
-            mService.notifyMediaBadRemoval(cooked[3]);
-        } else {
-            Log.w(TAG, "Unhandled event {" + raw + "}");
-        }
-    }
-    
-
-    private void sendCommand(String command) {
-        sendCommand(command, null);
-    }
-
-    /**
-     * Sends a command to Vold with a single argument
-     *
-     * @param command  The command to send to the mount service daemon
-     * @param argument The argument to send with the command (or null)
-     */
-    private void sendCommand(String command, String argument) {
-        synchronized (this) {
-            // Log.d(TAG, "sendCommand {" + command + "} {" + argument + "}");
-            if (mOutputStream == null) {
-                Log.e(TAG, "No connection to Vold", new IllegalStateException());
-            } else {
-                StringBuilder builder = new StringBuilder(command);
-                if (argument != null) {
-                    builder.append(argument);
-                }
-                builder.append('\0');
-
-                try {
-                    mOutputStream.write(builder.toString().getBytes());
-                } catch (IOException ex) {
-                    Log.e(TAG, "IOException in sendCommand", ex);
-                }
-            }
-        }
-    }
-
-    private synchronized ArrayList<String> doCommand(String cmd) throws IllegalStateException {
-        sendCommand(cmd);
-
-        ArrayList<String> response = new ArrayList<String>();
-        boolean complete = false;
-        int code = -1;
-
-        while (!complete) {
-            try {
-                String line = mResponseQueue.take();
-//                Log.d(TAG, "Removed off queue -> " + line);
-                String[] tokens = line.split(" ");
-                code = Integer.parseInt(tokens[0]);
-
-                if ((code >= 200) && (code < 600))
-                    complete = true;
-                response.add(line);
-            } catch (InterruptedException ex) {
-                Log.e(TAG, "InterruptedException");
-            }
-        }
-
-        if (code >= 400 && code < 600) {
-            throw new IllegalStateException(String.format(
-                                               "Command %s failed with code %d",
-                                                cmd, code));
-        }
-        return response;
-    }
-
-    boolean getShareAvailable(String method) throws IllegalStateException  {
-        ArrayList<String> rsp = doCommand("share_available " + method);
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code = Integer.parseInt(tok[0]);
-            if (code == ResponseCode.ShareAvailabilityResult) {
-                if (tok[2].equals("available"))
-                    return true;
-                return false;
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
-    }
-
-    /**
-     * Enables or disables USB mass storage support.
-     * 
-     * @param enable  true to enable USB mass storage support
-     */
-    void setShareMethodEnabled(String mountPoint, String method,
-                               boolean enable) throws IllegalStateException {
-        doCommand((enable ? "" : "un") + "share " + mountPoint + " " + method);
-    }
-
-    /**
-     * Mount media at given mount point.
-     */
-    public void mountVolume(String label) throws IllegalStateException {
-        doCommand("mount " + label);
-    }
-
-    /**
-     * Unmount media at given mount point.
-     */
-    public void unmountVolume(String label) throws IllegalStateException {
-        doCommand("unmount " + label);
-    }
-
-    /**
-     * Format media at given mount point.
-     */
-    public void formatVolume(String label) throws IllegalStateException {
-        doCommand("format " + label);
-    }
-
-    public String createAsec(String id, int sizeMb, String fstype, String key,
-                           int ownerUid) throws IllegalStateException {
-        String cmd = String.format("create_asec %s %d %s %s %d",
-                                   id, sizeMb, fstype, key, ownerUid);
-        doCommand(cmd);
-        return getAsecPath(id);
-    }
-
-    public void finalizeAsec(String id) throws IllegalStateException {
-        doCommand("finalize_asec " + id);
-    }
-
-    public void destroyAsec(String id) throws IllegalStateException {
-        doCommand("destroy_asec " + id);
-    }
-
-    public String mountAsec(String id, String key, int ownerUid) throws IllegalStateException {
-        String cmd = String.format("mount_asec %s %s %d",
-                                   id, key, ownerUid);
-        doCommand(cmd);
-        return getAsecPath(id);
-    }
-
-    public String getAsecPath(String id) throws IllegalStateException {
-        ArrayList<String> rsp = doCommand("asec_path " + id);
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code = Integer.parseInt(tok[0]);
-            if (code == ResponseCode.AsecPathResult) {
-                return tok[1];
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
-    }
-
-    public String[] listAsec() throws IllegalStateException {
-        ArrayList<String> rsp = doCommand("list_asec");
-
-        String[] rdata = new String[rsp.size()];
-        int idx = 0;
-
-        for (String line : rsp) {
-            String []tok = line.split(" ");
-            int code = Integer.parseInt(tok[0]);
-            if (code == ResponseCode.AsecListResult) {
-                rdata[idx++] = tok[1];
-            } else if (code == ResponseCode.CommandOkay) {
-                return rdata;
-            } else {
-                throw new IllegalStateException(String.format("Unexpected response code %d", code));
-            }
-        }
-        throw new IllegalStateException("Got an empty response");
-    }
-}
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 81ebe03..c8a6915 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -33,6 +33,7 @@
 import android.os.Handler;
 import android.text.TextUtils;
 import android.util.Log;
+import java.util.ArrayList;
 
 import android.provider.Settings;
 import android.content.ContentResolver;
@@ -46,7 +47,8 @@
  * MountService implements an to the mount service daemon
  * @hide
  */
-class MountService extends IMountService.Stub {
+class MountService extends IMountService.Stub
+        implements INativeDaemonConnectorCallbacks {
     
     private static final String TAG = "MountService";
 
@@ -63,15 +65,33 @@
         public static final int SharedMnt  = 8;
     }
 
+    class VoldResponseCode {
+        public static final int VolumeListResult               = 110;
+        public static final int AsecListResult                 = 111;
+
+        public static final int ShareAvailabilityResult        = 210;
+        public static final int AsecPathResult                 = 211;
+
+        public static final int VolumeStateChange              = 605;
+        public static final int VolumeMountFailedBlank         = 610;
+        public static final int VolumeMountFailedDamaged       = 611;
+        public static final int VolumeMountFailedNoMedia       = 612;
+        public static final int ShareAvailabilityChange        = 620;
+        public static final int VolumeDiskInserted             = 630;
+        public static final int VolumeDiskRemoved              = 631;
+        public static final int VolumeBadRemoval               = 632;
+    }
+
+
     /**
      * Binder context for this service
      */
     private Context mContext;
     
     /**
-     * listener object for communicating with the mount service daemon
+     * connectorr object for communicating with vold
      */
-    private MountListener mListener;
+    private NativeDaemonConnector mConnector;
 
     /**
      * The notification that is shown when a USB mass storage host
@@ -119,12 +139,12 @@
         mContext = context;
 
         // Register a BOOT_COMPLETED handler so that we can start
-        // MountListener. We defer the startup so that we don't
+        // our NativeDaemonConnector. We defer the startup so that we don't
         // start processing events before we ought-to
         mContext.registerReceiver(mBroadcastReceiver,
                 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
 
-        mListener =  new MountListener(this);       
+        mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
         mShowSafeUnmountNotificationWhenUnmounted = false;
 
         mPlaySounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
@@ -202,7 +222,18 @@
             String action = intent.getAction();
 
             if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
-                Thread thread = new Thread(mListener, MountListener.class.getName());
+                /*
+                 * Vold does not run in the simulator, so fake out a mounted
+                 * event to trigger MediaScanner
+                 */
+                if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
+                    notifyMediaMounted(
+                            Environment.getExternalStorageDirectory().getPath(), false);
+                    return;
+                }
+
+                Thread thread = new Thread(
+                        mConnector, NativeDaemonConnector.class.getName());
                 thread.start();
             }
         }
@@ -258,7 +289,7 @@
              */
             try {
                 String m = Environment.getExternalStorageDirectory().toString();
-                unmountMedia(m);
+                unmountVolume(m);
 
                 int retries = 12;
                 while (!state.equals(Environment.MEDIA_UNMOUNTED) && (retries-- >=0)) {
@@ -302,17 +333,14 @@
             String vs = getVolumeState(vp);
 
             if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
-                mListener.unmountVolume(vp);
+                unmountVolume(vp);
                 updateUsbMassStorageNotification(true, false);
             }
 
-            mListener.setShareMethodEnabled(Environment
-                                            .getExternalStorageDirectory()
-                                            .getPath(),
-                                            "ums", enable);
+            setShareMethodEnabled(vp, "ums", enable);
             mUmsEnabled = enable;
             if (!enable) {
-                mountMedia(vp);
+                mountVolume(vp);
                 if (mPromptUms) {
                     updateUsbMassStorageNotification(false, false);
                 } else {
@@ -352,19 +380,19 @@
     /**
      * Attempt to mount external media
      */
-    public void mountMedia(String mountPath) throws IllegalStateException {
+    public void mountVolume(String mountPath) throws IllegalStateException {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires MOUNT_UNMOUNT_FILESYSTEMS permission");
         }
-        mListener.mountVolume(mountPath);
+        mConnector.doCommand(String.format("mount %s", mountPath));
     }
 
     /**
      * Attempt to unmount external media to prepare for eject
      */
-    public void unmountMedia(String mountPath) throws IllegalStateException {
+    public void unmountVolume(String mountPath) throws IllegalStateException {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS) 
                 != PackageManager.PERMISSION_GRANTED) {
@@ -375,23 +403,51 @@
         // to display the notification
         mShowSafeUnmountNotificationWhenUnmounted = true;
 
-        // tell mountd to unmount the media
-        mListener.unmountVolume(mountPath);
+        mConnector.doCommand(String.format("unmount %s", mountPath));
     }
 
     /**
      * Attempt to format external media
      */
-    public void formatMedia(String formatPath) throws IllegalStateException {
+    public void formatVolume(String formatPath) throws IllegalStateException {
         if (mContext.checkCallingOrSelfPermission(
                 android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS) 
                 != PackageManager.PERMISSION_GRANTED) {
             throw new SecurityException("Requires MOUNT_FORMAT_FILESYSTEMS permission");
         }
 
-        mListener.formatVolume(formatPath);
+        mConnector.doCommand(String.format("format %s", formatPath));
     }
 
+    boolean getShareAvailable(String method) throws IllegalStateException  {
+        ArrayList<String> rsp = mConnector.doCommand("share_available " + method);
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == VoldResponseCode.ShareAvailabilityResult) {
+                if (tok[2].equals("available"))
+                    return true;
+                return false;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
+
+    /**
+     * Enables or disables USB mass storage support.
+     * 
+     * @param enable  true to enable USB mass storage support
+     */
+    void setShareMethodEnabled(String mountPoint, String method,
+                               boolean enable) throws IllegalStateException {
+        mConnector.doCommand(String.format(
+                "%sshare %s %s", (enable ? "" : "un"), mountPoint, method));
+    }
+
+
     /**
      * Returns true if we're playing media notification sounds.
      */
@@ -417,7 +473,7 @@
             Log.w(TAG, "Multiple volumes not currently supported");
             return;
         }
-        Log.w(TAG, "State for {" + mountPoint + "} = {" + state + "}");
+        Log.i(TAG, "State for {" + mountPoint + "} = {" + state + "}");
         mLegacyState = state;
     }
 
@@ -455,14 +511,18 @@
         }
     }
 
-    void onVoldConnected() {
+    /**
+     *
+     * Callback from NativeDaemonConnector
+     */
+    public void onDaemonConnected() {
         new Thread() {
             public void run() {
                 try {
                     if (!getVolumeState(Environment.getExternalStorageDirectory().getPath())
                                  .equals(Environment.MEDIA_MOUNTED)) {
                         try {
-                            mountMedia(Environment.getExternalStorageDirectory().getPath());
+                            mountVolume(Environment.getExternalStorageDirectory().getPath());
                         } catch (Exception ex) {
                             Log.w(TAG, "Connection-mount failed");
                         }
@@ -474,7 +534,7 @@
                 }
 
                 try {
-                    boolean avail = mListener.getShareAvailable("ums");
+                    boolean avail = getShareAvailable("ums");
                     notifyShareAvailabilityChange("ums", avail);
                 } catch (Exception ex) {
                     Log.w(TAG, "Failed to get share availability");
@@ -483,6 +543,49 @@
         }.start();
     }
 
+    /**
+     *
+     * Callback from NativeDaemonConnector
+     */
+    public boolean onEvent(int code, String raw, String[] cooked) {
+        // Log.d(TAG, "event {" + raw + "}");
+        if (code == VoldResponseCode.VolumeStateChange) {
+            // FMT: NNN Volume <label> <mountpoint> state changed
+            // from <old_#> (<old_str>) to <new_#> (<new_str>)
+            notifyVolumeStateChange(
+                    cooked[2], cooked[3], Integer.parseInt(cooked[7]),
+                            Integer.parseInt(cooked[10]));
+        } else if (code == VoldResponseCode.VolumeMountFailedBlank) {
+            // FMT: NNN Volume <label> <mountpoint> mount failed - no supported file-systems
+            notifyMediaNoFs(cooked[3]);
+            // FMT: NNN Volume <label> <mountpoint> mount failed - no media
+        } else if (code == VoldResponseCode.VolumeMountFailedNoMedia) {
+            notifyMediaRemoved(cooked[3]);
+        } else if (code == VoldResponseCode.VolumeMountFailedDamaged) {
+            // FMT: NNN Volume <label> <mountpoint> mount failed - filesystem check failed
+            notifyMediaUnmountable(cooked[3]);
+        } else if (code == VoldResponseCode.ShareAvailabilityChange) {
+            // FMT: NNN Share method <method> now <available|unavailable>
+            boolean avail = false;
+            if (cooked[5].equals("available")) {
+                avail = true;
+            }
+            notifyShareAvailabilityChange(cooked[3], avail);
+        } else if (code == VoldResponseCode.VolumeDiskInserted) {
+            // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
+            notifyMediaInserted(cooked[3]);
+        } else if (code == VoldResponseCode.VolumeDiskRemoved) {
+            // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
+            notifyMediaRemoved(cooked[3]);
+        } else if (code == VoldResponseCode.VolumeBadRemoval) {
+            // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
+            notifyMediaBadRemoval(cooked[3]);
+        } else {
+            return false;
+        }
+       return true;
+    }
+
     void notifyVolumeStateChange(String label, String mountPoint, int oldState,
                                  int newState) throws IllegalStateException {
         String vs = getVolumeState(mountPoint);
@@ -587,7 +690,7 @@
         new Thread() {
             public void run() {
                 try {
-                    mountMedia(path);
+                    mountVolume(path);
                 } catch (Exception ex) {
                     Log.w(TAG, "Failed to mount media on insertion", ex);
                 }
@@ -903,29 +1006,62 @@
     }
 
     public String[] getSecureContainerList() throws IllegalStateException {
-        return mListener.listAsec();
+        ArrayList<String> rsp = mConnector.doCommand("list_asec");
+
+        String[] rdata = new String[rsp.size()];
+        int idx = 0;
+
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == VoldResponseCode.AsecListResult) {
+                rdata[idx++] = tok[1];
+            } else if (code == NativeDaemonConnector.ResponseCode.CommandOkay) {
+                return rdata;
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
     }
 
     public String createSecureContainer(String id, int sizeMb, String fstype,
                                     String key, int ownerUid) throws IllegalStateException {
-        return mListener.createAsec(id, sizeMb, fstype, key, ownerUid);
+        String cmd = String.format("create_asec %s %d %s %s %d",
+                                   id, sizeMb, fstype, key, ownerUid);
+        mConnector.doCommand(cmd);
+        return getSecureContainerPath(id);
     }
 
     public void finalizeSecureContainer(String id) throws IllegalStateException {
-        mListener.finalizeAsec(id);
+        mConnector.doCommand(String.format("finalize_asec %s", id));
     }
 
     public void destroySecureContainer(String id) throws IllegalStateException {
-        mListener.destroyAsec(id);
+        mConnector.doCommand(String.format("destroy_asec %s", id));
     }
    
-    public String mountSecureContainer(String id, String key, int ownerUid) throws IllegalStateException {
-        return mListener.mountAsec(id, key, ownerUid);
+    public String mountSecureContainer(String id, String key,
+                                       int ownerUid) throws IllegalStateException {
+        String cmd = String.format("mount_asec %s %s %d",
+                                   id, key, ownerUid);
+        mConnector.doCommand(cmd);
+        return getSecureContainerPath(id);
     }
 
     public String getSecureContainerPath(String id) throws IllegalStateException {
-        return mListener.getAsecPath(id);
-    }
+        ArrayList<String> rsp = mConnector.doCommand("asec_path " + id);
 
+        for (String line : rsp) {
+            String []tok = line.split(" ");
+            int code = Integer.parseInt(tok[0]);
+            if (code == VoldResponseCode.AsecPathResult) {
+                return tok[1];
+            } else {
+                throw new IllegalStateException(String.format("Unexpected response code %d", code));
+            }
+        }
+        throw new IllegalStateException("Got an empty response");
+    }
 }
 
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
new file mode 100644
index 0000000..da3e562
--- /dev/null
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.server;
+
+import android.net.LocalSocketAddress;
+import android.net.LocalSocket;
+import android.os.Environment;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.Config;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.lang.IllegalStateException;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.ListIterator;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+/**
+ * Generic connector class for interfacing with a native
+ * daemon which uses the libsysutils FrameworkListener
+ * protocol.
+ */
+final class NativeDaemonConnector implements Runnable {
+
+    private BlockingQueue<String> mResponseQueue;
+    private OutputStream          mOutputStream;
+    private String                TAG = "NativeDaemonConnector";
+    private String                mSocket;
+    private INativeDaemonConnectorCallbacks mCallbacks;
+
+    class ResponseCode {
+        public static final int ActionInitiated                = 100;
+
+        public static final int CommandOkay                    = 200;
+
+        // The range of 400 -> 599 is reserved for cmd failures
+        public static final int OperationFailed                = 400;
+        public static final int CommandSyntaxError             = 500;
+        public static final int CommandParameterError          = 501;
+
+        public static final int UnsolicitedInformational       = 600;
+
+        //
+        public static final int FailedRangeStart               = 400;
+        public static final int FailedRangeEnd                 = 599;
+    }
+
+    NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks,
+                          String socket, int responseQueueSize, String logTag) {
+        mCallbacks = callbacks;
+        if (logTag != null)
+            TAG = logTag;
+        mSocket = socket;
+        mResponseQueue = new LinkedBlockingQueue<String>(responseQueueSize);
+    }
+
+    public void run() {
+
+        while (true) {
+            try {
+                listenToSocket();
+            } catch (Exception e) {
+                Log.e(TAG, "Error in NativeDaemonConnector", e);
+                SystemClock.sleep(1000);
+            }
+        }
+    }
+
+    private void listenToSocket() {
+       LocalSocket socket = null;
+
+        try {
+            socket = new LocalSocket();
+            LocalSocketAddress address = new LocalSocketAddress(mSocket,
+                    LocalSocketAddress.Namespace.RESERVED);
+
+            socket.connect(address);
+            mCallbacks.onDaemonConnected();
+
+            InputStream inputStream = socket.getInputStream();
+            mOutputStream = socket.getOutputStream();
+
+            byte[] buffer = new byte[4096];
+
+            while (true) {
+                int count = inputStream.read(buffer);
+                if (count < 0) break;
+
+                int start = 0;
+                for (int i = 0; i < count; i++) {
+                    if (buffer[i] == 0) {
+                        String event = new String(buffer, start, i - start);
+//                        Log.d(TAG, "Got packet {" + event + "}");
+
+                        String[] tokens = event.split(" ");
+                        try {
+                            int code = Integer.parseInt(tokens[0]);
+
+                            if (code >= ResponseCode.UnsolicitedInformational) {
+                                try {
+                                    if (!mCallbacks.onEvent(code, event, tokens)) {
+                                        Log.w(TAG, String.format(
+                                                "Unhandled event (%s)", event));
+                                    }
+                                } catch (Exception ex) {
+                                    Log.e(TAG, String.format(
+                                            "Error handling '%s'", event), ex);
+                                }
+                            } else {
+                                try {
+                                    mResponseQueue.put(event);
+                                } catch (InterruptedException ex) {
+                                    Log.e(TAG, "Failed to put response onto queue", ex);
+                                }
+                            }
+                        } catch (NumberFormatException nfe) {
+                            Log.w(TAG, String.format("Bad msg (%s)", event));
+                        }
+                        start = i + 1;
+                    }
+                }
+            }
+        } catch (IOException ex) {
+            Log.e(TAG, "Communications error", ex);
+        }
+
+        synchronized (this) {
+            if (mOutputStream != null) {
+                try {
+                    mOutputStream.close();
+                } catch (IOException e) {
+                    Log.w(TAG, "Failed closing output stream", e);
+                }
+
+                mOutputStream = null;
+            }
+        }
+
+        try {
+            if (socket != null) {
+                socket.close();
+            }
+        } catch (IOException ex) {
+            Log.w(TAG, "Failed closing socket", ex);
+        }
+
+        Log.e(TAG, "Failed to connect to native daemon",
+                new IllegalStateException());
+        SystemClock.sleep(5000);
+    }
+
+    private void sendCommand(String command) {
+        sendCommand(command, null);
+    }
+
+    /**
+     * Sends a command to the daemon with a single argument
+     *
+     * @param command  The command to send to the daemon
+     * @param argument The argument to send with the command (or null)
+     */
+    private void sendCommand(String command, String argument) {
+        synchronized (this) {
+             Log.d(TAG, "sendCommand {" + command + "} {" + argument + "}");
+            if (mOutputStream == null) {
+                Log.e(TAG, "No connection to daemon", new IllegalStateException());
+            } else {
+                StringBuilder builder = new StringBuilder(command);
+                if (argument != null) {
+                    builder.append(argument);
+                }
+                builder.append('\0');
+
+                try {
+                    mOutputStream.write(builder.toString().getBytes());
+                } catch (IOException ex) {
+                    Log.e(TAG, "IOException in sendCommand", ex);
+                }
+            }
+        }
+    }
+
+    public synchronized ArrayList<String> doCommand(String cmd) throws IllegalStateException {
+        sendCommand(cmd);
+
+        ArrayList<String> response = new ArrayList<String>();
+        boolean complete = false;
+        int code = -1;
+
+        while (!complete) {
+            try {
+                String line = mResponseQueue.take();
+//                Log.d(TAG, "Removed off queue -> " + line);
+                String[] tokens = line.split(" ");
+                try {
+                    code = Integer.parseInt(tokens[0]);
+                } catch (NumberFormatException nfe) {
+                    throw new IllegalStateException(
+                            String.format("Invalid response from daemon (%s)", line));
+                }
+
+                if ((code >= 200) && (code < 600))
+                    complete = true;
+                response.add(line);
+            } catch (InterruptedException ex) {
+                Log.e(TAG, "InterruptedException");
+            }
+        }
+
+        if (code >= ResponseCode.FailedRangeStart &&
+                code <= ResponseCode.FailedRangeEnd) {
+            throw new IllegalStateException(String.format(
+                                               "Command %s failed with code %d",
+                                                cmd, code));
+        }
+        return response;
+    }
+}
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index de3dcb0..d5de1f0 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -330,6 +330,17 @@
             return false;
         }
 
+        /**
+         * Multiple calls to unregisterReceiver() cause exception and a system crash.
+         * This can happen if a supplicant is lost (or firmware crash occurs) and user indicates
+         * disable wifi at the same time.
+         * Avoid doing a disable when the current Wifi state is UNKNOWN
+         * TODO: Handle driver load fail and supplicant lost as seperate states
+         */
+        if (mWifiState == WIFI_STATE_UNKNOWN && !enable) {
+            return false;
+        }
+
         setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
 
         if (enable) {
diff --git a/tests/TransformTest/Android.mk b/tests/TransformTest/Android.mk
new file mode 100644
index 0000000..2d3637d
--- /dev/null
+++ b/tests/TransformTest/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := TransformTest
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_PACKAGE)
diff --git a/tests/TransformTest/AndroidManifest.xml b/tests/TransformTest/AndroidManifest.xml
new file mode 100644
index 0000000..5c9995f
--- /dev/null
+++ b/tests/TransformTest/AndroidManifest.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.google.android.test.transform">
+    <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="7" />
+    <application android:label="TransformTest">
+        <activity android:name="TransformTestActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/tests/TransformTest/res/drawable/logo.png b/tests/TransformTest/res/drawable/logo.png
new file mode 100644
index 0000000..4d717a8
--- /dev/null
+++ b/tests/TransformTest/res/drawable/logo.png
Binary files differ
diff --git a/tests/TransformTest/res/values/strings.xml b/tests/TransformTest/res/values/strings.xml
new file mode 100644
index 0000000..a0eb81f
--- /dev/null
+++ b/tests/TransformTest/res/values/strings.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<resources>
+    <string name="act_title">TransformTest</string>
+</resources>
diff --git a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
new file mode 100644
index 0000000..52286d1
--- /dev/null
+++ b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.google.android.test.transform;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.TransformGestureDetector;
+import android.view.View;
+import android.widget.LinearLayout;
+
+public class TransformTestActivity extends Activity {
+    public TransformTestActivity() {
+        super();
+        init(false);
+    }
+    
+    public TransformTestActivity(boolean noCompat) {
+        super();
+        init(noCompat);
+    }
+    
+    public void init(boolean noCompat) {
+
+    }
+    
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        final LayoutInflater li = (LayoutInflater)getSystemService(
+                LAYOUT_INFLATER_SERVICE);
+        
+        this.setTitle(R.string.act_title);
+        LinearLayout root = new LinearLayout(this);
+        root.setOrientation(LinearLayout.VERTICAL);
+
+        TransformView view = new TransformView(getApplicationContext());
+        Drawable drawable = getResources().getDrawable(R.drawable.logo);
+        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicWidth());
+        view.setDrawable(drawable);
+
+        root.addView(view);
+        setContentView(root);
+    }
+    
+    private class TransformView extends View {
+        private Drawable mDrawable;
+        private float mPosX;
+        private float mPosY;
+        private float mScale = 1.f;
+        private Matrix mMatrix;
+        private TransformGestureDetector mDetector;
+        
+        private class Listener implements TransformGestureDetector.OnTransformGestureListener {
+
+            public boolean onTransform(TransformGestureDetector detector) {
+                Log.d("ttest", "Translation: (" + detector.getTranslateX() +
+                        ", " + detector.getTranslateY() + ")");
+                float scale = detector.getScaleFactor();
+                Log.d("ttest", "Scale: " + scale);
+                if (mScale * scale > 0.1f) {
+                    if (mScale * scale < 10.f) {
+                        mScale *= scale;
+                    } else {
+                        mScale = 10.f;
+                    }
+                } else {
+                    mScale = 0.1f;
+                }
+
+                mPosX += detector.getTranslateX();
+                mPosY += detector.getTranslateY();
+                
+                Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")");
+                
+                float sizeX = mDrawable.getIntrinsicWidth()/2;
+                float sizeY = mDrawable.getIntrinsicHeight()/2;
+                float centerX = detector.getCenterX();
+                float centerY = detector.getCenterY();
+                float diffX = centerX - mPosX;
+                float diffY = centerY - mPosY;
+                diffX = diffX*scale - diffX;
+                diffY = diffY*scale - diffY;
+                mPosX -= diffX;
+                mPosY -= diffY;
+                mMatrix.reset();
+                mMatrix.postTranslate(-sizeX, -sizeY);
+                mMatrix.postScale(mScale, mScale);
+                mMatrix.postTranslate(mPosX, mPosY);
+                                
+                invalidate();
+
+                return true;
+            }
+
+            public boolean onTransformBegin(TransformGestureDetector detector) {
+                return true;
+            }
+
+            public boolean onTransformEnd(TransformGestureDetector detector) {
+                return true;
+            }
+
+            public boolean onTransformFling(TransformGestureDetector detector) {
+                return false;
+            }
+            
+        }
+        
+        public TransformView(Context context) {
+            super(context);
+            mMatrix = new Matrix();
+            mDetector = new TransformGestureDetector(context, new Listener());
+            DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+            mPosX = metrics.widthPixels/2;
+            mPosY = metrics.heightPixels/2;
+        }
+        
+        public void setDrawable(Drawable d) {
+            mDrawable = d;
+            
+            float sizeX = mDrawable.getIntrinsicWidth()/2;
+            float sizeY = mDrawable.getIntrinsicHeight()/2;
+            mMatrix.reset();
+            mMatrix.postTranslate(-sizeX, -sizeY);
+            mMatrix.postScale(mScale, mScale);
+            mMatrix.postTranslate(mPosX, mPosY);
+        }
+        
+        @Override
+        public boolean onTouchEvent(MotionEvent event) {
+            boolean handled = mDetector.onTouchEvent(event);
+            
+            int pointerCount = event.getPointerCount();
+            Log.d("ttest", "pointerCount: " + pointerCount);
+
+            return handled;
+        }
+        
+        @Override
+        public void onDraw(Canvas canvas) {
+            int saveCount = canvas.getSaveCount();
+            canvas.save();
+            canvas.concat(mMatrix);
+            mDrawable.draw(canvas);
+            canvas.restoreToCount(saveCount);
+        }
+    }
+}
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index de6ff14..ae8f242 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1768,31 +1768,33 @@
                     fprintf(stderr, "ERROR: %s\n", error.string());
                     return -1;
                 }
-                // asdf     --> package.asdf
-                // .asdf  .a.b  --> package.asdf package.a.b
-                // asdf.adsf --> asdf.asdf
-                String8 rule("-keep class ");
-                const char* p = name.string();
-                const char* q = strchr(p, '.');
-                if (p == q) {
-                    rule += pkg;
-                    rule += name;
-                } else if (q == NULL) {
-                    rule += pkg;
-                    rule += ".";
-                    rule += name;
-                } else {
-                    rule += name;
+                if (name.length() > 0) {
+                    // asdf     --> package.asdf
+                    // .asdf  .a.b  --> package.asdf package.a.b
+                    // asdf.adsf --> asdf.asdf
+                    String8 rule("-keep class ");
+                    const char* p = name.string();
+                    const char* q = strchr(p, '.');
+                    if (p == q) {
+                        rule += pkg;
+                        rule += name;
+                    } else if (q == NULL) {
+                        rule += pkg;
+                        rule += ".";
+                        rule += name;
+                    } else {
+                        rule += name;
+                    }
+
+                    String8 location = tag;
+                    location += " ";
+                    location += assFile->getSourceFile();
+                    char lineno[20];
+                    sprintf(lineno, ":%d", tree.getLineNumber());
+                    location += lineno;
+
+                    keep->add(rule, location);
                 }
-
-                String8 location = tag;
-                location += " ";
-                location += assFile->getSourceFile();
-                char lineno[20];
-                sprintf(lineno, ":%d", tree.getLineNumber());
-                location += lineno;
-
-                keep->add(rule, location);
             }
         }
     }