Merge "Add OBB flags to support overlays" into gingerbread
diff --git a/api/9.xml b/api/9.xml
index f18623f..46f087f 100644
--- a/api/9.xml
+++ b/api/9.xml
@@ -379555,7 +379555,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value=""100-Continue""
+ value=""100-continue""
  static="true"
  final="true"
  deprecated="not deprecated"
diff --git a/api/current.xml b/api/current.xml
index f494775..61f5e16 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -403611,7 +403611,7 @@
  type="java.lang.String"
  transient="false"
  volatile="false"
- value=""100-Continue""
+ value=""100-continue""
  static="true"
  final="true"
  deprecated="not deprecated"
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index c88e086..c96d562 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -3298,10 +3298,6 @@
             Slog.e(TAG, "Failed to find provider info for " + name);
             return null;
         }
-        if (holder.permissionFailure != null) {
-            throw new SecurityException("Permission " + holder.permissionFailure
-                    + " required for provider " + name);
-        }
 
         IContentProvider prov = installProvider(context, holder.provider,
                 holder.info, true);
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index e99d4b4..2870c50 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -2844,6 +2844,7 @@
                 boolean returnValue;
 
                 boolean hasListeners;
+                boolean changesMade = false;
                 List<String> keysModified = null;
                 Set<OnSharedPreferenceChangeListener> listeners = null;
 
@@ -2857,17 +2858,31 @@
 
                     synchronized (this) {
                         if (mClear) {
-                            mMap.clear();
+                            if (!mMap.isEmpty()) {
+                                changesMade = true;
+                                mMap.clear();
+                            }
                             mClear = false;
                         }
 
                         for (Entry<String, Object> e : mModified.entrySet()) {
                             String k = e.getKey();
                             Object v = e.getValue();
-                            if (v == this) {
-                                mMap.remove(k);
+                            if (v == this) {  // magic value for a removal mutation
+                                if (mMap.containsKey(k)) {
+                                    mMap.remove(k);
+                                    changesMade = true;
+                                }
                             } else {
-                                mMap.put(k, v);
+                                boolean isSame = false;
+                                if (mMap.containsKey(k)) {
+                                    Object existingValue = mMap.get(k);
+                                    isSame = existingValue != null && existingValue.equals(v);
+                                }
+                                if (!isSame) {
+                                    mMap.put(k, v);
+                                    changesMade = true;
+                                }
                             }
 
                             if (hasListeners) {
@@ -2878,7 +2893,7 @@
                         mModified.clear();
                     }
 
-                    returnValue = writeFileLocked();
+                    returnValue = writeFileLocked(changesMade);
                 }
 
                 if (hasListeners) {
@@ -2923,9 +2938,16 @@
             return str;
         }
 
-        private boolean writeFileLocked() {
+        private boolean writeFileLocked(boolean changesMade) {
             // Rename the current file so it may be used as a backup during the next read
             if (mFile.exists()) {
+                if (!changesMade) {
+                    // If the file already exists, but no changes were
+                    // made to the underlying map, it's wasteful to
+                    // re-write the file.  Return as if we wrote it
+                    // out.
+                    return true;
+                }
                 if (!mBackupFile.exists()) {
                     if (!mFile.renameTo(mBackupFile)) {
                         Log.e(TAG, "Couldn't rename file " + mFile
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 81b28b9..416f289 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -326,28 +326,19 @@
     /** Information you can retrieve about a particular application. */
     public static class ContentProviderHolder implements Parcelable {
         public final ProviderInfo info;
-        public final String permissionFailure;
         public IContentProvider provider;
         public boolean noReleaseNeeded;
 
         public ContentProviderHolder(ProviderInfo _info) {
             info = _info;
-            permissionFailure = null;
         }
 
-        public ContentProviderHolder(ProviderInfo _info,
-                String _permissionFailure) {
-            info = _info;
-            permissionFailure = _permissionFailure;
-        }
-        
         public int describeContents() {
             return 0;
         }
 
         public void writeToParcel(Parcel dest, int flags) {
             info.writeToParcel(dest, 0);
-            dest.writeString(permissionFailure);
             if (provider != null) {
                 dest.writeStrongBinder(provider.asBinder());
             } else {
@@ -369,7 +360,6 @@
 
         private ContentProviderHolder(Parcel source) {
             info = ProviderInfo.CREATOR.createFromParcel(source);
-            permissionFailure = source.readString();
             provider = ContentProviderNative.asInterface(
                 source.readStrongBinder());
             noReleaseNeeded = source.readInt() != 0;
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 9b9f796..dc4e9c4 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -86,6 +86,7 @@
     private String mReadPermission;
     private String mWritePermission;
     private PathPermission[] mPathPermissions;
+    private boolean mExported;
 
     private Transport mTransport = new Transport();
 
@@ -257,9 +258,9 @@
             final Context context = getContext();
             final String rperm = getReadPermission();
             final int pid = Binder.getCallingPid();
-            if (rperm == null
+            if (mExported && (rperm == null
                     || context.checkPermission(rperm, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
+                    == PackageManager.PERMISSION_GRANTED)) {
                 return;
             }
             
@@ -303,9 +304,9 @@
             final Context context = getContext();
             final String wperm = getWritePermission();
             final int pid = Binder.getCallingPid();
-            if (wperm == null
+            if (mExported && (wperm == null
                     || context.checkPermission(wperm, pid, uid)
-                    == PackageManager.PERMISSION_GRANTED) {
+                    == PackageManager.PERMISSION_GRANTED)) {
                 return true;
             }
             
@@ -786,6 +787,7 @@
                 setReadPermission(info.readPermission);
                 setWritePermission(info.writePermission);
                 setPathPermissions(info.pathPermissions);
+                mExported = info.exported;
             }
             ContentProvider.this.onCreate();
         }
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index f72de67..a0abc6c 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1138,9 +1138,9 @@
          */
         public static final String SCENE_MODE_BARCODE = "barcode";
 
-        // Values for focus mode settings.
         /**
-         * Auto-focus mode.
+         * Auto-focus mode. Applications should call {@link
+         * #autoFocus(AutoFocusCallback)} to start the focus in this mode.
          */
         public static final String FOCUS_MODE_AUTO = "auto";
 
@@ -1149,6 +1149,12 @@
          * {@link #autoFocus(AutoFocusCallback)} in this mode.
          */
         public static final String FOCUS_MODE_INFINITY = "infinity";
+
+        /**
+         * Macro (close-up) focus mode. Applications should call
+         * {@link #autoFocus(AutoFocusCallback)} to start the focus in this
+         * mode.
+         */
         public static final String FOCUS_MODE_MACRO = "macro";
 
         /**
@@ -1170,7 +1176,9 @@
          * Continuous auto focus mode. The camera continuously tries to focus.
          * This is ideal for shooting video or shooting photo of moving object.
          * Auto focus starts when the parameter is set. Applications should not
-         * call {@link #autoFocus(AutoFocusCallback)} in this mode.
+         * call {@link #autoFocus(AutoFocusCallback)} in this mode. To stop
+         * continuous focus, applications should change the focus mode to other
+         * modes.
          */
         public static final String FOCUS_MODE_CONTINUOUS = "continuous";
 
@@ -1948,6 +1956,7 @@
          * @see #FOCUS_MODE_INFINITY
          * @see #FOCUS_MODE_MACRO
          * @see #FOCUS_MODE_FIXED
+         * @see #FOCUS_MODE_EDOF
          * @see #FOCUS_MODE_CONTINUOUS
          */
         public String getFocusMode() {
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index f6d237a..d2c3eaa 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -1525,7 +1525,22 @@
      * Typically the atmospheric pressure is read from a
      * {@link Sensor#TYPE_PRESSURE} sensor. The pressure at sea level must be
      * known, usually it can be retrieved from airport databases in the
-     * vicinity.
+     * vicinity. If unknown, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE}
+     * as an approximation, but absolute altitudes won't be accurate.
+     * </p>
+     * <p>
+     * To calculate altitude differences, you must calculate the difference
+     * between the altitudes at both points. If you don't know the altitude
+     * as sea level, you can use {@link #PRESSURE_STANDARD_ATMOSPHERE} instead,
+     * which will give good results considering the range of pressure typically
+     * involved.
+     * </p>
+     * <p>
+     * <code><ul>
+     *  float altitude_difference =
+     *      getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure_at_point2)
+     *      - getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pressure_at_point1);
+     * </ul></code>
      * </p>
      *
      * @param p0 pressure at sea level
diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java
index 214510d..c34cb2f 100644
--- a/core/java/android/net/MobileDataStateTracker.java
+++ b/core/java/android/net/MobileDataStateTracker.java
@@ -53,6 +53,10 @@
     private boolean mEnabled;
     private BroadcastReceiver mStateReceiver;
 
+    // DEFAULT and HIPRI are the same connection.  If we're one of these we need to check if
+    // the other is also disconnected before we reset sockets
+    private boolean mIsDefaultOrHipri = false;
+
     /**
      * Create a new MobileDataStateTracker
      * @param context the application context of the caller
@@ -71,6 +75,10 @@
         } else {
             mApnTypeToWatchFor = mApnType;
         }
+        if (netType == ConnectivityManager.TYPE_MOBILE ||
+                netType == ConnectivityManager.TYPE_MOBILE_HIPRI) {
+            mIsDefaultOrHipri = true;
+        }
 
         mPhoneService = null;
         if(netType == ConnectivityManager.TYPE_MOBILE) {
@@ -138,6 +146,7 @@
     }
 
     private class MobileDataStateReceiver extends BroadcastReceiver {
+        ConnectivityManager mConnectivityManager;
         public void onReceive(Context context, Intent intent) {
             synchronized(this) {
                 if (intent.getAction().equals(TelephonyIntents.
@@ -190,7 +199,26 @@
                                 }
 
                                 setDetailedState(DetailedState.DISCONNECTED, reason, apnName);
-                                if (mInterfaceName != null) {
+                                boolean doReset = true;
+                                if (mIsDefaultOrHipri == true) {
+                                    // both default and hipri must go down before we reset
+                                    int typeToCheck = (Phone.APN_TYPE_DEFAULT.equals(mApnType) ?
+                                            ConnectivityManager.TYPE_MOBILE_HIPRI :
+                                            ConnectivityManager.TYPE_MOBILE);
+                                    if (mConnectivityManager == null) {
+                                        mConnectivityManager =
+                                                (ConnectivityManager)context.getSystemService(
+                                                Context.CONNECTIVITY_SERVICE);
+                                    }
+                                    if (mConnectivityManager != null) {
+                                        NetworkInfo info = mConnectivityManager.getNetworkInfo(
+                                                    typeToCheck);
+                                        if (info != null && info.isConnected() == true) {
+                                            doReset = false;
+                                        }
+                                    }
+                                }
+                                if (doReset && mInterfaceName != null) {
                                     NetworkUtils.resetConnections(mInterfaceName);
                                 }
                                 // can't do this here - ConnectivityService needs it to clear stuff
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 2ca08ea..402443c 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -990,7 +990,8 @@
                             }
                         }) : 0;
         long durationDraw =
-                (root || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(view,
+                (root || !view.willNotDraw() || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(
+                        view,
                         new ViewOperation<Object>() {
                             public Object[] pre() {
                                 final DisplayMetrics metrics =
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
index fed55dc..55d11bb 100755
--- a/core/java/android/view/WindowOrientationListener.java
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -103,11 +103,11 @@
         }
     }
 
-    public int getCurrentRotation() {
+    public int getCurrentRotation(int lastRotation) {
         if (mEnabled) {
-            return mSensorEventListener.getCurrentRotation();
+            return mSensorEventListener.getCurrentRotation(lastRotation);
         }
-        return -1;
+        return lastRotation;
     }
 
     /**
@@ -153,9 +153,15 @@
         private static final int ROTATION_270 = 2;
 
         // Mapping our internal aliases into actual Surface rotation values
-        private static final int[] SURFACE_ROTATIONS = new int[] {
+        private static final int[] INTERNAL_TO_SURFACE_ROTATION = new int[] {
             Surface.ROTATION_0, Surface.ROTATION_90, Surface.ROTATION_270};
 
+        // Mapping Surface rotation values to internal aliases.
+        // We have no constant for Surface.ROTATION_180.  That should never happen, but if it
+        // does, we'll arbitrarily choose a mapping.
+        private static final int[] SURFACE_TO_INTERNAL_ROTATION = new int[] {
+            ROTATION_0, ROTATION_90, ROTATION_90, ROTATION_270};
+
         // Threshold ranges of orientation angle to transition into other orientation states.
         // The first list is for transitions from ROTATION_0, the next for ROTATION_90, etc.
         // ROTATE_TO defines the orientation each threshold range transitions to, and must be kept
@@ -243,8 +249,12 @@
             return (float) SAMPLING_PERIOD_MS / (timeConstantMs + SAMPLING_PERIOD_MS);
         }
 
-        int getCurrentRotation() {
-            return SURFACE_ROTATIONS[mRotation];
+        int getCurrentRotation(int lastRotation) {
+            if (mTiltDistrust > 0) {
+                // we really don't know the current orientation, so trust what's currently displayed
+                mRotation = SURFACE_TO_INTERNAL_ROTATION[lastRotation];
+            }
+            return INTERNAL_TO_SURFACE_ROTATION[mRotation];
         }
 
         private void calculateNewRotation(float orientation, float tiltAngle) {
@@ -267,7 +277,7 @@
 
             if (localLOGV) Log.i(TAG, " new rotation = " + rotation);
             mRotation = rotation;
-            mOrientationListener.onOrientationChanged(getCurrentRotation());
+            mOrientationListener.onOrientationChanged(INTERNAL_TO_SURFACE_ROTATION[mRotation]);
         }
 
         private float lowpassFilter(float newValue, float oldValue, float alpha) {
@@ -306,7 +316,8 @@
             mTiltAngle = lowpassFilter(newTiltAngle, mTiltAngle, alpha);
 
             float absoluteTilt = Math.abs(mTiltAngle);
-            if (checkFullyTilted(absoluteTilt)) {
+            checkFullyTilted(absoluteTilt);
+            if (mTiltDistrust > 0) {
                 return; // when fully tilted, ignore orientation entirely
             }
 
@@ -347,11 +358,9 @@
          * get un-tilted.
          *
          * @param absoluteTilt the absolute value of the current tilt angle
-         * @return true if the phone is fully tilted
          */
-        private boolean checkFullyTilted(float absoluteTilt) {
-            boolean fullyTilted = absoluteTilt > MAX_TILT;
-            if (fullyTilted) {
+        private void checkFullyTilted(float absoluteTilt) {
+            if (absoluteTilt > MAX_TILT) {
                 if (mRotation == ROTATION_0) {
                     mOrientationAngle = 0;
                 } else if (mRotation == ROTATION_90) {
@@ -366,7 +375,6 @@
             } else if (mTiltDistrust > 0) {
                 mTiltDistrust--;
             }
-            return fullyTilted;
         }
 
         /**
@@ -389,8 +397,8 @@
          */
         private void filterOrientation(float absoluteTilt, float orientationAngle) {
             float alpha = DEFAULT_LOWPASS_ALPHA;
-            if (mTiltDistrust > 0 || mAccelerationDistrust > 1) {
-                // when fully tilted, or under more than a transient acceleration, distrust heavily
+            if (mAccelerationDistrust > 1) {
+                // when under more than a transient acceleration, distrust heavily
                 alpha = ACCELERATING_LOWPASS_ALPHA;
             } else if (absoluteTilt > PARTIAL_TILT || mAccelerationDistrust == 1) {
                 // when tilted partway, or under transient acceleration, distrust lightly
diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp
index 204bb74..578de6f 100644
--- a/core/jni/android/graphics/Graphics.cpp
+++ b/core/jni/android/graphics/Graphics.cpp
@@ -518,35 +518,48 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM)
-    : fEnv(env), fReportSizeToVM(reportSizeToVM) {}
+    : fReportSizeToVM(reportSizeToVM) {
+    if (env->GetJavaVM(&fVM) != JNI_OK) {
+        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
+        sk_throw();
+    }
+}
     
 bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
-    return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM);
+    JNIEnv* env = vm2env(fVM);
+    return GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, fReportSizeToVM);
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 
 JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
-    : fEnv(env), fTotalSize(0) {}
+    : fTotalSize(0) {
+    if (env->GetJavaVM(&fVM) != JNI_OK) {
+        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
+        sk_throw();
+    }
+}
 
 JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
+    JNIEnv* env = vm2env(fVM);
     jlong jtotalSize = fTotalSize;
-    fEnv->CallVoidMethod(gVMRuntime_singleton,
+    env->CallVoidMethod(gVMRuntime_singleton,
             gVMRuntime_trackExternalFreeMethodID,
             jtotalSize);
 }
 
 bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
     jlong jsize = memorySize;  // the VM wants longs for the size
-    bool r = fEnv->CallBooleanMethod(gVMRuntime_singleton,
+    JNIEnv* env = vm2env(fVM);
+    bool r = env->CallBooleanMethod(gVMRuntime_singleton,
             gVMRuntime_trackExternalAllocationMethodID,
             jsize);
-    if (GraphicsJNI::hasException(fEnv)) {
+    if (GraphicsJNI::hasException(env)) {
         return false;
     }
     if (!r) {
         LOGE("VM won't let us allocate %zd bytes\n", memorySize);
-        doThrowOOME(fEnv, "bitmap size exceeds VM budget");
+        doThrowOOME(env, "bitmap size exceeds VM budget");
         return false;
     }
     fTotalSize += memorySize;
diff --git a/core/jni/android/graphics/GraphicsJNI.h b/core/jni/android/graphics/GraphicsJNI.h
index 8d6528b..1a43a3e 100644
--- a/core/jni/android/graphics/GraphicsJNI.h
+++ b/core/jni/android/graphics/GraphicsJNI.h
@@ -80,7 +80,7 @@
     virtual bool allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable);
     
 private:
-    JNIEnv* fEnv;
+    JavaVM* fVM;
     bool fReportSizeToVM;
 };
 
@@ -92,7 +92,7 @@
     virtual bool reportMemory(size_t memorySize);
 
 private:
-    JNIEnv* fEnv;
+    JavaVM* fVM;
     size_t fTotalSize;
 };
 
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index 6a5d254..99a3115 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -337,11 +337,14 @@
     static const char PIXEL_FORMAT_JPEG[];
 
     // Values for focus mode settings.
-    // Auto-focus mode.
+    // Auto-focus mode. Applications should call
+    // CameraHardwareInterface.autoFocus to start the focus in this mode.
     static const char FOCUS_MODE_AUTO[];
     // Focus is set at infinity. Applications should not call
     // CameraHardwareInterface.autoFocus in this mode.
     static const char FOCUS_MODE_INFINITY[];
+    // Macro (close-up) focus mode. Applications should call
+    // CameraHardwareInterface.autoFocus to start the focus in this mode.
     static const char FOCUS_MODE_MACRO[];
     // Focus is fixed. The camera is always in this mode if the focus is not
     // adjustable. If the camera has auto-focus, this mode can fix the
@@ -355,7 +358,8 @@
     // Continuous auto focus mode. The camera continuously tries to focus. This
     // is ideal for shooting video or shooting photo of moving object. Auto
     // focus starts when the parameter is set. Applications should not call
-    // CameraHardwareInterface.autoFocus in this mode.
+    // CameraHardwareInterface.autoFocus in this mode.  To stop continuous
+    // focus, applications should change the focus mode to other modes.
     static const char FOCUS_MODE_CONTINUOUS[];
 
     // The camera determines the exposure by giving more weight to the
diff --git a/include/ui/EventHub.h b/include/ui/EventHub.h
index 3132941..3d42856 100644
--- a/include/ui/EventHub.h
+++ b/include/ui/EventHub.h
@@ -224,6 +224,7 @@
         uint8_t*        keyBitmask;
         KeyLayoutMap*   layoutMap;
         String8         keylayoutFilename;
+        int             fd;
         device_t*       next;
         
         device_t(int32_t _id, const char* _path, const char* name);
diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h
index 2505cb0..aed4fa1 100644
--- a/include/ui/InputDispatcher.h
+++ b/include/ui/InputDispatcher.h
@@ -159,6 +159,12 @@
     virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid,
             Vector<InputTarget>& outTargets) = 0;
+
+    /* Gets the maximum suggested event delivery rate per second.
+     * This value is used to throttle motion event movement actions on a per-device
+     * basis.  It is not intended to be a hard limit.
+     */
+    virtual int32_t getMaxEventsPerSecond() = 0;
 };
 
 
@@ -332,6 +338,8 @@
         // Linked list of motion samples associated with this motion event.
         MotionSample firstSample;
         MotionSample* lastSample;
+
+        uint32_t countSamples() const;
     };
 
     // Tracks the progress of dispatching a particular event to a particular connection.
@@ -587,6 +595,17 @@
     Condition mInjectionSyncFinishedCondition;
     void decrementPendingSyncDispatchesLocked(EventEntry* entry);
 
+    // Throttling state.
+    struct ThrottleState {
+        nsecs_t minTimeBetweenEvents;
+
+        nsecs_t lastEventTime;
+        int32_t lastDeviceId;
+        uint32_t lastSource;
+
+        uint32_t originalSampleCount; // only collected during debugging
+    } mThrottleState;
+
     // Key repeat tracking.
     // XXX Move this up to the input reader instead.
     struct KeyRepeatState {
diff --git a/include/ui/InputReader.h b/include/ui/InputReader.h
index 71c6c51..56d2765 100644
--- a/include/ui/InputReader.h
+++ b/include/ui/InputReader.h
@@ -480,10 +480,6 @@
         inline void clear() {
             fields = 0;
         }
-
-        inline bool isDirty() {
-            return fields != 0;
-        }
     } mAccumulator;
 
     float mXScale;
@@ -702,7 +698,7 @@
         } historyData[AVERAGING_HISTORY_SIZE];
     } mAveragingTouchFilter;
 
-    struct JumpTouchFilterState {
+    struct JumpyTouchFilterState {
         uint32_t jumpyPointsDropped;
     } mJumpyTouchFilter;
 
@@ -765,10 +761,6 @@
         inline void clear() {
             fields = 0;
         }
-
-        inline bool isDirty() {
-            return fields != 0;
-        }
     } mAccumulator;
 
     bool mDown;
@@ -804,7 +796,8 @@
             FIELD_ABS_MT_WIDTH_MAJOR = 16,
             FIELD_ABS_MT_WIDTH_MINOR = 32,
             FIELD_ABS_MT_ORIENTATION = 64,
-            FIELD_ABS_MT_TRACKING_ID = 128
+            FIELD_ABS_MT_TRACKING_ID = 128,
+            FIELD_ABS_MT_PRESSURE = 256,
         };
 
         uint32_t pointerCount;
@@ -819,6 +812,7 @@
             int32_t absMTWidthMinor;
             int32_t absMTOrientation;
             int32_t absMTTrackingId;
+            int32_t absMTPressure;
 
             inline void clear() {
                 fields = 0;
@@ -829,10 +823,6 @@
             pointerCount = 0;
             pointers[0].clear();
         }
-
-        inline bool isDirty() {
-            return pointerCount != 0;
-        }
     } mAccumulator;
 
     void initialize();
diff --git a/libs/ui/EventHub.cpp b/libs/ui/EventHub.cpp
index 33393fe..891661d 100644
--- a/libs/ui/EventHub.cpp
+++ b/libs/ui/EventHub.cpp
@@ -60,7 +60,6 @@
 #define ID_MASK  0x0000ffff
 #define SEQ_MASK 0x7fff0000
 #define SEQ_SHIFT 16
-#define id_to_index(id)         ((id&ID_MASK)+1)
 
 #ifndef ABS_MT_TOUCH_MAJOR
 #define ABS_MT_TOUCH_MAJOR      0x30    /* Major axis of touching ellipse */
@@ -87,7 +86,7 @@
 
 EventHub::device_t::device_t(int32_t _id, const char* _path, const char* name)
     : id(_id), path(_path), name(name), classes(0)
-    , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), next(NULL) {
+    , keyBitmask(NULL), layoutMap(new KeyLayoutMap()), fd(-1), next(NULL) {
 }
 
 EventHub::device_t::~device_t() {
@@ -152,9 +151,9 @@
 
     struct input_absinfo info;
 
-    if(ioctl(mFDs[id_to_index(device->id)].fd, EVIOCGABS(axis), &info)) {
+    if(ioctl(device->fd, EVIOCGABS(axis), &info)) {
         LOGW("Error reading absolute controller %d for device %s fd %d\n",
-             axis, device->name.string(), mFDs[id_to_index(device->id)].fd);
+             axis, device->name.string(), device->fd);
         return -errno;
     }
 
@@ -183,7 +182,7 @@
 int32_t EventHub::getScanCodeStateLocked(device_t* device, int32_t scanCode) const {
     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
     memset(key_bitmask, 0, sizeof(key_bitmask));
-    if (ioctl(mFDs[id_to_index(device->id)].fd,
+    if (ioctl(device->fd,
                EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
         return test_bit(scanCode, key_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
     }
@@ -206,8 +205,7 @@
 
     uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)];
     memset(key_bitmask, 0, sizeof(key_bitmask));
-    if (ioctl(mFDs[id_to_index(device->id)].fd,
-               EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
+    if (ioctl(device->fd, EVIOCGKEY(sizeof(key_bitmask)), key_bitmask) >= 0) {
         #if 0
         for (size_t i=0; i<=KEY_MAX; i++) {
             LOGI("(Scan code %d: down=%d)", i, test_bit(i, key_bitmask));
@@ -243,7 +241,7 @@
 int32_t EventHub::getSwitchStateLocked(device_t* device, int32_t sw) const {
     uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)];
     memset(sw_bitmask, 0, sizeof(sw_bitmask));
-    if (ioctl(mFDs[id_to_index(device->id)].fd,
+    if (ioctl(device->fd,
                EVIOCGSW(sizeof(sw_bitmask)), sw_bitmask) >= 0) {
         return test_bit(sw, sw_bitmask) ? AKEY_STATE_DOWN : AKEY_STATE_UP;
     }
@@ -583,7 +581,6 @@
         if (strcmp(name, test) == 0) {
             LOGI("ignoring event id %s driver %s\n", deviceName, test);
             close(fd);
-            fd = -1;
             return -1;
         }
     }
@@ -657,6 +654,7 @@
         return -1;
     }
 
+    device->fd = fd;
     mFDs[mFDCount].fd = fd;
     mFDs[mFDCount].events = POLLIN;
     mFDs[mFDCount].revents = 0;
@@ -814,6 +812,14 @@
                 device->id, name, propName, keylayoutFilename);
     }
 
+    // If the device isn't recognized as something we handle, don't monitor it.
+    if (device->classes == 0) {
+        LOGV("Dropping device %s %p, id = %d\n", deviceName, device, devid);
+        close(fd);
+        delete device;
+        return -1;
+    }
+
     LOGI("New device: path=%s name=%s id=0x%x (of 0x%x) index=%d fd=%d classes=0x%x\n",
          deviceName, name, device->id, mNumDevicesById, mFDCount, fd, device->classes);
          
diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp
index 13030b5..ce616a4 100644
--- a/libs/ui/InputDispatcher.cpp
+++ b/libs/ui/InputDispatcher.cpp
@@ -28,6 +28,9 @@
 // Log debug messages about input event injection.
 #define DEBUG_INJECTION 0
 
+// Log debug messages about input event throttling.
+#define DEBUG_THROTTLING 0
+
 #include <cutils/log.h>
 #include <ui/InputDispatcher.h>
 
@@ -66,6 +69,15 @@
 
     mKeyRepeatState.lastKeyEntry = NULL;
 
+    int32_t maxEventsPerSecond = policy->getMaxEventsPerSecond();
+    mThrottleState.minTimeBetweenEvents = 1000000000LL / maxEventsPerSecond;
+    mThrottleState.lastDeviceId = -1;
+
+#if DEBUG_THROTTLING
+    mThrottleState.originalSampleCount = 0;
+    LOGD("Throttling - Max events per second = %d", maxEventsPerSecond);
+#endif
+
     mCurrentInputTargetsValid = false;
 }
 
@@ -144,12 +156,60 @@
                 }
             } else {
                 // Inbound queue has at least one entry.
-                // Start processing it but leave it on the queue until later so that the
+                EventEntry* entry = mInboundQueue.head.next;
+
+                // Consider throttling the entry if it is a move event and there are no
+                // other events behind it in the queue.  Due to movement batching, additional
+                // samples may be appended to this event by the time the throttling timeout
+                // expires.
+                // TODO Make this smarter and consider throttling per device independently.
+                if (entry->type == EventEntry::TYPE_MOTION) {
+                    MotionEntry* motionEntry = static_cast<MotionEntry*>(entry);
+                    int32_t deviceId = motionEntry->deviceId;
+                    uint32_t source = motionEntry->source;
+                    if (motionEntry->next == & mInboundQueue.tail
+                            && motionEntry->action == AMOTION_EVENT_ACTION_MOVE
+                            && deviceId == mThrottleState.lastDeviceId
+                            && source == mThrottleState.lastSource) {
+                        nsecs_t nextTime = mThrottleState.lastEventTime
+                                + mThrottleState.minTimeBetweenEvents;
+                        if (currentTime < nextTime) {
+                            // Throttle it!
+#if DEBUG_THROTTLING
+                            LOGD("Throttling - Delaying motion event for "
+                                    "device 0x%x, source 0x%08x by up to %0.3fms.",
+                                    deviceId, source, (nextTime - currentTime) * 0.000001);
+#endif
+                            if (nextTime < nextWakeupTime) {
+                                nextWakeupTime = nextTime;
+                            }
+                            if (mThrottleState.originalSampleCount == 0) {
+                                mThrottleState.originalSampleCount =
+                                        motionEntry->countSamples();
+                            }
+                            goto Throttle;
+                        }
+                    }
+
+#if DEBUG_THROTTLING
+                    if (mThrottleState.originalSampleCount != 0) {
+                        uint32_t count = motionEntry->countSamples();
+                        LOGD("Throttling - Motion event sample count grew by %d from %d to %d.",
+                                count - mThrottleState.originalSampleCount,
+                                mThrottleState.originalSampleCount, count);
+                        mThrottleState.originalSampleCount = 0;
+                    }
+#endif
+
+                    mThrottleState.lastEventTime = currentTime;
+                    mThrottleState.lastDeviceId = deviceId;
+                    mThrottleState.lastSource = source;
+                }
+
+                // Start processing the entry but leave it on the queue until later so that the
                 // input reader can keep appending samples onto a motion event between the
                 // time we started processing it and the time we finally enqueue dispatch
                 // entries for it.
-                EventEntry* entry = mInboundQueue.head.next;
-
                 switch (entry->type) {
                 case EventEntry::TYPE_CONFIGURATION_CHANGED: {
                     ConfigurationChangedEntry* typedEntry =
@@ -179,6 +239,8 @@
                 mInboundQueue.dequeue(entry);
                 mAllocator.releaseEventEntry(entry);
                 skipPoll = true;
+
+            Throttle: ;
             }
         }
 
@@ -192,8 +254,8 @@
         return;
     }
 
-    // Wait for callback or timeout or wake.
-    nsecs_t timeout = nanoseconds_to_milliseconds(nextWakeupTime - currentTime);
+    // Wait for callback or timeout or wake.  (make sure we round up, not down)
+    nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL;
     int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0;
     mPollLoop->pollOnce(timeoutMillis);
 }
@@ -1708,6 +1770,16 @@
     motionEntry->lastSample = sample;
 }
 
+// --- InputDispatcher::MotionEntry ---
+
+uint32_t InputDispatcher::MotionEntry::countSamples() const {
+    uint32_t count = 1;
+    for (MotionSample* sample = firstSample.next; sample != NULL; sample = sample->next) {
+        count += 1;
+    }
+    return count;
+}
+
 // --- InputDispatcher::Connection ---
 
 InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) :
diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp
index 5f5a4ac..6f042ec 100644
--- a/libs/ui/InputReader.cpp
+++ b/libs/ui/InputReader.cpp
@@ -945,7 +945,6 @@
         mAccumulator.fields = Accumulator::FIELD_BTN_MOUSE;
         mAccumulator.btnMouse = false;
         sync(when);
-        mAccumulator.clear();
     }
 
     InputMapper::reset();
@@ -958,9 +957,9 @@
         case BTN_MOUSE:
             mAccumulator.fields |= Accumulator::FIELD_BTN_MOUSE;
             mAccumulator.btnMouse = rawEvent->value != 0;
-
+            // Sync now since BTN_MOUSE is not necessarily followed by SYN_REPORT and
+            // we need to ensure that we report the up/down promptly.
             sync(rawEvent->when);
-            mAccumulator.clear();
             break;
         }
         break;
@@ -981,10 +980,7 @@
     case EV_SYN:
         switch (rawEvent->scanCode) {
         case SYN_REPORT:
-            if (mAccumulator.isDirty()) {
-                sync(rawEvent->when);
-                mAccumulator.clear();
-            }
+            sync(rawEvent->when);
             break;
         }
         break;
@@ -992,13 +988,17 @@
 }
 
 void TrackballInputMapper::sync(nsecs_t when) {
+    uint32_t fields = mAccumulator.fields;
+    if (fields == 0) {
+        return; // no new state changes, so nothing to do
+    }
+
     int motionEventAction;
     PointerCoords pointerCoords;
     nsecs_t downTime;
     { // acquire lock
         AutoMutex _l(mLock);
 
-        uint32_t fields = mAccumulator.fields;
         bool downChanged = fields & Accumulator::FIELD_BTN_MOUSE;
 
         if (downChanged) {
@@ -1061,6 +1061,8 @@
     } // release lock
 
     applyPolicyAndDispatch(when, motionEventAction, & pointerCoords, downTime);
+
+    mAccumulator.clear();
 }
 
 void TrackballInputMapper::applyPolicyAndDispatch(nsecs_t when, int32_t motionEventAction,
@@ -2380,8 +2382,8 @@
     mDown = false;
     mX = 0;
     mY = 0;
-    mPressure = 0;
-    mSize = 0;
+    mPressure = 1; // default to 1 for devices that don't report pressure
+    mSize = 0; // default to 0 for devices that don't report size
 }
 
 void SingleTouchInputMapper::reset() {
@@ -2397,9 +2399,9 @@
         case BTN_TOUCH:
             mAccumulator.fields |= Accumulator::FIELD_BTN_TOUCH;
             mAccumulator.btnTouch = rawEvent->value != 0;
-
-            sync(rawEvent->when);
-            mAccumulator.clear();
+            // Don't sync immediately.  Wait until the next SYN_REPORT since we might
+            // not have received valid position information yet.  This logic assumes that
+            // BTN_TOUCH is always followed by SYN_REPORT as part of a complete packet.
             break;
         }
         break;
@@ -2428,10 +2430,7 @@
     case EV_SYN:
         switch (rawEvent->scanCode) {
         case SYN_REPORT:
-            if (mAccumulator.isDirty()) {
-                sync(rawEvent->when);
-                mAccumulator.clear();
-            }
+            sync(rawEvent->when);
             break;
         }
         break;
@@ -2439,9 +2438,10 @@
 }
 
 void SingleTouchInputMapper::sync(nsecs_t when) {
-    /* Update device state */
-
     uint32_t fields = mAccumulator.fields;
+    if (fields == 0) {
+        return; // no new state changes, so nothing to do
+    }
 
     if (fields & Accumulator::FIELD_BTN_TOUCH) {
         mDown = mAccumulator.btnTouch;
@@ -2472,8 +2472,8 @@
         mCurrentTouch.pointers[0].y = mY;
         mCurrentTouch.pointers[0].pressure = mPressure;
         mCurrentTouch.pointers[0].size = mSize;
-        mCurrentTouch.pointers[0].touchMajor = mPressure;
-        mCurrentTouch.pointers[0].touchMinor = mPressure;
+        mCurrentTouch.pointers[0].touchMajor = mSize;
+        mCurrentTouch.pointers[0].touchMinor = mSize;
         mCurrentTouch.pointers[0].toolMajor = mSize;
         mCurrentTouch.pointers[0].toolMinor = mSize;
         mCurrentTouch.pointers[0].orientation = 0;
@@ -2482,6 +2482,8 @@
     }
 
     syncTouch(when, true);
+
+    mAccumulator.clear();
 }
 
 void SingleTouchInputMapper::configureAxes() {
@@ -2494,8 +2496,8 @@
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_PRESSURE, & mAxes.pressure);
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_TOOL_WIDTH, & mAxes.size);
 
-    mAxes.touchMajor = mAxes.pressure;
-    mAxes.touchMinor = mAxes.pressure;
+    mAxes.touchMajor = mAxes.size;
+    mAxes.touchMinor = mAxes.size;
     mAxes.toolMajor = mAxes.size;
     mAxes.toolMinor = mAxes.size;
 }
@@ -2585,10 +2587,7 @@
         }
 
         case SYN_REPORT:
-            if (mAccumulator.isDirty()) {
-                sync(rawEvent->when);
-                mAccumulator.clear();
-            }
+            sync(rawEvent->when);
             break;
         }
         break;
@@ -2598,11 +2597,7 @@
 void MultiTouchInputMapper::sync(nsecs_t when) {
     static const uint32_t REQUIRED_FIELDS =
             Accumulator::FIELD_ABS_MT_POSITION_X
-            | Accumulator::FIELD_ABS_MT_POSITION_Y
-            | Accumulator::FIELD_ABS_MT_TOUCH_MAJOR
-            | Accumulator::FIELD_ABS_MT_WIDTH_MAJOR;
-
-    /* Update device state */
+            | Accumulator::FIELD_ABS_MT_POSITION_Y;
 
     uint32_t inCount = mAccumulator.pointerCount;
     uint32_t outCount = 0;
@@ -2611,53 +2606,76 @@
     mCurrentTouch.clear();
 
     for (uint32_t inIndex = 0; inIndex < inCount; inIndex++) {
-        uint32_t fields = mAccumulator.pointers[inIndex].fields;
+        const Accumulator::Pointer& inPointer = mAccumulator.pointers[inIndex];
+        uint32_t fields = inPointer.fields;
 
         if ((fields & REQUIRED_FIELDS) != REQUIRED_FIELDS) {
-#if DEBUG_POINTERS
-            LOGD("Pointers: Missing required multitouch pointer fields: index=%d, fields=%d",
-                    inIndex, fields);
-            continue;
-#endif
-        }
-
-        if (mAccumulator.pointers[inIndex].absMTTouchMajor <= 0) {
-            // Pointer is not down.  Drop it.
+            // Some drivers send empty MT sync packets without X / Y to indicate a pointer up.
+            // Drop this finger.
             continue;
         }
 
-        mCurrentTouch.pointers[outCount].x = mAccumulator.pointers[inIndex].absMTPositionX;
-        mCurrentTouch.pointers[outCount].y = mAccumulator.pointers[inIndex].absMTPositionY;
+        PointerData& outPointer = mCurrentTouch.pointers[outCount];
+        outPointer.x = inPointer.absMTPositionX;
+        outPointer.y = inPointer.absMTPositionY;
 
-        mCurrentTouch.pointers[outCount].touchMajor =
-                mAccumulator.pointers[inIndex].absMTTouchMajor;
-        mCurrentTouch.pointers[outCount].touchMinor =
-                (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) != 0
-                ? mAccumulator.pointers[inIndex].absMTTouchMinor
-                        : mAccumulator.pointers[inIndex].absMTTouchMajor;
+        if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MAJOR) {
+            int32_t value = inPointer.absMTTouchMajor;
+            if (value <= 0) {
+                // Some devices send sync packets with X / Y but with a 0 touch major to indicate
+                // a pointer up.  Drop this finger.
+                continue;
+            }
+            outPointer.touchMajor = inPointer.absMTTouchMajor;
+        } else {
+            outPointer.touchMajor = 0;
+        }
 
-        mCurrentTouch.pointers[outCount].toolMajor =
-                mAccumulator.pointers[inIndex].absMTWidthMajor;
-        mCurrentTouch.pointers[outCount].toolMinor =
-                (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) != 0
-                ? mAccumulator.pointers[inIndex].absMTWidthMinor
-                        : mAccumulator.pointers[inIndex].absMTWidthMajor;
+        if (fields & Accumulator::FIELD_ABS_MT_TOUCH_MINOR) {
+            outPointer.touchMinor = inPointer.absMTTouchMinor;
+        } else {
+            outPointer.touchMinor = outPointer.touchMajor;
+        }
 
-        mCurrentTouch.pointers[outCount].orientation =
-                (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) != 0
-                ? mAccumulator.pointers[inIndex].absMTOrientation : 0;
+        if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MAJOR) {
+            outPointer.toolMajor = inPointer.absMTWidthMajor;
+        } else {
+            outPointer.toolMajor = outPointer.touchMajor;
+        }
 
-        // Derive an approximation of pressure and size.
-        // FIXME assignment of pressure may be incorrect, probably better to let
-        // pressure = touch / width.  Later on we pass width to MotionEvent as a size, which
-        // isn't quite right either.  Should be using touch for that.
-        mCurrentTouch.pointers[outCount].pressure = mAccumulator.pointers[inIndex].absMTTouchMajor;
-        mCurrentTouch.pointers[outCount].size = mAccumulator.pointers[inIndex].absMTWidthMajor;
+        if (fields & Accumulator::FIELD_ABS_MT_WIDTH_MINOR) {
+            outPointer.toolMinor = inPointer.absMTWidthMinor;
+        } else {
+            outPointer.toolMinor = outPointer.toolMajor;
+        }
+
+        if (fields & Accumulator::FIELD_ABS_MT_ORIENTATION) {
+            outPointer.orientation = inPointer.absMTOrientation;
+        } else {
+            outPointer.orientation = 0;
+        }
+
+        if (fields & Accumulator::FIELD_ABS_MT_PRESSURE) {
+            outPointer.pressure = inPointer.absMTPressure;
+        } else {
+            // Derive an approximation of pressure.
+            // FIXME Traditionally we have just passed a normalized value based on
+            //       ABS_MT_TOUCH_MAJOR as an estimate of pressure but the result is not
+            //       very meaningful, particularly on large displays.  We should probably let
+            //       pressure = touch_major / tool_major but it is unclear whether that will
+            //       break applications.
+            outPointer.pressure = outPointer.touchMajor;
+        }
+
+        // Size is an alias for a normalized tool width.
+        // FIXME Normalized tool width doesn't actually make much sense since it literally
+        //       means the approaching contact major axis is divided by its full range as
+        //       reported by the driver.  On a large display this could produce very small values.
+        outPointer.size = outPointer.toolMajor;
 
         if (havePointerIds) {
-            if (fields & Accumulator::
-                    FIELD_ABS_MT_TRACKING_ID) {
-                uint32_t id = uint32_t(mAccumulator.pointers[inIndex].absMTTrackingId);
+            if (fields & Accumulator::FIELD_ABS_MT_TRACKING_ID) {
+                uint32_t id = uint32_t(inPointer.absMTTrackingId);
 
                 if (id > MAX_POINTER_ID) {
 #if DEBUG_POINTERS
@@ -2668,7 +2686,7 @@
                     havePointerIds = false;
                 }
                 else {
-                    mCurrentTouch.pointers[outCount].id = id;
+                    outPointer.id = id;
                     mCurrentTouch.idToIndex[id] = outCount;
                     mCurrentTouch.idBits.markBit(id);
                 }
@@ -2683,6 +2701,8 @@
     mCurrentTouch.pointerCount = outCount;
 
     syncTouch(when, havePointerIds);
+
+    mAccumulator.clear();
 }
 
 void MultiTouchInputMapper::configureAxes() {
@@ -2697,6 +2717,7 @@
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MAJOR, & mAxes.toolMajor);
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_WIDTH_MINOR, & mAxes.toolMinor);
     getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_ORIENTATION, & mAxes.orientation);
+    getEventHub()->getAbsoluteAxisInfo(getDeviceId(), ABS_MT_PRESSURE, & mAxes.pressure);
 
     if (! mAxes.touchMinor.valid) {
         mAxes.touchMinor = mAxes.touchMajor;
@@ -2706,7 +2727,10 @@
         mAxes.toolMinor = mAxes.toolMajor;
     }
 
-    mAxes.pressure = mAxes.touchMajor;
+    if (! mAxes.pressure.valid) {
+        mAxes.pressure = mAxes.touchMajor;
+    }
+
     mAxes.size = mAxes.toolMajor;
 }
 
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 6af3a7f..12a1e6e 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -1273,6 +1273,14 @@
     hexdump(csd, csd_size);
 #endif
 
+    if (csd_size == 0) {
+        // There's no further information, i.e. no codec specific data
+        // Let's assume that the information provided in the mpeg4 headers
+        // is accurate and hope for the best.
+
+        return OK;
+    }
+
     if (csd_size < 2) {
         return ERROR_MALFORMED;
     }
diff --git a/media/libstagefright/OggExtractor.cpp b/media/libstagefright/OggExtractor.cpp
index b699d8f..9630092 100644
--- a/media/libstagefright/OggExtractor.cpp
+++ b/media/libstagefright/OggExtractor.cpp
@@ -158,7 +158,7 @@
     ReadOptions::SeekMode mode;
     if (options && options->getSeekTo(&seekTimeUs, &mode)) {
         off_t pos = seekTimeUs * mExtractor->mImpl->approxBitrate() / 8000000ll;
-        LOGI("seeking to offset %ld", pos);
+        LOGV("seeking to offset %ld", pos);
 
         if (mExtractor->mImpl->seekToOffset(pos) != OK) {
             return ERROR_END_OF_STREAM;
@@ -267,7 +267,7 @@
     uint8_t header[27];
     if (mSource->readAt(offset, header, sizeof(header))
             < (ssize_t)sizeof(header)) {
-        LOGE("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
+        LOGV("failed to read %d bytes at offset 0x%08lx", sizeof(header), offset);
 
         return ERROR_IO;
     }
@@ -384,7 +384,7 @@
                     packetSize);
 
             if (n < (ssize_t)packetSize) {
-                LOGE("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
+                LOGV("failed to read %d bytes at 0x%08lx", packetSize, dataOffset);
                 return ERROR_IO;
             }
 
@@ -418,7 +418,7 @@
                 buffer = NULL;
             }
 
-            LOGE("readPage returned %ld", n);
+            LOGV("readPage returned %ld", n);
 
             return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
         }
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 224b4bf..b2d697b3 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -18,6 +18,8 @@
 
 #include "ASessionDescription.h"
 
+#include <ctype.h>
+
 #include <media/stagefright/foundation/ABuffer.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AMessage.h>
@@ -37,6 +39,10 @@
     size_t keyLen = strlen(key);
 
     for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
         const char *colonPos = strchr(s, ';');
 
         size_t len =
@@ -253,7 +259,11 @@
         mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
 
         int32_t width, height;
-        sessionDesc->getDimensions(index, PT, &width, &height);
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            // TODO: extract dimensions from sequence parameter set.
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
 
         mFormat->setInt32(kKeyWidth, width);
         mFormat->setInt32(kKeyHeight, height);
@@ -271,7 +281,10 @@
         mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
 
         int32_t width, height;
-        sessionDesc->getDimensions(index, PT, &width, &height);
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
 
         mFormat->setInt32(kKeyWidth, width);
         mFormat->setInt32(kKeyHeight, height);
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index e9162c0..5f8f5fd 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -136,6 +136,20 @@
     return true;
 }
 
+static void MakeSocketBlocking(int s, bool blocking) {
+    // Make socket non-blocking.
+    int flags = fcntl(s, F_GETFL, 0);
+    CHECK_NE(flags, -1);
+
+    if (blocking) {
+        flags &= ~O_NONBLOCK;
+    } else {
+        flags |= O_NONBLOCK;
+    }
+
+    CHECK_NE(fcntl(s, F_SETFL, flags), -1);
+}
+
 void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
     ++mConnectionID;
 
@@ -150,10 +164,7 @@
 
     mSocket = socket(AF_INET, SOCK_STREAM, 0);
 
-    // Make socket non-blocking.
-    int flags = fcntl(mSocket, F_GETFL, 0);
-    CHECK_NE(flags, -1);
-    CHECK_NE(fcntl(mSocket, F_SETFL, flags | O_NONBLOCK), -1);
+    MakeSocketBlocking(mSocket, false);
 
     AString url;
     CHECK(msg->findString("url", &url));
@@ -210,7 +221,7 @@
         mSocket = -1;
 
         flushPendingRequests();
-    } 
+    }
 
     sp<AMessage> reply;
     CHECK(msg->findMessage("reply", &reply));
@@ -347,7 +358,13 @@
     CHECK_GE(res, 0);
 
     if (res == 1) {
-        if (!receiveRTSPReponse()) {
+        MakeSocketBlocking(mSocket, true);
+
+        bool success = receiveRTSPReponse();
+
+        MakeSocketBlocking(mSocket, false);
+
+        if (!success) {
             // Something horrible, irreparable has happened.
             flushPendingRequests();
             return;
diff --git a/media/libstagefright/rtsp/ASessionDescription.cpp b/media/libstagefright/rtsp/ASessionDescription.cpp
index ad813cd..4ea7fda 100644
--- a/media/libstagefright/rtsp/ASessionDescription.cpp
+++ b/media/libstagefright/rtsp/ASessionDescription.cpp
@@ -203,13 +203,18 @@
     }
 }
 
-void ASessionDescription::getDimensions(
+bool ASessionDescription::getDimensions(
         size_t index, unsigned long PT,
         int32_t *width, int32_t *height) const {
+    *width = 0;
+    *height = 0;
+
     char key[20];
     sprintf(key, "a=framesize:%lu", PT);
     AString value;
-    CHECK(findAttribute(index, key, &value));
+    if (!findAttribute(index, key, &value)) {
+        return false;
+    }
 
     const char *s = value.c_str();
     char *end;
@@ -221,6 +226,8 @@
     *height = strtoul(s, &end, 10);
     CHECK_GT(end, s);
     CHECK_EQ(*end, '\0');
+
+    return true;
 }
 
 bool ASessionDescription::getDurationUs(int64_t *durationUs) const {
diff --git a/media/libstagefright/rtsp/ASessionDescription.h b/media/libstagefright/rtsp/ASessionDescription.h
index b26980f..a3fa79e 100644
--- a/media/libstagefright/rtsp/ASessionDescription.h
+++ b/media/libstagefright/rtsp/ASessionDescription.h
@@ -44,7 +44,7 @@
             size_t index, unsigned long *PT,
             AString *desc, AString *params) const;
 
-    void getDimensions(
+    bool getDimensions(
             size_t index, unsigned long PT,
             int32_t *width, int32_t *height) const;
 
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index f21c8dc..b19ad48 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -309,6 +309,16 @@
                 size_t trackIndex;
                 CHECK(msg->findSize("track-index", &trackIndex));
 
+                int32_t eos;
+                if (msg->findInt32("eos", &eos)) {
+                    LOG(INFO) << "received BYE on track index " << trackIndex;
+#if 0
+                    TrackInfo *track = &mTracks.editItemAt(trackIndex);
+                    track->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
+#endif
+                    return;
+                }
+
                 sp<RefBase> obj;
                 CHECK(msg->findObject("access-unit", &obj));
 
diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp
index 5bbe441..9e25681 100644
--- a/opengl/libagl/egl.cpp
+++ b/opengl/libagl/egl.cpp
@@ -576,41 +576,44 @@
     buffer = 0;
 
     // dequeue a new buffer
-    nativeWindow->dequeueBuffer(nativeWindow, &buffer);
-    
-    // TODO: lockBuffer should rather be executed when the very first
-    // direct rendering occurs.
-    nativeWindow->lockBuffer(nativeWindow, buffer);
-    
-    // reallocate the depth-buffer if needed
-    if ((width != buffer->width) || (height != buffer->height)) {
-        // TODO: we probably should reset the swap rect here
-        // if the window size has changed
-        width = buffer->width;
-        height = buffer->height;
-        if (depth.data) {
-            free(depth.data);
-            depth.width   = width;
-            depth.height  = height;
-            depth.stride  = buffer->stride;
-            depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
-            if (depth.data == 0) {
-                setError(EGL_BAD_ALLOC, EGL_FALSE);
-                return EGL_FALSE;
+    if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) == NO_ERROR) {
+
+        // TODO: lockBuffer should rather be executed when the very first
+        // direct rendering occurs.
+        nativeWindow->lockBuffer(nativeWindow, buffer);
+
+        // reallocate the depth-buffer if needed
+        if ((width != buffer->width) || (height != buffer->height)) {
+            // TODO: we probably should reset the swap rect here
+            // if the window size has changed
+            width = buffer->width;
+            height = buffer->height;
+            if (depth.data) {
+                free(depth.data);
+                depth.width   = width;
+                depth.height  = height;
+                depth.stride  = buffer->stride;
+                depth.data    = (GGLubyte*)malloc(depth.stride*depth.height*2);
+                if (depth.data == 0) {
+                    setError(EGL_BAD_ALLOC, EGL_FALSE);
+                    return EGL_FALSE;
+                }
             }
         }
-    }
-    
-    // keep a reference on the buffer
-    buffer->common.incRef(&buffer->common);
 
-    // finally pin the buffer down
-    if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | 
-            GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
-        LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
-                buffer, buffer->width, buffer->height);
-        return setError(EGL_BAD_ACCESS, EGL_FALSE);
-        // FIXME: we should make sure we're not accessing the buffer anymore
+        // keep a reference on the buffer
+        buffer->common.incRef(&buffer->common);
+
+        // finally pin the buffer down
+        if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN |
+                GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) {
+            LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)",
+                    buffer, buffer->width, buffer->height);
+            return setError(EGL_BAD_ACCESS, EGL_FALSE);
+            // FIXME: we should make sure we're not accessing the buffer anymore
+        }
+    } else {
+        return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE);
     }
 
     return EGL_TRUE;
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index e2e6f1a..274124b 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -2076,9 +2076,7 @@
                 return mDeskDockRotation;
             } else {
                 if (useSensorForOrientationLp(orientation)) {
-                    // If the user has enabled auto rotation by default, do it.
-                    int curRotation = mOrientationListener.getCurrentRotation();
-                    return curRotation >= 0 ? curRotation : lastRotation;
+                    return mOrientationListener.getCurrentRotation(lastRotation);
                 }
                 return Surface.ROTATION_0;
             }
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 6e7633e..ff31470 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1601,7 +1601,7 @@
         }
 
         if (mSuspended) {
-            sleepTime = idleSleepTime;
+            sleepTime = suspendSleepTimeUs();
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
@@ -2021,6 +2021,11 @@
     return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000) / 2;
 }
 
+uint32_t AudioFlinger::MixerThread::suspendSleepTimeUs()
+{
+    return (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+}
+
 // ----------------------------------------------------------------------------
 AudioFlinger::DirectOutputThread::DirectOutputThread(const sp<AudioFlinger>& audioFlinger, AudioStreamOut* output, int id, uint32_t device)
     :   PlaybackThread(audioFlinger, output, id, device)
@@ -2365,7 +2370,7 @@
         }
 
         if (mSuspended) {
-            sleepTime = idleSleepTime;
+            sleepTime = suspendSleepTimeUs();
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
@@ -2486,6 +2491,18 @@
     return time;
 }
 
+uint32_t AudioFlinger::DirectOutputThread::suspendSleepTimeUs()
+{
+    uint32_t time;
+    if (AudioSystem::isLinearPCM(mFormat)) {
+        time = (uint32_t)(((mFrameCount * 1000) / mSampleRate) * 1000);
+    } else {
+        time = 10000;
+    }
+    return time;
+}
+
+
 // ----------------------------------------------------------------------------
 
 AudioFlinger::DuplicatingThread::DuplicatingThread(const sp<AudioFlinger>& audioFlinger, AudioFlinger::MixerThread* mainThread, int id)
@@ -2612,7 +2629,7 @@
         }
 
         if (mSuspended) {
-            sleepTime = idleSleepTime;
+            sleepTime = suspendSleepTimeUs();
         }
         // sleepTime == 0 means we must write to audio hardware
         if (sleepTime == 0) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 5520551..51881f0 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -664,6 +664,7 @@
         virtual void            deleteTrackName_l(int name) = 0;
         virtual uint32_t        activeSleepTimeUs() = 0;
         virtual uint32_t        idleSleepTimeUs() = 0;
+        virtual uint32_t        suspendSleepTimeUs() = 0;
 
     private:
 
@@ -724,6 +725,7 @@
         virtual     void        deleteTrackName_l(int name);
         virtual     uint32_t    activeSleepTimeUs();
         virtual     uint32_t    idleSleepTimeUs();
+        virtual     uint32_t    suspendSleepTimeUs();
 
         AudioMixer*                     mAudioMixer;
     };
@@ -744,6 +746,7 @@
         virtual     void        deleteTrackName_l(int name);
         virtual     uint32_t    activeSleepTimeUs();
         virtual     uint32_t    idleSleepTimeUs();
+        virtual     uint32_t    suspendSleepTimeUs();
 
     private:
         void applyVolume(uint16_t leftVol, uint16_t rightVol, bool ramp);
diff --git a/services/java/com/android/server/InputManager.java b/services/java/com/android/server/InputManager.java
index c2c799b..f330d40 100644
--- a/services/java/com/android/server/InputManager.java
+++ b/services/java/com/android/server/InputManager.java
@@ -26,6 +26,7 @@
 import android.os.Environment;
 import android.os.LocalPowerManager;
 import android.os.PowerManager;
+import android.os.SystemProperties;
 import android.util.Slog;
 import android.util.Xml;
 import android.view.InputChannel;
@@ -47,9 +48,6 @@
 
 /*
  * Wraps the C++ InputManager and provides its callbacks.
- * 
- * XXX Tempted to promote this to a first-class service, ie. InputManagerService, to
- *     improve separation of concerns with respect to the window manager.
  */
 public class InputManager {
     static final String TAG = "InputManager";
@@ -507,5 +505,18 @@
             
             return names.toArray(new String[names.size()]);
         }
+        
+        @SuppressWarnings("unused")
+        public int getMaxEventsPerSecond() {
+            int result = 0;
+            try {
+                result = Integer.parseInt(SystemProperties.get("windowsmgr.max_events_per_sec"));
+            } catch (NumberFormatException e) {
+            }
+            if (result < 1) {
+                result = 60;
+            }
+            return result;
+        }
     }
 }
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index 4762ddb..9d31502 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -1162,7 +1162,7 @@
                     } catch (RemoteException e) {
                     }
                 } catch (NameNotFoundException e) {
-                    Log.w(TAG, "Unable to create context for heavy notification", e);
+                    Slog.w(TAG, "Unable to create context for heavy notification", e);
                 }
             } break;
             case CANCEL_HEAVY_NOTIFICATION_MSG: {
@@ -2367,7 +2367,7 @@
             }
             
             if (proc == null) {
-                Log.w(TAG, "crashApplication: nothing for uid=" + uid
+                Slog.w(TAG, "crashApplication: nothing for uid=" + uid
                         + " initialPid=" + initialPid
                         + " packageName=" + packageName);
                 return;
@@ -4051,6 +4051,9 @@
                     return false;
                 }
             }
+            if (!pi.exported && pi.applicationInfo.uid != uid) {
+                return false;
+            }
             return true;
         } catch (RemoteException e) {
             return false;
@@ -4199,8 +4202,8 @@
         if (perm == null) {
             perm = new UriPermission(targetUid, uri);
             targetUris.put(uri, perm);
-
         }
+
         perm.modeFlags |= modeFlags;
         if (activity == null) {
             perm.globalModeFlags |= modeFlags;
@@ -4221,6 +4224,11 @@
 
     void grantUriPermissionFromIntentLocked(int callingUid,
             String targetPkg, Intent intent, ActivityRecord activity) {
+        if (DEBUG_URI_PERMISSION) Slog.v(TAG,
+                "Grant URI perm to " + (intent != null ? intent.getData() : null)
+                + " from " + intent + "; flags=0x"
+                + Integer.toHexString(intent != null ? intent.getFlags() : 0));
+
         if (intent == null) {
             return;
         }
@@ -4899,13 +4907,12 @@
     }
 
     private final String checkContentProviderPermissionLocked(
-            ProviderInfo cpi, ProcessRecord r, int mode) {
+            ProviderInfo cpi, ProcessRecord r) {
         final int callingPid = (r != null) ? r.pid : Binder.getCallingPid();
         final int callingUid = (r != null) ? r.info.uid : Binder.getCallingUid();
         if (checkComponentPermission(cpi.readPermission, callingPid, callingUid,
                 cpi.exported ? -1 : cpi.applicationInfo.uid)
-                == PackageManager.PERMISSION_GRANTED
-                && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+                == PackageManager.PERMISSION_GRANTED) {
             return null;
         }
         if (checkComponentPermission(cpi.writePermission, callingPid, callingUid,
@@ -4922,8 +4929,7 @@
                 PathPermission pp = pps[i];
                 if (checkComponentPermission(pp.getReadPermission(), callingPid, callingUid,
                         cpi.exported ? -1 : cpi.applicationInfo.uid)
-                        == PackageManager.PERMISSION_GRANTED
-                        && mode == ParcelFileDescriptor.MODE_READ_ONLY || mode == -1) {
+                        == PackageManager.PERMISSION_GRANTED) {
                     return null;
                 }
                 if (checkComponentPermission(pp.getWritePermission(), callingPid, callingUid,
@@ -4934,6 +4940,15 @@
             }
         }
         
+        HashMap<Uri, UriPermission> perms = mGrantedUriPermissions.get(callingUid);
+        if (perms != null) {
+            for (Map.Entry<Uri, UriPermission> uri : perms.entrySet()) {
+                if (uri.getKey().getAuthority().equals(cpi.authority)) {
+                    return null;
+                }
+            }
+        }
+
         String msg = "Permission Denial: opening provider " + cpi.name
                 + " from " + (r != null ? r : "(null)") + " (pid=" + callingPid
                 + ", uid=" + callingUid + ") requires "
@@ -4963,10 +4978,9 @@
             cpr = mProvidersByName.get(name);
             if (cpr != null) {
                 cpi = cpr.info;
-                if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
-                    return new ContentProviderHolder(cpi,
-                            cpi.readPermission != null
-                                    ? cpi.readPermission : cpi.writePermission);
+                String msg;
+                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                    throw new SecurityException(msg);
                 }
 
                 if (r != null && cpr.canRunHere(r)) {
@@ -5026,10 +5040,9 @@
                     return null;
                 }
 
-                if (checkContentProviderPermissionLocked(cpi, r, -1) != null) {
-                    return new ContentProviderHolder(cpi,
-                            cpi.readPermission != null
-                                    ? cpi.readPermission : cpi.writePermission);
+                String msg;
+                if ((msg=checkContentProviderPermissionLocked(cpi, r)) != null) {
+                    throw new SecurityException(msg);
                 }
 
                 if (!mSystemReady && !mDidUpdate && !mWaitingUpdate
@@ -6180,7 +6193,7 @@
                 Binder.restoreCallingIdentity(origId);
             }
             int res = result.get();
-            Log.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
+            Slog.w(TAG, "handleApplicationStrictModeViolation; res=" + res);
         }
     }
 
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index ba58b43..3addc0d 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -139,6 +139,7 @@
     jmethodID filterJumpyTouchEvents;
     jmethodID getVirtualKeyDefinitions;
     jmethodID getExcludedDeviceNames;
+    jmethodID getMaxEventsPerSecond;
 } gCallbacksClassInfo;
 
 static struct {
@@ -249,6 +250,7 @@
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
     virtual int32_t waitForMotionEventTargets(MotionEvent* motionEvent, uint32_t policyFlags,
             int32_t injectorPid, int32_t injectorUid, Vector<InputTarget>& outTargets);
+    virtual int32_t getMaxEventsPerSecond();
 
 private:
     struct InputWindow {
@@ -310,6 +312,9 @@
     int32_t mFilterTouchEvents;
     int32_t mFilterJumpyTouchEvents;
 
+    // Cached throttling policy.
+    int32_t mMaxEventsPerSecond;
+
     // Cached display state.  (lock mDisplayLock)
     Mutex mDisplayLock;
     int32_t mDisplayWidth, mDisplayHeight;
@@ -400,6 +405,7 @@
 
 NativeInputManager::NativeInputManager(jobject callbacksObj) :
     mFilterTouchEvents(-1), mFilterJumpyTouchEvents(-1),
+    mMaxEventsPerSecond(-1),
     mDisplayWidth(-1), mDisplayHeight(-1), mDisplayOrientation(ROTATION_0),
     mDispatchEnabled(true), mDispatchFrozen(false), mWindowsReady(true),
     mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL),
@@ -921,6 +927,21 @@
     }
 }
 
+int32_t NativeInputManager::getMaxEventsPerSecond() {
+    if (mMaxEventsPerSecond < 0) {
+        JNIEnv* env = jniEnv();
+
+        jint result = env->CallIntMethod(mCallbacksObj,
+                gCallbacksClassInfo.getMaxEventsPerSecond);
+        if (checkAndClearExceptionFromCallback(env, "getMaxEventsPerSecond")) {
+            result = 60;
+        }
+
+        mMaxEventsPerSecond = result;
+    }
+    return mMaxEventsPerSecond;
+}
+
 void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) {
 #if DEBUG_FOCUS
     LOGD("setInputWindows");
@@ -2293,6 +2314,9 @@
     GET_METHOD_ID(gCallbacksClassInfo.getExcludedDeviceNames, gCallbacksClassInfo.clazz,
             "getExcludedDeviceNames", "()[Ljava/lang/String;");
 
+    GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, gCallbacksClassInfo.clazz,
+            "getMaxEventsPerSecond", "()I");
+
     // VirtualKeyDefinition
 
     FIND_CLASS(gVirtualKeyDefinitionClassInfo.clazz,
diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk
index 79772ed..a14bfb5 100644
--- a/services/surfaceflinger/Android.mk
+++ b/services/surfaceflinger/Android.mk
@@ -5,7 +5,6 @@
     clz.cpp.arm \
     DisplayHardware/DisplayHardware.cpp \
     DisplayHardware/DisplayHardwareBase.cpp \
-    DisplayHardware/HWComposer.cpp \
     BlurFilter.cpp.arm \
     GLExtensions.cpp \
     Layer.cpp \
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
index 166c528..2eac0a8 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp
@@ -36,11 +36,11 @@
 
 #include "DisplayHardware/DisplayHardware.h"
 
+#include <hardware/copybit.h>
 #include <hardware/overlay.h>
 #include <hardware/gralloc.h>
 
 #include "GLExtensions.h"
-#include "HWComposer.h"
 
 using namespace android;
 
@@ -76,7 +76,7 @@
         const sp<SurfaceFlinger>& flinger,
         uint32_t dpy)
     : DisplayHardwareBase(flinger, dpy),
-      mFlags(0), mHwc(0)
+      mFlags(0)
 {
     init(dpy);
 }
@@ -262,17 +262,6 @@
 
     // Unbind the context from this thread
     eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
-
-
-    // initialize the H/W composer
-    mHwc = new HWComposer();
-    if (mHwc->initCheck() == NO_ERROR) {
-        mHwc->setFrameBuffer(mDisplay, mSurface);
-    }
-}
-
-HWComposer& DisplayHardware::getHwComposer() const {
-    return *mHwc;
 }
 
 /*
@@ -328,12 +317,7 @@
     }
     
     mPageFlipCount++;
-
-    if (mHwc->initCheck() == NO_ERROR) {
-        mHwc->commit();
-    } else {
-        eglSwapBuffers(dpy, surface);
-    }
+    eglSwapBuffers(dpy, surface);
     checkEGLErrors("eglSwapBuffers");
 
     // for debugging
diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.h b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
index f2cfd2d..66bf521 100644
--- a/services/surfaceflinger/DisplayHardware/DisplayHardware.h
+++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.h
@@ -34,11 +34,12 @@
 #include "DisplayHardware/DisplayHardwareBase.h"
 
 struct overlay_control_device_t;
+struct framebuffer_device_t;
+struct copybit_image_t;
 
 namespace android {
 
 class FramebufferNativeWindow;
-class HWComposer;
 
 class DisplayHardware : public DisplayHardwareBase
 {
@@ -79,9 +80,6 @@
     uint32_t getPageFlipCount() const;
     EGLDisplay getEGLDisplay() const { return mDisplay; }
     overlay_control_device_t* getOverlayEngine() const { return mOverlayEngine; }
-
-    // Hardware Composer
-    HWComposer& getHwComposer() const;
     
     status_t compositionComplete() const;
     
@@ -109,8 +107,6 @@
     GLint           mMaxViewportDims;
     GLint           mMaxTextureSize;
     
-    HWComposer*     mHwc;
-
     sp<FramebufferNativeWindow> mNativeWindow;
     overlay_control_device_t* mOverlayEngine;
 };
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp
deleted file mode 100644
index 0291d78..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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.
- */
-
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-
-#include <utils/Errors.h>
-
-#include <hardware/hardware.h>
-
-#include <cutils/log.h>
-
-#include <EGL/egl.h>
-
-#include "HWComposer.h"
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-HWComposer::HWComposer()
-    : mModule(0), mHwc(0), mList(0), mCapacity(0),
-      mDpy(EGL_NO_DISPLAY), mSur(EGL_NO_SURFACE)
-{
-    int err = hw_get_module(HWC_HARDWARE_MODULE_ID, &mModule);
-    LOGW_IF(err, "%s module not found", HWC_HARDWARE_MODULE_ID);
-    if (err == 0) {
-        err = hwc_open(mModule, &mHwc);
-        LOGE_IF(err, "%s device failed to initialize (%s)",
-                HWC_HARDWARE_COMPOSER, strerror(-err));
-    }
-}
-
-HWComposer::~HWComposer() {
-    free(mList);
-    if (mHwc) {
-        hwc_close(mHwc);
-    }
-}
-
-status_t HWComposer::initCheck() const {
-    return mHwc ? NO_ERROR : NO_INIT;
-}
-
-void HWComposer::setFrameBuffer(EGLDisplay dpy, EGLSurface sur) {
-    mDpy = (hwc_display_t)dpy;
-    mSur = (hwc_surface_t)sur;
-}
-
-status_t HWComposer::createWorkList(size_t numLayers) {
-    if (mHwc) {
-        if (!mList || mCapacity < numLayers) {
-            free(mList);
-            size_t size = sizeof(hwc_layer_list) + numLayers*sizeof(hwc_layer_t);
-            mList = (hwc_layer_list_t*)malloc(size);
-            mCapacity = numLayers;
-        }
-        mList->flags = HWC_GEOMETRY_CHANGED;
-        mList->numHwLayers = numLayers;
-    }
-    return NO_ERROR;
-}
-
-status_t HWComposer::prepare() const {
-    int err = mHwc->prepare(mHwc, mList);
-    return (status_t)err;
-}
-
-status_t HWComposer::commit() const {
-    int err = mHwc->set(mHwc, mDpy, mSur, mList);
-    mList->flags &= ~HWC_GEOMETRY_CHANGED;
-    return (status_t)err;
-}
-
-size_t HWComposer::getNumLayers() const {
-    return mList ? mList->numHwLayers : 0;
-}
-
-hwc_layer_t* HWComposer::getLayers() const {
-    return mList ? mList->hwLayers : 0;
-}
-
-// ---------------------------------------------------------------------------
-}; // namespace android
diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h
deleted file mode 100644
index c5d5c2b..0000000
--- a/services/surfaceflinger/DisplayHardware/HWComposer.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * 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.
- */
-
-#ifndef ANDROID_SF_HWCOMPOSER_H
-#define ANDROID_SF_HWCOMPOSER_H
-
-#include <stdint.h>
-#include <sys/types.h>
-
-#include <EGL/egl.h>
-
-#include <hardware/hwcomposer.h>
-
-namespace android {
-// ---------------------------------------------------------------------------
-
-class HWComposer
-{
-public:
-
-    HWComposer();
-    ~HWComposer();
-
-    status_t initCheck() const;
-
-    // tells the HAL what the framebuffer is
-    void setFrameBuffer(EGLDisplay dpy, EGLSurface sur);
-
-    // create a work list for numLayers layer
-    status_t createWorkList(size_t numLayers);
-
-    // Asks the HAL what it can do
-    status_t prepare() const;
-
-    // commits the list
-    status_t commit() const;
-
-
-    size_t getNumLayers() const;
-    hwc_layer_t* getLayers() const;
-
-private:
-    hw_module_t const*      mModule;
-    hwc_composer_device_t*  mHwc;
-    hwc_layer_list_t*       mList;
-    size_t                  mCapacity;
-    hwc_display_t           mDpy;
-    hwc_surface_t           mSur;
-};
-
-
-// ---------------------------------------------------------------------------
-}; // namespace android
-
-#endif // ANDROID_SF_HWCOMPOSER_H
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 3720e1669..629d993 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -35,7 +35,6 @@
 #include "Layer.h"
 #include "SurfaceFlinger.h"
 #include "DisplayHardware/DisplayHardware.h"
-#include "DisplayHardware/HWComposer.h"
 
 
 #define DEBUG_RESIZE    0
@@ -178,62 +177,6 @@
     return NO_ERROR;
 }
 
-void Layer::setGeometry(hwc_layer_t* hwcl)
-{
-    hwcl->compositionType = HWC_FRAMEBUFFER;
-    hwcl->hints = 0;
-    hwcl->flags = 0;
-    hwcl->transform = 0;
-    hwcl->blending = HWC_BLENDING_NONE;
-
-    // we can't do alpha-fade with the hwc HAL
-    const State& s(drawingState());
-    if (s.alpha < 0xFF) {
-        hwcl->flags = HWC_SKIP_LAYER;
-        return;
-    }
-
-    // we can only handle simple transformation
-    if (mOrientation & Transform::ROT_INVALID) {
-        hwcl->flags = HWC_SKIP_LAYER;
-        return;
-    }
-
-    hwcl->transform = mOrientation;
-
-    if (needsBlending()) {
-        hwcl->blending = mPremultipliedAlpha ?
-                HWC_BLENDING_PREMULT : HWC_BLENDING_COVERAGE;
-    }
-
-    hwcl->displayFrame.left   = mTransformedBounds.left;
-    hwcl->displayFrame.top    = mTransformedBounds.top;
-    hwcl->displayFrame.right  = mTransformedBounds.right;
-    hwcl->displayFrame.bottom = mTransformedBounds.bottom;
-
-    hwcl->visibleRegionScreen.rects =
-            reinterpret_cast<hwc_rect_t const *>(
-                    visibleRegionScreen.getArray(
-                            &hwcl->visibleRegionScreen.numRects));
-}
-
-void Layer::setPerFrameData(hwc_layer_t* hwcl) {
-    sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
-    if (buffer == NULL) {
-        // this situation can happen if we ran out of memory for instance.
-        // not much we can do. continue to use whatever texture was bound
-        // to this context.
-        hwcl->handle = NULL;
-        return;
-    }
-    hwcl->handle = const_cast<native_handle_t*>(buffer->handle);
-    // TODO: set the crop value properly
-    hwcl->sourceCrop.left   = 0;
-    hwcl->sourceCrop.top    = 0;
-    hwcl->sourceCrop.right  = buffer->width;
-    hwcl->sourceCrop.bottom = buffer->height;
-}
-
 void Layer::reloadTexture(const Region& dirty)
 {
     sp<GraphicBuffer> buffer(mBufferManager.getActiveBuffer());
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 188da6a..e1d283b 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -68,8 +68,6 @@
     bool isFixedSize() const;
 
     // LayerBase interface
-    virtual void setGeometry(hwc_layer_t* hwcl);
-    virtual void setPerFrameData(hwc_layer_t* hwcl);
     virtual void onDraw(const Region& clip) const;
     virtual uint32_t doTransaction(uint32_t transactionFlags);
     virtual void lockPageFlip(bool& recomputeVisibleRegions);
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 043d54d..91ac915 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -307,15 +307,6 @@
     }
 }
 
-void LayerBase::setGeometry(hwc_layer_t* hwcl) {
-    hwcl->flags |= HWC_SKIP_LAYER;
-}
-
-void LayerBase::setPerFrameData(hwc_layer_t* hwcl) {
-    hwcl->compositionType = HWC_FRAMEBUFFER;
-    hwcl->handle = NULL;
-}
-
 void LayerBase::draw(const Region& clip) const
 {
     // reset GL state
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index dd1cd05..22bf857 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -35,8 +35,6 @@
 
 #include <pixelflinger/pixelflinger.h>
 
-#include <hardware/hwcomposer.h>
-
 #include "Transform.h"
 
 namespace android {
@@ -110,10 +108,6 @@
 
     virtual const char* getTypeId() const { return "LayerBase"; }
 
-    virtual void setGeometry(hwc_layer_t* hwcl);
-
-    virtual void setPerFrameData(hwc_layer_t* hwcl);
-
     /**
      * draw - performs some global clipping optimizations
      * and calls onDraw().
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index d257897..637ae48 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -52,7 +52,6 @@
 #include "SurfaceFlinger.h"
 
 #include "DisplayHardware/DisplayHardware.h"
-#include "DisplayHardware/HWComposer.h"
 
 /* ideally AID_GRAPHICS would be in a semi-public header
  * or there would be a way to map a user/group name to its id
@@ -77,7 +76,6 @@
         mAccessSurfaceFlinger("android.permission.ACCESS_SURFACE_FLINGER"),
         mDump("android.permission.DUMP"),
         mVisibleRegionsDirty(false),
-        mHwWorkListDirty(false),
         mDeferReleaseConsole(false),
         mFreezeDisplay(false),
         mFreezeCount(0),
@@ -370,11 +368,6 @@
     // post surfaces (if needed)
     handlePageFlip();
 
-    if (UNLIKELY(mHwWorkListDirty)) {
-        // build the h/w work list
-        handleWorkList();
-    }
-
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
     if (LIKELY(hw.canDraw() && !isFrozen())) {
         // repaint the framebuffer (if needed)
@@ -450,7 +443,6 @@
         handleTransactionLocked(transactionFlags, ditchedLayers);
         mLastTransactionTime = systemTime() - now;
         mDebugInTransaction = 0;
-        mHwWorkListDirty = true;
         // here the transaction has been committed
     }
 
@@ -458,7 +450,6 @@
      * Clean-up all layers that went away
      * (do this without the lock held)
      */
-
     const size_t count = ditchedLayers.size();
     for (size_t i=0 ; i<count ; i++) {
         if (ditchedLayers[i] != 0) {
@@ -692,8 +683,8 @@
 void SurfaceFlinger::handlePageFlip()
 {
     bool visibleRegions = mVisibleRegionsDirty;
-    LayerVector& currentLayers(
-            const_cast<LayerVector&>(mDrawingState.layersSortedByZ));
+    LayerVector& currentLayers = const_cast<LayerVector&>(
+            mDrawingState.layersSortedByZ);
     visibleRegions |= lockPageFlip(currentLayers);
 
         const DisplayHardware& hw = graphicPlane(0).displayHardware();
@@ -716,7 +707,6 @@
 
             mWormholeRegion = screenRegion.subtract(opaqueRegion);
             mVisibleRegionsDirty = false;
-            mHwWorkListDirty = true;
         }
 
     unlockPageFlip(currentLayers);
@@ -747,20 +737,6 @@
     }
 }
 
-void SurfaceFlinger::handleWorkList()
-{
-    mHwWorkListDirty = false;
-    HWComposer& hwc(graphicPlane(0).displayHardware().getHwComposer());
-    if (hwc.initCheck() == NO_ERROR) {
-        const Vector< sp<LayerBase> >& currentLayers(mVisibleLayersSortedByZ);
-        const size_t count = currentLayers.size();
-        hwc.createWorkList(count);
-        hwc_layer_t* const cur(hwc.getLayers());
-        for (size_t i=0 ; cur && i<count ; i++) {
-            currentLayers[i]->setGeometry(&cur[i]);
-        }
-    }
-}
 
 void SurfaceFlinger::handleRepaint()
 {
@@ -825,72 +801,9 @@
         // draw something...
         drawWormhole();
     }
-
-    status_t err = NO_ERROR;
     const Vector< sp<LayerBase> >& layers(mVisibleLayersSortedByZ);
-    size_t count = layers.size();
-
-    const DisplayHardware& hw(graphicPlane(0).displayHardware());
-    HWComposer& hwc(hw.getHwComposer());
-    hwc_layer_t* const cur(hwc.getLayers());
-
-    LOGE_IF(cur && hwc.getNumLayers() != count,
-            "HAL number of layers (%d) doesn't match surfaceflinger (%d)",
-            hwc.getNumLayers(), count);
-
-    // just to be extra-safe, use the smallest count
-    if (hwc.initCheck() == NO_ERROR) {
-        count = count < hwc.getNumLayers() ? count : hwc.getNumLayers();
-    }
-
-    /*
-     *  update the per-frame h/w composer data for each layer
-     *  and build the transparent region of the FB
-     */
-    Region transparent;
-    if (cur) {
-        for (size_t i=0 ; i<count ; i++) {
-            const sp<LayerBase>& layer(layers[i]);
-            layer->setPerFrameData(&cur[i]);
-            if (cur[i].hints & HWC_HINT_CLEAR_FB) {
-                if (!(layer->needsBlending())) {
-                    transparent.orSelf(layer->visibleRegionScreen);
-                }
-            }
-        }
-        err = hwc.prepare();
-        LOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
-    }
-
-    /*
-     *  clear the area of the FB that need to be transparent
-     */
-    transparent.andSelf(dirty);
-    if (!transparent.isEmpty()) {
-        glClearColor(0,0,0,0);
-        Region::const_iterator it = transparent.begin();
-        Region::const_iterator const end = transparent.end();
-        const int32_t height = hw.getHeight();
-        while (it != end) {
-            const Rect& r(*it++);
-            const GLint sy = height - (r.top + r.height());
-            glScissor(r.left, sy, r.width(), r.height());
-            glClear(GL_COLOR_BUFFER_BIT);
-        }
-    }
-
-
-    /*
-     * and then, render the layers targeted at the framebuffer
-     */
-    for (size_t i=0 ; i<count ; i++) {
-        if (cur) {
-            if (!(cur[i].compositionType == HWC_FRAMEBUFFER) ||
-                    cur[i].flags & HWC_SKIP_LAYER) {
-                // skip layers handled by the HAL
-                continue;
-            }
-        }
+    const size_t count = layers.size();
+    for (size_t i=0 ; i<count ; ++i) {
         const sp<LayerBase>& layer(layers[i]);
         const Region clip(dirty.intersect(layer->visibleRegionScreen));
         if (!clip.isEmpty()) {
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 8e286e5..8ecfc01 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -296,7 +296,6 @@
             void        handlePageFlip();
             bool        lockPageFlip(const LayerVector& currentLayers);
             void        unlockPageFlip(const LayerVector& currentLayers);
-            void        handleWorkList();
             void        handleRepaint();
             void        postFramebuffer();
             void        composeSurfaces(const Region& dirty);
@@ -371,7 +370,6 @@
                 Region                      mInvalidRegion;
                 Region                      mWormholeRegion;
                 bool                        mVisibleRegionsDirty;
-                bool                        mHwWorkListDirty;
                 bool                        mDeferReleaseConsole;
                 bool                        mFreezeDisplay;
                 int32_t                     mFreezeCount;
diff --git a/voip/java/android/net/rtp/AudioGroup.java b/voip/java/android/net/rtp/AudioGroup.java
index dc86082..37cc121 100644
--- a/voip/java/android/net/rtp/AudioGroup.java
+++ b/voip/java/android/net/rtp/AudioGroup.java
@@ -49,27 +49,28 @@
     synchronized void add(AudioStream stream, AudioCodec codec, int codecType, int dtmfType) {
         if (!mStreams.containsKey(stream)) {
             try {
-                int id = add(stream.getMode(), stream.dup(),
+                int socket = stream.dup();
+                add(stream.getMode(), socket,
                         stream.getRemoteAddress().getHostAddress(), stream.getRemotePort(),
                         codec.name, codec.sampleRate, codec.sampleCount, codecType, dtmfType);
-                mStreams.put(stream, id);
+                mStreams.put(stream, socket);
             } catch (NullPointerException e) {
                 throw new IllegalStateException(e);
             }
         }
     }
 
-    private native int add(int mode, int socket, String remoteAddress, int remotePort,
+    private native void add(int mode, int socket, String remoteAddress, int remotePort,
             String codecName, int sampleRate, int sampleCount, int codecType, int dtmfType);
 
     synchronized void remove(AudioStream stream) {
-        Integer id = mStreams.remove(stream);
-        if (id != null) {
-            remove(id);
+        Integer socket = mStreams.remove(stream);
+        if (socket != null) {
+            remove(socket);
         }
     }
 
-    private native void remove(int id);
+    private native void remove(int socket);
 
     /**
      * Sends a DTMF digit to every {@link AudioStream} in this group. Currently
diff --git a/voip/jni/rtp/AudioGroup.cpp b/voip/jni/rtp/AudioGroup.cpp
index fc1ed9b..08a8d1c 100644
--- a/voip/jni/rtp/AudioGroup.cpp
+++ b/voip/jni/rtp/AudioGroup.cpp
@@ -367,7 +367,7 @@
             MSG_TRUNC | MSG_DONTWAIT);
 
         // Do we need to check SSRC, sequence, and timestamp? They are not
-        // reliable but at least they can be used to identity duplicates?
+        // reliable but at least they can be used to identify duplicates?
         if (length < 12 || length > (int)sizeof(buffer) ||
             (ntohl(*(uint32_t *)buffer) & 0xC07F0000) != mCodecMagic) {
             LOGD("stream[%d] malformed packet", mSocket);
@@ -526,70 +526,6 @@
     LOGD("group[%d] is dead", mDeviceSocket);
 }
 
-#define FROYO_COMPATIBLE
-#ifdef FROYO_COMPATIBLE
-
-// Copied from AudioRecord.cpp.
-status_t AudioRecord_getMinFrameCount(
-        int* frameCount,
-        uint32_t sampleRate,
-        int format,
-        int channelCount)
-{
-    size_t size = 0;
-    if (AudioSystem::getInputBufferSize(sampleRate, format, channelCount, &size)
-            != NO_ERROR) {
-        LOGE("AudioSystem could not query the input buffer size.");
-        return NO_INIT;
-    }
-
-    if (size == 0) {
-        LOGE("Unsupported configuration: sampleRate %d, format %d, channelCount %d",
-            sampleRate, format, channelCount);
-        return BAD_VALUE;
-    }
-
-    // We double the size of input buffer for ping pong use of record buffer.
-    size <<= 1;
-
-    if (AudioSystem::isLinearPCM(format)) {
-        size /= channelCount * (format == AudioSystem::PCM_16_BIT ? 2 : 1);
-    }
-
-    *frameCount = size;
-    return NO_ERROR;
-}
-
-// Copied from AudioTrack.cpp.
-status_t AudioTrack_getMinFrameCount(
-        int* frameCount,
-        int streamType,
-        uint32_t sampleRate)
-{
-    int afSampleRate;
-    if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
-    int afFrameCount;
-    if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
-    uint32_t afLatency;
-    if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
-        return NO_INIT;
-    }
-
-    // Ensure that buffer depth covers at least audio hardware latency
-    uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
-    if (minBufCount < 2) minBufCount = 2;
-
-    *frameCount = (sampleRate == 0) ? afFrameCount * minBufCount :
-              afFrameCount * minBufCount * sampleRate / afSampleRate;
-    return NO_ERROR;
-}
-
-#endif
-
 bool AudioGroup::set(int sampleRate, int sampleCount)
 {
     mEventQueue = epoll_create(2);
@@ -603,15 +539,6 @@
     // Find out the frame count for AudioTrack and AudioRecord.
     int output = 0;
     int input = 0;
-#ifdef FROYO_COMPATIBLE
-    if (AudioTrack_getMinFrameCount(&output, AudioSystem::VOICE_CALL,
-        sampleRate) != NO_ERROR || output <= 0 ||
-        AudioRecord_getMinFrameCount(&input, sampleRate,
-        AudioSystem::PCM_16_BIT, 1) != NO_ERROR || input <= 0) {
-        LOGE("cannot compute frame count");
-        return false;
-    }
-#else
     if (AudioTrack::getMinFrameCount(&output, AudioSystem::VOICE_CALL,
         sampleRate) != NO_ERROR || output <= 0 ||
         AudioRecord::getMinFrameCount(&input, sampleRate,
@@ -619,7 +546,6 @@
         LOGE("cannot compute frame count");
         return false;
     }
-#endif
     LOGD("reported frame count: output %d, input %d", output, input);
 
     output = (output + sampleCount - 1) / sampleCount * sampleCount;
@@ -771,6 +697,10 @@
     for (AudioStream *stream = mChain; stream->mNext; stream = stream->mNext) {
         AudioStream *target = stream->mNext;
         if (target->mSocket == socket) {
+            if (epoll_ctl(mEventQueue, EPOLL_CTL_DEL, socket, NULL)) {
+                LOGE("epoll_ctl: %s", strerror(errno));
+                return false;
+            }
             stream->mNext = target->mNext;
             LOGD("stream[%d] leaves group[%d]", socket, mDeviceSocket);
             delete target;
@@ -874,7 +804,7 @@
 static jfieldID gNative;
 static jfieldID gMode;
 
-jint add(JNIEnv *env, jobject thiz, jint mode,
+void add(JNIEnv *env, jobject thiz, jint mode,
     jint socket, jstring jRemoteAddress, jint remotePort,
     jstring jCodecName, jint sampleRate, jint sampleCount,
     jint codecType, jint dtmfType)
@@ -887,7 +817,7 @@
     sockaddr_storage remote;
     if (parse(env, jRemoteAddress, remotePort, &remote) < 0) {
         // Exception already thrown.
-        return -1;
+        goto error;
     }
     if (sampleRate < 0 || sampleCount < 0 || codecType < 0 || codecType > 127) {
         jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
@@ -895,12 +825,12 @@
     }
     if (!jCodecName) {
         jniThrowNullPointerException(env, "codecName");
-        return -1;
+        goto error;
     }
     codecName = env->GetStringUTFChars(jCodecName, NULL);
     if (!codecName) {
         // Exception already thrown.
-        return -1;
+        goto error;
     }
 
     // Create audio stream.
@@ -909,8 +839,10 @@
         codecType, dtmfType)) {
         jniThrowException(env, "java/lang/IllegalStateException",
             "cannot initialize audio stream");
+        env->ReleaseStringUTFChars(jCodecName, codecName);
         goto error;
     }
+    env->ReleaseStringUTFChars(jCodecName, codecName);
     socket = -1;
 
     // Create audio group.
@@ -934,16 +866,13 @@
 
     // Succeed.
     env->SetIntField(thiz, gNative, (int)group);
-    env->ReleaseStringUTFChars(jCodecName, codecName);
-    return socket;
+    return;
 
 error:
     delete group;
     delete stream;
     close(socket);
     env->SetIntField(thiz, gNative, NULL);
-    env->ReleaseStringUTFChars(jCodecName, codecName);
-    return -1;
 }
 
 void remove(JNIEnv *env, jobject thiz, jint socket)
@@ -976,7 +905,7 @@
 }
 
 JNINativeMethod gMethods[] = {
-    {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)I", (void *)add},
+    {"add", "(IILjava/lang/String;ILjava/lang/String;IIII)V", (void *)add},
     {"remove", "(I)V", (void *)remove},
     {"setMode", "(I)V", (void *)setMode},
     {"sendDtmf", "(I)V", (void *)sendDtmf},