Merge "ArrayIndexOutOfBounds exception in TouchExplorer."
diff --git a/api/current.txt b/api/current.txt
index 0cb32f0..2df88e4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22847,8 +22847,10 @@
     field public static final int TYPE_VIEW_HOVER_ENTER = 128; // 0x80
     field public static final int TYPE_VIEW_HOVER_EXIT = 256; // 0x100
     field public static final int TYPE_VIEW_LONG_CLICKED = 2; // 0x2
+    field public static final int TYPE_VIEW_SCROLLED = 4096; // 0x1000
     field public static final int TYPE_VIEW_SELECTED = 4; // 0x4
     field public static final int TYPE_VIEW_TEXT_CHANGED = 16; // 0x10
+    field public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 8192; // 0x2000
     field public static final int TYPE_WINDOW_CONTENT_CHANGED = 2048; // 0x800
     field public static final int TYPE_WINDOW_STATE_CHANGED = 32; // 0x20
   }
@@ -22897,6 +22899,7 @@
     method public boolean isFocused();
     method public boolean isLongClickable();
     method public boolean isPassword();
+    method public boolean isScrollable();
     method public boolean isSelected();
     method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
     method public static android.view.accessibility.AccessibilityNodeInfo obtain();
@@ -22916,6 +22919,7 @@
     method public void setPackageName(java.lang.CharSequence);
     method public void setParent(android.view.View);
     method public void setPassword(boolean);
+    method public void setScrollable(boolean);
     method public void setSelected(boolean);
     method public void setSource(android.view.View);
     method public void setText(java.lang.CharSequence);
@@ -22937,13 +22941,17 @@
     method public int getItemCount();
     method public android.os.Parcelable getParcelableData();
     method public int getRemovedCount();
+    method public int getScrollX();
+    method public int getScrollY();
     method public android.view.accessibility.AccessibilityNodeInfo getSource();
     method public java.util.List<java.lang.CharSequence> getText();
+    method public int getToIndex();
     method public int getWindowId();
     method public boolean isChecked();
     method public boolean isEnabled();
     method public boolean isFullScreen();
     method public boolean isPassword();
+    method public boolean isScrollable();
     method public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord);
     method public static android.view.accessibility.AccessibilityRecord obtain();
     method public void recycle();
@@ -22960,7 +22968,11 @@
     method public void setParcelableData(android.os.Parcelable);
     method public void setPassword(boolean);
     method public void setRemovedCount(int);
+    method public void setScrollX(int);
+    method public void setScrollY(int);
+    method public void setScrollable(boolean);
     method public void setSource(android.view.View);
+    method public void setToIndex(int);
   }
 
 }
diff --git a/cmds/keystore/keystore.cpp b/cmds/keystore/keystore.cpp
index bbd1a1b..1c1f37a 100644
--- a/cmds/keystore/keystore.cpp
+++ b/cmds/keystore/keystore.cpp
@@ -708,7 +708,7 @@
     uid_t euid;
     uint32_t perms;
 } users[] = {
-    {AID_SYSTEM,   ~0,         ~GET},
+    {AID_SYSTEM,   ~0,         ~0},
     {AID_VPN,      AID_SYSTEM, GET},
     {AID_WIFI,     AID_SYSTEM, GET},
     {AID_ROOT,     AID_SYSTEM, GET},
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index e927f6c..208869b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1573,7 +1573,7 @@
 
         boolean hardwareAccelerated = sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_hardwareAccelerated,
-                false);
+                owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH);
 
         if (sa.getBoolean(
                 com.android.internal.R.styleable.AndroidManifestApplication_hasCode,
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 0be1776..8a42693 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -288,7 +288,8 @@
      * you can call {@link #reconnect()} to reclaim the camera.
      *
      * <p>This must be done before calling
-     * {@link android.media.MediaRecorder#setCamera(Camera)}.
+     * {@link android.media.MediaRecorder#setCamera(Camera)}. This cannot be
+     * called after recording starts.
      *
      * <p>If you are not recording video, you probably do not need this method.
      *
@@ -301,6 +302,11 @@
      * Camera objects are locked by default unless {@link #unlock()} is
      * called.  Normally {@link #reconnect()} is used instead.
      *
+     * <p>Since API level 13, camera is automatically locked for applications in
+     * {@link android.media.MediaRecorder#start()}. Applications can use the
+     * camera (ex: zoom) after recording starts. There is no need to call this
+     * after recording starts or stops.
+     *
      * <p>If you are not recording video, you probably do not need this method.
      *
      * @throws RuntimeException if the camera cannot be re-locked (for
@@ -315,9 +321,10 @@
      * which will re-acquire the lock and allow you to continue using the
      * camera.
      *
-     * <p>This must be done after {@link android.media.MediaRecorder} is
-     * done recording if {@link android.media.MediaRecorder#setCamera(Camera)}
-     * was used.
+     * <p>Since API level 13, camera is automatically locked for applications in
+     * {@link android.media.MediaRecorder#start()}. Applications can use the
+     * camera (ex: zoom) after recording starts. There is no need to call this
+     * after recording starts or stops.
      *
      * <p>If you are not recording video, you probably do not need this method.
      *
@@ -827,7 +834,9 @@
      * <p>This method is only valid when preview is active (after
      * {@link #startPreview()}).  Preview will be stopped after the image is
      * taken; callers must call {@link #startPreview()} again if they want to
-     * re-start preview or take more pictures.
+     * re-start preview or take more pictures. This should not be called between
+     * {@link android.media.MediaRecorder#start()} and
+     * {@link android.media.MediaRecorder#stop()}.
      *
      * <p>After calling this method, you must not call {@link #startPreview()}
      * or take another picture until the JPEG callback has returned.
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 1b28aa2..6c7c58d 100644
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -278,6 +278,16 @@
 
         /**
          * Current version under development.
+         *
+         * <p>Applications targeting this or a later release will get these
+         * new changes in behavior:</p>
+         * <ul>
+         * <li> 2d drawing hardware acceleration is now turned on by default.
+         * You can use
+         * {@link android.R.attr#hardwareAccelerated android:hardwareAccelerated}
+         * to turn it off if needed, although this is strongly discouraged since
+         * it will result in poor performance on larger screen devices.
+         * </ul>
          */
         public static final int ICE_CREAM_SANDWICH = CUR_DEVELOPMENT;
     }
diff --git a/core/java/android/pim/EventRecurrence.java b/core/java/android/pim/EventRecurrence.java
index cde7dac..830f63f 100644
--- a/core/java/android/pim/EventRecurrence.java
+++ b/core/java/android/pim/EventRecurrence.java
@@ -456,6 +456,19 @@
         return true;
     }
 
+    /**
+     * Determines whether this rule specifies a simple monthly rule by weekday, such as
+     * "FREQ=MONTHLY;BYDAY=3TU" (the 3rd Tuesday of every month).
+     * <p>
+     * Negative days, e.g. "FREQ=MONTHLY;BYDAY=-1TU" (the last Tuesday of every month),
+     * will cause "false" to be returned.
+     * <p>
+     * Rules that fire every week, such as "FREQ=MONTHLY;BYDAY=TU" (every Tuesday of every
+     * month) will cause "false" to be returned.  (Note these are usually expressed as
+     * WEEKLY rules, and hence are uncommon.)
+     *
+     * @return true if this rule is of the appropriate form
+     */
     public boolean repeatsMonthlyOnDayCount() {
         if (this.freq != MONTHLY) {
             return false;
@@ -465,6 +478,10 @@
             return false;
         }
 
+        if (bydayNum[0] <= 0) {
+            return false;
+        }
+
         return true;
     }
 
diff --git a/core/java/android/provider/SyncConstValue.java b/core/java/android/provider/SyncConstValue.java
index 2fcf315..027aed1 100644
--- a/core/java/android/provider/SyncConstValue.java
+++ b/core/java/android/provider/SyncConstValue.java
@@ -18,7 +18,7 @@
 
 /**
  * Columns for tables that are synced to a server.
- * @deprecated
+ * @deprecated Do not use.
  * @hide
  */
 public interface SyncConstValue
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
index 5dbda90..6c3d387 100755
--- a/core/java/android/view/KeyEvent.java
+++ b/core/java/android/view/KeyEvent.java
@@ -2361,11 +2361,11 @@
      * Gets the {@link KeyCharacterMap} associated with the keyboard device.
      *
      * @return The associated key character map.
-     * @throws {@link UnavailableException} if the key character map
+     * @throws {@link KeyCharacterMap.UnavailableException} if the key character map
      * could not be loaded because it was malformed or the default key character map
      * is missing from the system.
      *
-     * @see {@link KeyCharacterMap#load}
+     * @see KeyCharacterMap#load
      */
     public final KeyCharacterMap getKeyCharacterMap() {
         return KeyCharacterMap.load(mDeviceId);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 7e75c4e..5743134 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2326,6 +2326,7 @@
     private CheckForLongPress mPendingCheckForLongPress;
     private CheckForTap mPendingCheckForTap = null;
     private PerformClick mPerformClick;
+    private SendViewScrolledAccessibilityEvent mSendViewScrolledAccessibilityEvent;
 
     private UnsetPressedState mUnsetPressedState;
 
@@ -3699,7 +3700,6 @@
      * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent)
      */
     public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
-
     }
 
     /**
@@ -3728,12 +3728,23 @@
         event.setEnabled(isEnabled());
         event.setContentDescription(mContentDescription);
 
-        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_FOCUSED && mAttachInfo != null) {
-            ArrayList<View> focusablesTempList = mAttachInfo.mFocusablesTempList;
-            getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD, FOCUSABLES_ALL);
-            event.setItemCount(focusablesTempList.size());
-            event.setCurrentItemIndex(focusablesTempList.indexOf(this));
-            focusablesTempList.clear();
+        final int eventType = event.getEventType();
+        switch (eventType) {
+            case AccessibilityEvent.TYPE_VIEW_FOCUSED: {
+                if (mAttachInfo != null) {
+                    ArrayList<View> focusablesTempList = mAttachInfo.mFocusablesTempList;
+                    getRootView().addFocusables(focusablesTempList, View.FOCUS_FORWARD,
+                            FOCUSABLES_ALL);
+                    event.setItemCount(focusablesTempList.size());
+                    event.setCurrentItemIndex(focusablesTempList.indexOf(this));
+                    focusablesTempList.clear();
+                }
+            } break;
+            case AccessibilityEvent.TYPE_VIEW_SCROLLED: {
+                event.setScrollX(mScrollX);
+                event.setScrollY(mScrollY);
+                event.setItemCount(getHeight());
+            } break;
         }
     }
 
@@ -6165,6 +6176,16 @@
     }
 
     /**
+     * Remove the pending callback for sending a
+     * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
+     */
+    private void removeSendViewScrolledAccessibilityEventCallback() {
+        if (mSendViewScrolledAccessibilityEvent != null) {
+            removeCallbacks(mSendViewScrolledAccessibilityEvent);
+        }
+    }
+
+    /**
      * Sets the TouchDelegate for this View.
      */
     public void setTouchDelegate(TouchDelegate delegate) {
@@ -6337,6 +6358,10 @@
      * @param oldt Previous vertical scroll origin.
      */
     protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            postSendViewScrolledAccessibilityEventCallback();
+        }
+
         mBackgroundSizeChanged = true;
 
         final AttachInfo ai = mAttachInfo;
@@ -8263,6 +8288,22 @@
     }
 
     /**
+     * Post a callback to send a {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} event.
+     * This event is sent at most once every
+     * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
+     */
+    private void postSendViewScrolledAccessibilityEventCallback() {
+        if (mSendViewScrolledAccessibilityEvent == null) {
+            mSendViewScrolledAccessibilityEvent = new SendViewScrolledAccessibilityEvent();
+        }
+        if (!mSendViewScrolledAccessibilityEvent.mIsPending) {
+            mSendViewScrolledAccessibilityEvent.mIsPending = true;
+            postDelayed(mSendViewScrolledAccessibilityEvent,
+                    ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
+        }
+    }
+
+    /**
      * Called by a parent to request that a child update its values for mScrollX
      * and mScrollY if necessary. This will typically be done if the child is
      * animating a scroll using a {@link android.widget.Scroller Scroller}
@@ -9019,6 +9060,7 @@
         removeUnsetPressCallback();
         removeLongPressCallback();
         removePerformClickCallback();
+        removeSendViewScrolledAccessibilityEventCallback();
 
         destroyDrawingCache();
 
@@ -13820,6 +13862,18 @@
                 host.invalidate(true);
             }
         }
+    }
 
+    /**
+     * Resuable callback for sending
+     * {@link AccessibilityEvent#TYPE_VIEW_SCROLLED} accessibility event.
+     */
+    private class SendViewScrolledAccessibilityEvent implements Runnable {
+        public volatile boolean mIsPending;
+
+        public void run() {
+            sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SCROLLED);
+            mIsPending = false;
+        }
     }
 }
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index e5d6d9f..2b692f3 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -136,13 +136,6 @@
     static final ArrayList<ComponentCallbacks> sConfigCallbacks
             = new ArrayList<ComponentCallbacks>();
 
-    /**
-     * Delay before dispatching an accessibility event that the window
-     * content has changed. The window content is considered changed
-     * after a layout pass.
-     */
-    private static final long SEND_WINDOW_CONTENT_CHANGED_DELAY_MILLIS = 500;
-
     long mLastTrackballTime = 0;
     final TrackballAxis mTrackballAxisX = new TrackballAxis();
     final TrackballAxis mTrackballAxisY = new TrackballAxis();
@@ -284,7 +277,7 @@
 
     AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager;
 
-    SendWindowContentChanged mSendWindowContentChanged;
+    SendWindowContentChangedAccessibilityEvent mSendWindowContentChangedAccessibilityEvent;
 
     private final int mDensity;
 
@@ -3692,14 +3685,19 @@
     /**
      * Post a callback to send a
      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
+     * This event is send at most once every
+     * {@link ViewConfiguration#getSendRecurringAccessibilityEventsInterval()}.
      */
     private void postSendWindowContentChangedCallback() {
-        if (mSendWindowContentChanged == null) {
-            mSendWindowContentChanged = new SendWindowContentChanged();
-        } else {
-            removeCallbacks(mSendWindowContentChanged);
+        if (mSendWindowContentChangedAccessibilityEvent == null) {
+            mSendWindowContentChangedAccessibilityEvent =
+                new SendWindowContentChangedAccessibilityEvent();
         }
-        postDelayed(mSendWindowContentChanged, SEND_WINDOW_CONTENT_CHANGED_DELAY_MILLIS);
+        if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) {
+            mSendWindowContentChangedAccessibilityEvent.mIsPending = true;
+            postDelayed(mSendWindowContentChangedAccessibilityEvent,
+                    ViewConfiguration.getSendRecurringAccessibilityEventsInterval());
+        }
     }
 
     /**
@@ -3707,8 +3705,8 @@
      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} event.
      */
     private void removeSendWindowContentChangedCallback() {
-        if (mSendWindowContentChanged != null) {
-            removeCallbacks(mSendWindowContentChanged);
+        if (mSendWindowContentChangedAccessibilityEvent != null) {
+            removeCallbacks(mSendWindowContentChangedAccessibilityEvent);
         }
     }
 
@@ -4634,10 +4632,19 @@
         }
     }
 
-    private class SendWindowContentChanged implements Runnable {
+    private class SendWindowContentChangedAccessibilityEvent implements Runnable {
+        public volatile boolean mIsPending;
+
         public void run() {
             if (mView != null) {
-                mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+                // Send the event directly since we do not want to append the
+                // source text because this is the text for the entire window
+                // and we just want to notify that the content has changed.
+                AccessibilityEvent event = AccessibilityEvent.obtain(
+                        AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
+                mView.onInitializeAccessibilityEvent(event);
+                AccessibilityManager.getInstance(mView.mContext).sendAccessibilityEvent(event);
+                mIsPending = false;
             }
         }
     }
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 5919150..f3a5050 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -176,6 +176,14 @@
     private static final int TOUCH_EXPLORATION_TAP_SLOP = 80;
 
     /**
+     * Delay before dispatching a recurring accessibility event in milliseconds.
+     * This delay guarantees that a recurring event will be send at most once
+     * during the {@link #SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS} time
+     * frame.
+     */
+    private static final long SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS = 400;
+
+    /**
      * The maximum size of View's drawing cache, expressed in bytes. This size
      * should be at least equal to the size of the screen in ARGB888 format.
      */
@@ -498,6 +506,19 @@
     }
 
     /**
+     * Interval for dispatching a recurring accessibility event in milliseconds.
+     * This interval guarantees that a recurring event will be send at most once
+     * during the {@link #getSendRecurringAccessibilityEventsInterval()} time frame.
+     *
+     * @return The delay in milliseconds.
+     *
+     * @hide
+     */
+    public static long getSendRecurringAccessibilityEventsInterval() {
+        return SEND_RECURRING_ACCESSIBILITY_EVENTS_INTERVAL_MILLIS;
+    }
+
+    /**
      * @return Distance a touch must be outside the bounds of a window for it
      * to be counted as outside the window for purposes of dismissing that
      * window.
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 30d6489..90452eb 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -56,76 +56,132 @@
  * <b>View clicked</b> - represents the event of clicking on a {@link android.view.View}
  * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
  * Type:{@link #TYPE_VIEW_CLICKED} <br>
- * Properties:
- * {@link #getClassName()},
- * {@link #getPackageName()},
- * {@link #getEventTime()},
- * {@link #getText()},
- * {@link #isChecked()},
- * {@link #isEnabled()},
- * {@link #isPassword()},
- * {@link #getItemCount()},
- * {@link #getCurrentItemIndex()}
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ *   <li>{@link #getText()} - The text of the source.</li>
+ *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
+ *   <li>{@link #isPassword()} - Whether the source is password.</li>
+ *   <li>{@link #isChecked()} - Whether the source is checked.</li>
+ * </ul>
  * <p>
  * <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View}
  * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
  * Type:{@link #TYPE_VIEW_LONG_CLICKED} <br>
- * Properties:
- * {@link #getClassName()},
- * {@link #getPackageName()},
- * {@link #getEventTime()},
- * {@link #getText()},
- * {@link #isChecked()},
- * {@link #isEnabled()},
- * {@link #isPassword()},
- * {@link #getItemCount()},
- * {@link #getCurrentItemIndex()}
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ *   <li>{@link #getText()} - The text of the source.</li>
+ *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
+ *   <li>{@link #isPassword()} - Whether the source is password.</li>
+ *   <li>{@link #isChecked()} - Whether the source is checked.</li>
+ * </ul>
  * <p>
  * <b>View selected</b> - represents the event of selecting an item usually in
  * the context of an {@link android.widget.AdapterView}. <br>
  * Type: {@link #TYPE_VIEW_SELECTED} <br>
- * Properties:
- * {@link #getClassName()},
- * {@link #getPackageName()},
- * {@link #getEventTime()},
- * {@link #getText()},
- * {@link #isChecked()},
- * {@link #isEnabled()},
- * {@link #isPassword()},
- * {@link #getItemCount()},
- * {@link #getCurrentItemIndex()}
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ *   <li>{@link #getText()} - The text of the source.</li>
+ *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
+ *   <li>{@link #isPassword()} - Whether the source is password.</li>
+ *   <li>{@link #isChecked()} - Whether the source is checked.</li>
+ *   <li>{@link #getItemCount()} -The number of selectable items of the source.</li>
+ *   <li>{@link #getCurrentItemIndex()} - The currently selected item index.</li>
+ * </ul>
+ * <p>
  * <p>
  * <b>View focused</b> - represents the event of focusing a
  * {@link android.view.View}. <br>
  * Type: {@link #TYPE_VIEW_FOCUSED} <br>
- * Properties:
- * {@link #getClassName()},
- * {@link #getPackageName()},
- * {@link #getEventTime()},
- * {@link #getText()},
- * {@link #isChecked()},
- * {@link #isEnabled()},
- * {@link #isPassword()},
- * {@link #getItemCount()},
- * {@link #getCurrentItemIndex()}
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ *   <li>{@link #getText()} - The text of the source.</li>
+ *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
+ *   <li>{@link #isPassword()} - Whether the source is password.</li>
+ *   <li>{@link #isChecked()} - Whether the source is checked.</li>
+ *   <li>{@link #getItemCount()} -The number of focusable items on the screen.</li>
+ *   <li>{@link #getCurrentItemIndex()} - The currently focused item index.</li>
+ * </ul>
  * <p>
  * <b>View text changed</b> - represents the event of changing the text of an
  * {@link android.widget.EditText}. <br>
  * Type: {@link #TYPE_VIEW_TEXT_CHANGED} <br>
- * Properties:
- * {@link #getClassName()},
- * {@link #getPackageName()},
- * {@link #getEventTime()},
- * {@link #getText()},
- * {@link #isChecked()},
- * {@link #isEnabled()},
- * {@link #isPassword()},
- * {@link #getItemCount()},
- * {@link #getCurrentItemIndex()},
- * {@link #getFromIndex()},
- * {@link #getAddedCount()},
- * {@link #getRemovedCount()},
- * {@link #getBeforeText()}
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ *   <li>{@link #getText()} - The text of the source.</li>
+ *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
+ *   <li>{@link #isPassword()} - Whether the source is password.</li>
+ *   <li>{@link #isChecked()} - Whether the source is checked.</li>
+ *   <li>{@link #getFromIndex()} - The text change start index.</li>
+ *   <li>{@link #getAddedCount()} - The number of added characters.</li>
+ *   <li>{@link #getRemovedCount()} - The number of removed characters.</li>
+ *   <li>{@link #getBeforeText()} - The text of the source before the change.</li>
+ * </ul>
+ * <p>
+ * <b>View text selection changed</b> - represents the event of changing the text
+ * selection of an {@link android.widget.EditText}.<br>
+ * Type: {@link #TYPE_VIEW_TEXT_SELECTION_CHANGED} <br>
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ *   <li>{@link #getText()} - The text of the source.</li>
+ *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
+ *   <li>{@link #isPassword()} - Whether the source is password.</li>
+ *   <li>{@link #getFromIndex()} - The selection start index.</li>
+ *   <li>{@link #getToIndex()} - The selection end index.</li>
+ *   <li>{@link #getItemCount()} - The length of the source text.</li>
+ * <ul>
+ * <p>
+ * <b>View scrolled</b> - represents the event of scrolling a view. If
+ * the source is a descendant of {@link android.widget.AdapterView} the
+ * scroll is reported in terms of visible items - the first visible item,
+ * the last visible item, and the total items - because the the source
+ * is unaware if its pixel size since its adapter is responsible for
+ * creating views. In all other cases the scroll is reported as the current
+ * scroll on the X and Y axis respectively plus the height of the source in
+ * pixels.<br>
+ * Type: {@link #TYPE_VIEW_SCROLLED} <br>
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ *   <li>{@link #getText()} - The text of the source.</li>
+ *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
+ *   <li>{@link #getScrollX())} - The horizontal offset of the source
+ *                                (without descendants of AdapterView)).</li>
+ *   <li>{@link #getScrollY())} - The vertical offset of the source
+ *                                (without descendants of AdapterView)).</li>
+ *   <li>{@link #getFromIndex()} - The index of the first visible item of the source
+ *                                 (for descendants of AdapterView).</li>
+ *   <li>{@link #getToIndex()} - The index of the last visible item of the source
+ *                               (for descendants of AdapterView).</li>
+ *   <li>{@link #getItemCount()} - The total items of the source (for descendants of AdapterView)
+ *                                 or the height of the source in pixels (all other cases).</li>
+ * <ul>
  * <p>
  * <b>TRANSITION TYPES</b> <br>
  * <p>
@@ -133,33 +189,40 @@
  * {@link android.widget.PopupWindow}, {@link android.view.Menu},
  * {@link android.app.Dialog}, etc. <br>
  * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br>
- * Properties:
- * {@link #getClassName()},
- * {@link #getPackageName()},
- * {@link #getEventTime()},
- * {@link #getText()}
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ *   <li>{@link #getText()} - The text of the source.</li>
+ * </ul>
  * <p>
  * <b>Window content changed</b> - represents the event of change in the
  * content of a window. This change can be adding/removing view, changing
  * a view size, etc.<br>
  * Type: {@link #TYPE_WINDOW_CONTENT_CHANGED} <br>
- * Properties:
- * {@link #getClassName()},
- * {@link #getPackageName()},
- * {@link #getEventTime()},
- * {@link #getText()}
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getSource()} - The source info (for registered clients).</li> *
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ * <ul>
  * <p>
  * <b>NOTIFICATION TYPES</b> <br>
  * <p>
  * <b>Notification state changed</b> - represents the event showing/hiding
  * {@link android.app.Notification}.
  * Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED} <br>
- * Properties:
- * {@link #getClassName()},
- * {@link #getPackageName()},
- * {@link #getEventTime()},
- * {@link #getText()}
- * {@link #getParcelableData()}
+ * Properties:</br>
+ * <ul>
+ *   <li>{@link #getClassName()} - The class name of the source.</li>
+ *   <li>{@link #getPackageName()} - The package name of the source.</li>
+ *   <li>{@link #getEventTime()}  - The event time.</li>
+ *   <li>{@link #getText()} - The text of the source.</li>
+ *   <li>{@link #getParcelableData()} - The posted {@link android.app.Notification}.</li>
+ * </ul>
  * <p>
  * <b>Security note</b>
  * <p>
@@ -258,6 +321,16 @@
     public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
 
     /**
+     * Represents the event of scrolling a view.
+     */
+    public static final int TYPE_VIEW_SCROLLED = 0x00001000;
+
+    /**
+     * Represents the event of changing the selection in an {@link android.widget.EditText}.
+     */
+    public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000;
+
+    /**
      * Mask for {@link AccessibilityEvent} all types.
      *
      * @see #TYPE_VIEW_CLICKED
@@ -564,6 +637,9 @@
         record.mCurrentItemIndex = parcel.readInt();
         record.mItemCount = parcel.readInt();
         record.mFromIndex = parcel.readInt();
+        record.mToIndex = parcel.readInt();
+        record.mScrollX = parcel.readInt();
+        record.mScrollY =  parcel.readInt();
         record.mAddedCount = parcel.readInt();
         record.mRemovedCount = parcel.readInt();
         record.mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
@@ -613,6 +689,9 @@
         parcel.writeInt(record.mCurrentItemIndex);
         parcel.writeInt(record.mItemCount);
         parcel.writeInt(record.mFromIndex);
+        parcel.writeInt(record.mToIndex);
+        parcel.writeInt(record.mScrollX);
+        parcel.writeInt(record.mScrollY);
         parcel.writeInt(record.mAddedCount);
         parcel.writeInt(record.mRemovedCount);
         TextUtils.writeToParcel(record.mClassName, parcel, flags);
@@ -657,8 +736,12 @@
                 builder.append("; IsPassword: " + record.isPassword());
                 builder.append("; IsChecked: " + record.isChecked());
                 builder.append("; IsFullScreen: " + record.isFullScreen());
+                builder.append("; Scrollable: " + record.isScrollable());
                 builder.append("; BeforeText: " + record.mBeforeText);
                 builder.append("; FromIndex: " + record.mFromIndex);
+                builder.append("; ToIndex: " + record.mToIndex);
+                builder.append("; ScrollX: " + record.mScrollX);
+                builder.append("; ScrollY: " + record.mScrollY);
                 builder.append("; AddedCount: " + record.mAddedCount);
                 builder.append("; RemovedCount: " + record.mRemovedCount);
                 builder.append("; ParcelableData: " + record.mParcelableData);
@@ -704,6 +787,10 @@
                 return "TYPE_TOUCH_EXPLORATION_GESTURE_END";
             case TYPE_WINDOW_CONTENT_CHANGED:
                 return "TYPE_WINDOW_CONTENT_CHANGED";
+            case TYPE_VIEW_TEXT_SELECTION_CHANGED:
+                return "TYPE_VIEW_TEXT_SELECTION_CHANGED";
+            case TYPE_VIEW_SCROLLED:
+                return "TYPE_VIEW_SCROLLED";
             default:
                 return null;
         }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 555667b..dbbe7be 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -83,6 +83,8 @@
 
     private static final int PROPERTY_PASSWORD = 0x00000100;
 
+    private static final int PROPERTY_SCROLLABLE = 0x00000200;
+
     // Readable representations - lazily initialized.
     private static SparseArray<String> sActionSymbolicNames;
 
@@ -570,6 +572,27 @@
     }
 
     /**
+     * Gets if the node is scrollable.
+     *
+     * @return True if the node is scrollable, false otherwise.
+     */
+    public boolean isScrollable() {
+        return getBooleanProperty(PROPERTY_SCROLLABLE);
+    }
+
+    /**
+     * Sets if the node is scrollable.
+     *
+     * @param scrollable True if the node is scrollable, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setScrollable(boolean scrollable) {
+        enforceNotSealed();
+        setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
+    }
+
+    /**
      * Gets the package this node comes from.
      *
      * @return The package name.
@@ -1017,6 +1040,7 @@
         builder.append("; longClickable: ").append(isLongClickable());
         builder.append("; enabled: ").append(isEnabled());
         builder.append("; password: ").append(isPassword());
+        builder.append("; scrollable: " + isScrollable());
 
         builder.append("; [");
 
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 9c495e21..836899f 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -40,6 +40,7 @@
     private static final int PROPERTY_ENABLED = 0x00000002;
     private static final int PROPERTY_PASSWORD = 0x00000004;
     private static final int PROPERTY_FULL_SCREEN = 0x00000080;
+    private static final int PROPERTY_SCROLLABLE = 0x00000100;
 
     // Housekeeping
     private static final int MAX_POOL_SIZE = 10;
@@ -54,6 +55,10 @@
     int mCurrentItemIndex;
     int mItemCount;
     int mFromIndex;
+    int mToIndex;
+    int mScrollX;
+    int mScrollY;
+
     int mAddedCount;
     int mRemovedCount;
     int mSourceViewId = View.NO_ID;
@@ -71,7 +76,6 @@
      * Hide constructor.
      */
     AccessibilityRecord() {
-
     }
 
     /**
@@ -85,6 +89,9 @@
         mCurrentItemIndex = record.mCurrentItemIndex;
         mItemCount = record.mItemCount;
         mFromIndex = record.mFromIndex;
+        mToIndex = record.mToIndex;
+        mScrollX = record.mScrollX;
+        mScrollY = record.mScrollY;
         mAddedCount = record.mAddedCount;
         mRemovedCount = record.mRemovedCount;
         mClassName = record.mClassName;
@@ -246,6 +253,27 @@
     }
 
     /**
+     * Gets if the source is scrollable.
+     *
+     * @return True if the source is scrollable, false otherwise.
+     */
+    public boolean isScrollable() {
+        return getBooleanProperty(PROPERTY_SCROLLABLE);
+    }
+
+    /**
+     * Sets if the source is scrollable.
+     *
+     * @param scrollable True if the source is scrollable, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setScrollable(boolean scrollable) {
+        enforceNotSealed();
+        setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
+    }
+
+    /**
      * Gets the number of items that can be visited.
      *
      * @return The number of items.
@@ -288,18 +316,24 @@
     }
 
     /**
-     * Gets the index of the first character of the changed sequence.
+     * Gets the index of the first character of the changed sequence,
+     * or the beginning of a text selection or the index of the first
+     * visible item when scrolling.
      *
-     * @return The index of the first character.
+     * @return The index of the first character or selection
+     *        start or the first visible item.
      */
     public int getFromIndex() {
         return mFromIndex;
     }
 
     /**
-     * Sets the index of the first character of the changed sequence.
+     * Sets the index of the first character of the changed sequence
+     * or the beginning of a text selection or the index of the first
+     * visible item when scrolling.
      *
-     * @param fromIndex The index of the first character.
+     * @param fromIndex The index of the first character or selection
+     *        start or the first visible item.
      *
      * @throws IllegalStateException If called from an AccessibilityService.
      */
@@ -309,6 +343,65 @@
     }
 
     /**
+     * Gets the index of text selection end or the index of the last
+     * visible item when scrolling.
+     *
+     * @return The index of selection end or last item index.
+     */
+    public int getToIndex() {
+        return mToIndex;
+    }
+
+    /**
+     * Sets the index of text selection end or the index of the last
+     * visible item when scrolling.
+     *
+     * @param toIndex The index of selection end or last item index.
+     */
+    public void setToIndex(int toIndex) {
+        enforceNotSealed();
+        mToIndex = toIndex;
+    }
+
+    /**
+     * Gets the scroll position of the source along the X axis.
+     *
+     * @return The scroll along the X axis.
+     */
+    public int getScrollX() {
+        return mScrollX;
+    }
+
+    /**
+     * Sets the scroll position of the source along the X axis.
+     *
+     * @param scrollX The scroll along the X axis.
+     */
+    public void setScrollX(int scrollX) {
+        enforceNotSealed();
+        mScrollX = scrollX;
+    }
+
+    /**
+     * Gets the scroll position of the source along the Y axis.
+     *
+     * @return The scroll along the Y axis.
+     */
+    public int getScrollY() {
+        return mScrollY;
+    }
+
+    /**
+     * Sets the scroll position of the source along the Y axis.
+     *
+     * @param scrollX The scroll along the Y axis.
+     */
+    public void setScrollY(int scrollY) {
+        enforceNotSealed();
+        mScrollY = scrollY;
+    }
+
+    /**
      * Gets the number of added characters.
      *
      * @return The number of added characters.
@@ -576,6 +669,9 @@
         mCurrentItemIndex = INVALID_POSITION;
         mItemCount = 0;
         mFromIndex = 0;
+        mToIndex = 0;
+        mScrollX = 0;
+        mScrollY = 0;
         mAddedCount = 0;
         mRemovedCount = 0;
         mClassName = null;
@@ -599,8 +695,12 @@
         builder.append("; IsPassword: " + getBooleanProperty(PROPERTY_PASSWORD));
         builder.append("; IsChecked: " + getBooleanProperty(PROPERTY_CHECKED));
         builder.append("; IsFullScreen: " + getBooleanProperty(PROPERTY_FULL_SCREEN));
+        builder.append("; Scrollable: " + getBooleanProperty(PROPERTY_SCROLLABLE));
         builder.append("; BeforeText: " + mBeforeText);
         builder.append("; FromIndex: " + mFromIndex);
+        builder.append("; ToIndex: " + mToIndex);
+        builder.append("; ScrollX: " + mScrollX);
+        builder.append("; ScrollY: " + mScrollY);
         builder.append("; AddedCount: " + mAddedCount);
         builder.append("; RemovedCount: " + mRemovedCount);
         builder.append("; ParcelableData: " + mParcelableData);
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 3fe8149..1449b18 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -55,6 +55,8 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputConnection;
@@ -1257,6 +1259,33 @@
         onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setScrollable(true);
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setScrollable(true);
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            event.setFromIndex(mFirstPosition);
+            event.setToIndex(mFirstPosition +  getChildCount());
+            event.setItemCount(mItemCount);
+        }
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        // Do not append text content to scroll events they are fired frequently
+        // and the client has already received another event type with the text.
+        if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            super.dispatchPopulateAccessibilityEvent(event);
+        }
+        return false;
+    }
+
     /**
      * Indicates whether the children's drawing cache is used during a scroll.
      * By default, the drawing cache is enabled but this will consume more memory.
diff --git a/core/java/android/widget/Gallery.java b/core/java/android/widget/Gallery.java
index ce76bee..0ffd087 100644
--- a/core/java/android/widget/Gallery.java
+++ b/core/java/android/widget/Gallery.java
@@ -34,6 +34,8 @@
 import android.view.ViewGroup;
 import android.view.SoundEffectConstants;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.Transformation;
 
 /**
@@ -344,7 +346,34 @@
     int getChildHeight(View child) {
         return child.getMeasuredHeight();
     }
-    
+
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setScrollable(true);
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setScrollable(true);
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            event.setFromIndex(mFirstPosition);
+            event.setToIndex(mFirstPosition +  getChildCount());
+            event.setItemCount(mItemCount);
+        }
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        // Do not append text content to scroll events they are fired frequently
+        // and the client has already received another event type with the text.
+        if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            super.dispatchPopulateAccessibilityEvent(event);
+        }
+        return false;
+    }
+
     /**
      * Tracks a motion scroll. In reality, this is used to do just about any
      * movement to items (touch scroll, arrow-key scroll, set an item as selected).
@@ -382,7 +411,9 @@
         mRecycler.clear();
         
         setSelectionToCenterChild();
-        
+
+        onScrollChanged(0, 0, 0, 0); // dummy values, View's implementation does not use these.
+
         invalidate();
     }
 
diff --git a/core/java/android/widget/HorizontalScrollView.java b/core/java/android/widget/HorizontalScrollView.java
index 17b3bda..7c9be1e 100644
--- a/core/java/android/widget/HorizontalScrollView.java
+++ b/core/java/android/widget/HorizontalScrollView.java
@@ -35,6 +35,8 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AnimationUtils;
 
 import java.util.List;
@@ -692,6 +694,28 @@
         awakenScrollBars();
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setScrollable(true);
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setScrollable(true);
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        // Do not append text content to scroll events they are fired frequently
+        // and the client has already received another event type with the text.
+        if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            super.dispatchPopulateAccessibilityEvent(event);
+        }
+        return false;
+    }
+
     private int getScrollRange() {
         int scrollRange = 0;
         if (getChildCount() > 0) {
diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java
index 27edb88..12775a4 100644
--- a/core/java/android/widget/ScrollView.java
+++ b/core/java/android/widget/ScrollView.java
@@ -36,6 +36,8 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewParent;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AnimationUtils;
 
 import java.util.List;
@@ -690,6 +692,28 @@
         awakenScrollBars();
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+        info.setScrollable(true);
+    }
+
+    @Override
+    public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        super.onInitializeAccessibilityEvent(event);
+        event.setScrollable(true);
+    }
+
+    @Override
+    public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
+        // Do not append text content to scroll events they are fired frequently
+        // and the client has already received another event type with the text.
+        if (event.getEventType() != AccessibilityEvent.TYPE_VIEW_SCROLLED) {
+            super.dispatchPopulateAccessibilityEvent(event);
+        }
+        return false;
+    }
+
     private int getScrollRange() {
         int scrollRange = 0;
         if (getChildCount() > 0) {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 17aea8b..85e7eec 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6834,9 +6834,9 @@
      * @param selEnd The new selection end location.
      */
     protected void onSelectionChanged(int selStart, int selEnd) {
-        // intentionally empty
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
     }
-    
+
     /**
      * Adds a TextWatcher to the list of those whose methods are called
      * whenever this TextView's text changes.
@@ -7922,6 +7922,12 @@
 
         final boolean isPassword = hasPasswordTransformationMethod();
         event.setPassword(isPassword);
+
+        if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED) {
+            event.setFromIndex(Selection.getSelectionStart(mText));
+            event.setToIndex(Selection.getSelectionEnd(mText));
+            event.setItemCount(mText.length());
+        }
     }
 
     @Override
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index f7974e9..c9ac57a 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -235,12 +235,13 @@
     <attr name="vmSafeMode" format="boolean" />
 
     <!-- <p>Flag indicating whether the application's rendering should be hardware
-         accelerated if possible. This flag is turned off by default, both for
-         applications and activities.</p>
+         accelerated if possible. This flag is turned on by default for applications
+         that are targeting {@link android.os.Build.VERSION_CODES#ICE_CREAM_SANDWICH}
+         or later.</p>
          <p>This flag can be set on the application and any activity declared
          in the manifest. When enabled for the application, each activity is
          automatically assumed to be hardware accelerated. This flag can be
-         overriden in the activity tags, either turning it off (if on for the
+         overridden in the activity tags, either turning it off (if on for the
          application) or on (if off for the application.)</p>
          <p>When this flag is turned on for an activity (either directly or via
          the application tag), every window created from the activity, including
diff --git a/include/camera/Camera.h b/include/camera/Camera.h
index 7106bfa..f701280 100644
--- a/include/camera/Camera.h
+++ b/include/camera/Camera.h
@@ -18,9 +18,11 @@
 #define ANDROID_HARDWARE_CAMERA_H
 
 #include <utils/Timers.h>
-#include <camera/ICameraClient.h>
 #include <gui/ISurfaceTexture.h>
 #include <system/camera.h>
+#include <camera/ICameraClient.h>
+#include <camera/ICameraRecordingProxy.h>
+#include <camera/ICameraRecordingProxyListener.h>
 
 namespace android {
 
@@ -70,7 +72,7 @@
     static  status_t    getCameraInfo(int cameraId,
                                       struct CameraInfo* cameraInfo);
     static  sp<Camera>  connect(int cameraId);
-                        ~Camera();
+            virtual     ~Camera();
             void        init();
 
             status_t    reconnect();
@@ -129,8 +131,11 @@
             status_t    storeMetaDataInBuffers(bool enabled);
 
             void        setListener(const sp<CameraListener>& listener);
+            void        setRecordingProxyListener(const sp<ICameraRecordingProxyListener>& listener);
             void        setPreviewCallbackFlags(int preview_callback_flag);
 
+            sp<ICameraRecordingProxy> getRecordingProxy();
+
     // ICameraClient interface
     virtual void        notifyCallback(int32_t msgType, int32_t ext, int32_t ext2);
     virtual void        dataCallback(int32_t msgType, const sp<IMemory>& dataPtr);
@@ -138,6 +143,20 @@
 
     sp<ICamera>         remote();
 
+    class RecordingProxy : public BnCameraRecordingProxy
+    {
+    public:
+        RecordingProxy(const sp<Camera>& camera);
+
+        // ICameraRecordingProxy interface
+        virtual status_t startRecording(const sp<ICameraRecordingProxyListener>& listener);
+        virtual void stopRecording();
+        virtual void releaseRecordingFrame(const sp<IMemory>& mem);
+
+    private:
+        sp<Camera>         mCamera;
+    };
+
 private:
                         Camera();
                         Camera(const Camera&);
@@ -162,12 +181,12 @@
             status_t            mStatus;
 
             sp<CameraListener>  mListener;
+            sp<ICameraRecordingProxyListener>  mRecordingProxyListener;
 
             friend class DeathNotifier;
 
             static  Mutex               mLock;
             static  sp<ICameraService>  mCameraService;
-
 };
 
 }; // namespace android
diff --git a/include/camera/ICameraRecordingProxy.h b/include/camera/ICameraRecordingProxy.h
new file mode 100644
index 0000000..2aac284
--- /dev/null
+++ b/include/camera/ICameraRecordingProxy.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_H
+#define ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_H
+
+#include <binder/IInterface.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class ICameraRecordingProxyListener;
+class IMemory;
+class Parcel;
+
+/*
+ * The purpose of ICameraRecordingProxy and ICameraRecordingProxyListener is to
+ * allow applications using the camera during recording.
+ *
+ * Camera service allows only one client at a time. Since camcorder application
+ * needs to own the camera to do things like zoom, the media recorder cannot
+ * access the camera directly during recording. So ICameraRecordingProxy is a
+ * proxy of ICamera, which allows the media recorder to start/stop the recording
+ * and release recording frames. ICameraRecordingProxyListener is an interface
+ * that allows the recorder to receive video frames during recording.
+ *
+ * ICameraRecordingProxy
+ *   startRecording()
+ *   stopRecording()
+ *   releaseRecordingFrame()
+ *
+ * ICameraRecordingProxyListener
+ *   dataCallbackTimestamp()
+
+ * The camcorder app opens the camera and starts the preview. The app passes
+ * ICamera and ICameraRecordingProxy to the media recorder by
+ * MediaRecorder::setCamera(). The recorder uses ICamera to setup the camera in
+ * MediaRecorder::start(). After setup, the recorder disconnects from camera
+ * service. The recorder calls ICameraRecordingProxy::startRecording() and
+ * passes a ICameraRecordingProxyListener to the app. The app connects back to
+ * camera service and starts the recording. The app owns the camera and can do
+ * things like zoom. The media recorder receives the video frames from the
+ * listener and releases them by ICameraRecordingProxy::releaseRecordingFrame.
+ * The recorder calls ICameraRecordingProxy::stopRecording() to stop the
+ * recording.
+ *
+ * The call sequences are as follows:
+ * 1. The app: Camera.unlock().
+ * 2. The app: MediaRecorder.setCamera().
+ * 3. Start recording
+ *    (1) The app: MediaRecorder.start().
+ *    (2) The recorder: ICamera.unlock() and ICamera.disconnect().
+ *    (3) The recorder: ICameraRecordingProxy.startRecording().
+ *    (4) The app: ICamera.reconnect().
+ *    (5) The app: ICamera.startRecording().
+ * 4. During recording
+ *    (1) The recorder: receive frames from ICameraRecordingProxyListener.dataCallbackTimestamp()
+ *    (2) The recorder: release frames by ICameraRecordingProxy.releaseRecordingFrame().
+ * 5. Stop recording
+ *    (1) The app: MediaRecorder.stop()
+ *    (2) The recorder: ICameraRecordingProxy.stopRecording().
+ *    (3) The app: ICamera.stopRecording().
+ */
+
+class ICameraRecordingProxy: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(CameraRecordingProxy);
+
+    virtual status_t        startRecording(const sp<ICameraRecordingProxyListener>& listener) = 0;
+    virtual void            stopRecording() = 0;
+    virtual void            releaseRecordingFrame(const sp<IMemory>& mem) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnCameraRecordingProxy: public BnInterface<ICameraRecordingProxy>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/camera/ICameraRecordingProxyListener.h b/include/camera/ICameraRecordingProxyListener.h
new file mode 100644
index 0000000..b6c0624
--- /dev/null
+++ b/include/camera/ICameraRecordingProxyListener.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_LISTENER_H
+#define ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_LISTENER_H
+
+#include <binder/IInterface.h>
+#include <stdint.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+class Parcel;
+class IMemory;
+
+class ICameraRecordingProxyListener: public IInterface
+{
+public:
+    DECLARE_META_INTERFACE(CameraRecordingProxyListener);
+
+    virtual void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType,
+                                       const sp<IMemory>& data) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnCameraRecordingProxyListener: public BnInterface<ICameraRecordingProxyListener>
+{
+public:
+    virtual status_t    onTransact( uint32_t code,
+                                    const Parcel& data,
+                                    Parcel* reply,
+                                    uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 28be7c1..a73267d 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -24,6 +24,7 @@
 
 class Surface;
 class ICamera;
+class ICameraRecordingProxy;
 class IMediaRecorderClient;
 
 class IMediaRecorder: public IInterface
@@ -31,28 +32,29 @@
 public:
     DECLARE_META_INTERFACE(MediaRecorder);
 
-    virtual	status_t		setCamera(const sp<ICamera>& camera) = 0;
-    virtual	status_t		setPreviewSurface(const sp<Surface>& surface) = 0;
-    virtual	status_t		setVideoSource(int vs) = 0;
-    virtual	status_t		setAudioSource(int as) = 0;
-    virtual	status_t		setOutputFormat(int of) = 0;
-    virtual	status_t		setVideoEncoder(int ve) = 0;
-    virtual	status_t		setAudioEncoder(int ae) = 0;
-    virtual	status_t		setOutputFile(const char* path) = 0;
-    virtual	status_t		setOutputFile(int fd, int64_t offset, int64_t length) = 0;
-    virtual	status_t		setOutputFileAuxiliary(int fd) = 0;
-    virtual	status_t		setVideoSize(int width, int height) = 0;
-    virtual	status_t		setVideoFrameRate(int frames_per_second) = 0;
-    virtual     status_t                setParameters(const String8& params) = 0;
-    virtual     status_t                setListener(const sp<IMediaRecorderClient>& listener) = 0;
-    virtual	status_t		prepare() = 0;
-    virtual	status_t		getMaxAmplitude(int* max) = 0;
-    virtual	status_t		start() = 0;
-    virtual	status_t		stop() = 0;
-    virtual	status_t		reset() = 0;
-    virtual status_t        init() = 0;
-    virtual status_t        close() = 0;
-    virtual	status_t		release() = 0;
+    virtual status_t setCamera(const sp<ICamera>& camera,
+                               const sp<ICameraRecordingProxy>& proxy) = 0;
+    virtual status_t setPreviewSurface(const sp<Surface>& surface) = 0;
+    virtual status_t setVideoSource(int vs) = 0;
+    virtual status_t setAudioSource(int as) = 0;
+    virtual status_t setOutputFormat(int of) = 0;
+    virtual status_t setVideoEncoder(int ve) = 0;
+    virtual status_t setAudioEncoder(int ae) = 0;
+    virtual status_t setOutputFile(const char* path) = 0;
+    virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0;
+    virtual status_t setOutputFileAuxiliary(int fd) = 0;
+    virtual status_t setVideoSize(int width, int height) = 0;
+    virtual status_t setVideoFrameRate(int frames_per_second) = 0;
+    virtual status_t setParameters(const String8& params) = 0;
+    virtual status_t setListener(const sp<IMediaRecorderClient>& listener) = 0;
+    virtual status_t prepare() = 0;
+    virtual status_t getMaxAmplitude(int* max) = 0;
+    virtual status_t start() = 0;
+    virtual status_t stop() = 0;
+    virtual status_t reset() = 0;
+    virtual status_t init() = 0;
+    virtual status_t close() = 0;
+    virtual status_t release() = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index 7e22a24..1c08969 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -24,6 +24,7 @@
 
 namespace android {
 
+class ICameraRecordingProxy;
 class Surface;
 
 struct MediaRecorderBase {
@@ -38,7 +39,8 @@
     virtual status_t setVideoEncoder(video_encoder ve) = 0;
     virtual status_t setVideoSize(int width, int height) = 0;
     virtual status_t setVideoFrameRate(int frames_per_second) = 0;
-    virtual status_t setCamera(const sp<ICamera>& camera) = 0;
+    virtual status_t setCamera(const sp<ICamera>& camera,
+                               const sp<ICameraRecordingProxy>& proxy) = 0;
     virtual status_t setPreviewSurface(const sp<Surface>& surface) = 0;
     virtual status_t setOutputFile(const char *path) = 0;
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length) = 0;
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 36bf34e..af12d3c 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -30,6 +30,7 @@
 class Surface;
 class IMediaRecorder;
 class ICamera;
+class ICameraRecordingProxy;
 
 typedef void (*media_completion_f)(status_t status, void *cookie);
 
@@ -202,7 +203,7 @@
 
     void        died();
     status_t    initCheck();
-    status_t    setCamera(const sp<ICamera>& camera);
+    status_t    setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy);
     status_t    setPreviewSurface(const sp<Surface>& surface);
     status_t    setVideoSource(int vs);
     status_t    setAudioSource(int as);
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index bb25bae3..80b7c1c 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -21,6 +21,7 @@
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaSource.h>
 #include <camera/ICamera.h>
+#include <camera/ICameraRecordingProxyListener.h>
 #include <camera/CameraParameters.h>
 #include <utils/List.h>
 #include <utils/RefBase.h>
@@ -68,6 +69,7 @@
      * @return NULL on error.
      */
     static CameraSource *CreateFromCamera(const sp<ICamera> &camera,
+                                          const sp<ICameraRecordingProxy> &proxy,
                                           int32_t cameraId,
                                           Size videoSize,
                                           int32_t frameRate,
@@ -111,6 +113,23 @@
     virtual void signalBufferReturned(MediaBuffer* buffer);
 
 protected:
+    class ProxyListener: public BnCameraRecordingProxyListener {
+    public:
+        ProxyListener(const sp<CameraSource>& source);
+        virtual void dataCallbackTimestamp(int64_t timestampUs, int32_t msgType,
+                const sp<IMemory> &data);
+
+    private:
+        sp<CameraSource> mSource;
+    };
+
+    // isBinderAlive needs linkToDeath to work.
+    class DeathNotifier: public IBinder::DeathRecipient {
+    public:
+        DeathNotifier() {}
+        virtual void binderDied(const wp<IBinder>& who);
+    };
+
     enum CameraFlags {
         FLAGS_SET_CAMERA = 1L << 0,
         FLAGS_HOT_CAMERA = 1L << 1,
@@ -123,6 +142,8 @@
     status_t mInitCheck;
 
     sp<Camera>   mCamera;
+    sp<ICameraRecordingProxy>   mCameraRecordingProxy;
+    sp<DeathNotifier> mDeathNotifier;
     sp<Surface>  mSurface;
     sp<MetaData> mMeta;
 
@@ -132,7 +153,8 @@
     bool mStarted;
     int32_t mNumFramesEncoded;
 
-    CameraSource(const sp<ICamera>& camera, int32_t cameraId,
+    CameraSource(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
+                 int32_t cameraId,
                  Size videoSize, int32_t frameRate,
                  const sp<Surface>& surface,
                  bool storeMetaDataInVideoBuffers);
@@ -172,10 +194,12 @@
     void releaseOneRecordingFrame(const sp<IMemory>& frame);
 
 
-    status_t init(const sp<ICamera>& camera, int32_t cameraId,
-                Size videoSize, int32_t frameRate,
-                bool storeMetaDataInVideoBuffers);
-    status_t isCameraAvailable(const sp<ICamera>& camera, int32_t cameraId);
+    status_t init(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
+                  int32_t cameraId, Size videoSize, int32_t frameRate,
+                  bool storeMetaDataInVideoBuffers);
+    status_t isCameraAvailable(const sp<ICamera>& camera,
+                               const sp<ICameraRecordingProxy>& proxy,
+                               int32_t cameraId);
     status_t isCameraColorFormatSupported(const CameraParameters& params);
     status_t configureCamera(CameraParameters* params,
                     int32_t width, int32_t height,
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h
index 0e5d534..f07ebba 100644
--- a/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/include/media/stagefright/CameraSourceTimeLapse.h
@@ -33,6 +33,7 @@
 public:
     static CameraSourceTimeLapse *CreateFromCamera(
         const sp<ICamera> &camera,
+        const sp<ICameraRecordingProxy> &proxy,
         int32_t cameraId,
         Size videoSize,
         int32_t videoFrameRate,
@@ -132,6 +133,7 @@
 
     CameraSourceTimeLapse(
         const sp<ICamera> &camera,
+        const sp<ICameraRecordingProxy> &proxy,
         int32_t cameraId,
         Size videoSize,
         int32_t videoFrameRate,
diff --git a/libs/camera/Android.mk b/libs/camera/Android.mk
index b17b3d2..dc00957 100644
--- a/libs/camera/Android.mk
+++ b/libs/camera/Android.mk
@@ -6,7 +6,9 @@
 	CameraParameters.cpp \
 	ICamera.cpp \
 	ICameraClient.cpp \
-	ICameraService.cpp
+	ICameraService.cpp \
+	ICameraRecordingProxy.cpp \
+	ICameraRecordingProxyListener.cpp
 
 LOCAL_SHARED_LIBRARIES := \
 	libcutils \
diff --git a/libs/camera/Camera.cpp b/libs/camera/Camera.cpp
index 5eb48da..3c00db5 100644
--- a/libs/camera/Camera.cpp
+++ b/libs/camera/Camera.cpp
@@ -19,11 +19,12 @@
 #define LOG_TAG "Camera"
 #include <utils/Log.h>
 #include <utils/threads.h>
-
+#include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/IMemory.h>
 
 #include <camera/Camera.h>
+#include <camera/ICameraRecordingProxyListener.h>
 #include <camera/ICameraService.h>
 
 #include <surfaceflinger/Surface.h>
@@ -236,6 +237,10 @@
 void Camera::stopRecording()
 {
     LOGV("stopRecording");
+    {
+        Mutex::Autolock _l(mLock);
+        mRecordingProxyListener.clear();
+    }
     sp <ICamera> c = mCamera;
     if (c == 0) return;
     c->stopRecording();
@@ -327,6 +332,12 @@
     mListener = listener;
 }
 
+void Camera::setRecordingProxyListener(const sp<ICameraRecordingProxyListener>& listener)
+{
+    Mutex::Autolock _l(mLock);
+    mRecordingProxyListener = listener;
+}
+
 void Camera::setPreviewCallbackFlags(int flag)
 {
     LOGV("setPreviewCallbackFlags");
@@ -364,6 +375,19 @@
 // callback from camera service when timestamped frame is ready
 void Camera::dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr)
 {
+    // If recording proxy listener is registered, forward the frame and return.
+    // The other listener (mListener) is ignored because the receiver needs to
+    // call releaseRecordingFrame.
+    sp<ICameraRecordingProxyListener> proxylistener;
+    {
+        Mutex::Autolock _l(mLock);
+        proxylistener = mRecordingProxyListener;
+    }
+    if (proxylistener != NULL) {
+        proxylistener->dataCallbackTimestamp(timestamp, msgType, dataPtr);
+        return;
+    }
+
     sp<CameraListener> listener;
     {
         Mutex::Autolock _l(mLock);
@@ -389,4 +413,34 @@
     LOGW("Camera server died!");
 }
 
+sp<ICameraRecordingProxy> Camera::getRecordingProxy() {
+    LOGV("getProxy");
+    return new RecordingProxy(this);
+}
+
+status_t Camera::RecordingProxy::startRecording(const sp<ICameraRecordingProxyListener>& listener)
+{
+    LOGV("RecordingProxy::startRecording");
+    mCamera->setRecordingProxyListener(listener);
+    mCamera->reconnect();
+    return mCamera->startRecording();
+}
+
+void Camera::RecordingProxy::stopRecording()
+{
+    LOGV("RecordingProxy::stopRecording");
+    mCamera->stopRecording();
+}
+
+void Camera::RecordingProxy::releaseRecordingFrame(const sp<IMemory>& mem)
+{
+    LOGV("RecordingProxy::releaseRecordingFrame");
+    mCamera->releaseRecordingFrame(mem);
+}
+
+Camera::RecordingProxy::RecordingProxy(const sp<Camera>& camera)
+{
+    mCamera = camera;
+}
+
 }; // namespace android
diff --git a/libs/camera/ICameraRecordingProxy.cpp b/libs/camera/ICameraRecordingProxy.cpp
new file mode 100644
index 0000000..64b6a5c
--- /dev/null
+++ b/libs/camera/ICameraRecordingProxy.cpp
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ICameraRecordingProxy"
+#include <camera/ICameraRecordingProxy.h>
+#include <camera/ICameraRecordingProxyListener.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <stdint.h>
+#include <utils/Log.h>
+
+namespace android {
+
+enum {
+    START_RECORDING = IBinder::FIRST_CALL_TRANSACTION,
+    STOP_RECORDING,
+    RELEASE_RECORDING_FRAME,
+};
+
+
+class BpCameraRecordingProxy: public BpInterface<ICameraRecordingProxy>
+{
+public:
+    BpCameraRecordingProxy(const sp<IBinder>& impl)
+        : BpInterface<ICameraRecordingProxy>(impl)
+    {
+    }
+
+    status_t startRecording(const sp<ICameraRecordingProxyListener>& listener)
+    {
+        LOGV("startRecording");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor());
+        data.writeStrongBinder(listener->asBinder());
+        remote()->transact(START_RECORDING, data, &reply);
+        return reply.readInt32();
+    }
+
+    void stopRecording()
+    {
+        LOGV("stopRecording");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor());
+        remote()->transact(STOP_RECORDING, data, &reply);
+    }
+
+    void releaseRecordingFrame(const sp<IMemory>& mem)
+    {
+        LOGV("releaseRecordingFrame");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor());
+        data.writeStrongBinder(mem->asBinder());
+        remote()->transact(RELEASE_RECORDING_FRAME, data, &reply);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(CameraRecordingProxy, "android.hardware.ICameraRecordingProxy");
+
+// ----------------------------------------------------------------------
+
+status_t BnCameraRecordingProxy::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case START_RECORDING: {
+            LOGV("START_RECORDING");
+            CHECK_INTERFACE(ICameraRecordingProxy, data, reply);
+            sp<ICameraRecordingProxyListener> listener =
+                interface_cast<ICameraRecordingProxyListener>(data.readStrongBinder());
+            reply->writeInt32(startRecording(listener));
+            return NO_ERROR;
+        } break;
+        case STOP_RECORDING: {
+            LOGV("STOP_RECORDING");
+            CHECK_INTERFACE(ICameraRecordingProxy, data, reply);
+            stopRecording();
+            return NO_ERROR;
+        } break;
+        case RELEASE_RECORDING_FRAME: {
+            LOGV("RELEASE_RECORDING_FRAME");
+            CHECK_INTERFACE(ICameraRecordingProxy, data, reply);
+            sp<IMemory> mem = interface_cast<IMemory>(data.readStrongBinder());
+            releaseRecordingFrame(mem);
+            return NO_ERROR;
+        } break;
+
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/libs/camera/ICameraRecordingProxyListener.cpp b/libs/camera/ICameraRecordingProxyListener.cpp
new file mode 100644
index 0000000..f8cece5
--- /dev/null
+++ b/libs/camera/ICameraRecordingProxyListener.cpp
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ICameraRecordingProxyListener"
+#include <camera/ICameraRecordingProxyListener.h>
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <utils/Log.h>
+
+namespace android {
+
+enum {
+    DATA_CALLBACK_TIMESTAMP = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpCameraRecordingProxyListener: public BpInterface<ICameraRecordingProxyListener>
+{
+public:
+    BpCameraRecordingProxyListener(const sp<IBinder>& impl)
+        : BpInterface<ICameraRecordingProxyListener>(impl)
+    {
+    }
+
+    void dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& imageData)
+    {
+        LOGV("dataCallback");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraRecordingProxyListener::getInterfaceDescriptor());
+        data.writeInt64(timestamp);
+        data.writeInt32(msgType);
+        data.writeStrongBinder(imageData->asBinder());
+        remote()->transact(DATA_CALLBACK_TIMESTAMP, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(CameraRecordingProxyListener, "android.hardware.ICameraRecordingProxyListener");
+
+// ----------------------------------------------------------------------
+
+status_t BnCameraRecordingProxyListener::onTransact(
+    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+    switch(code) {
+        case DATA_CALLBACK_TIMESTAMP: {
+            LOGV("DATA_CALLBACK_TIMESTAMP");
+            CHECK_INTERFACE(ICameraRecordingProxyListener, data, reply);
+            nsecs_t timestamp = data.readInt64();
+            int32_t msgType = data.readInt32();
+            sp<IMemory> imageData = interface_cast<IMemory>(data.readStrongBinder());
+            dataCallbackTimestamp(timestamp, msgType, imageData);
+            return NO_ERROR;
+        } break;
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index dd45111..6f42596 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -16,17 +16,16 @@
 
 package android.media;
 
-import android.media.CamcorderProfile;
 import android.hardware.Camera;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
 import android.util.Log;
 import android.view.Surface;
-import java.io.IOException;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
+
 import java.io.FileDescriptor;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.lang.ref.WeakReference;
 
 /**
@@ -112,7 +111,8 @@
     /**
      * Sets a Camera to use for recording. Use this function to switch
      * quickly between preview and capture mode without a teardown of
-     * the camera object. Must call before prepare().
+     * the camera object. {@link android.hardware.Camera#unlock()} should be
+     * called before this. Must call before prepare().
      *
      * @param c the Camera to use for recording
      */
@@ -718,6 +718,11 @@
      * Begins capturing and encoding data to the file specified with
      * setOutputFile(). Call this after prepare().
      *
+     * <p>Since API level 13, if applications set a camera via
+     * {@link #setCamera(Camera)}, the apps can use the camera after this method
+     * call. The apps do not need to lock the camera again. The apps should not
+     * start another recording session during recording.
+     *
      * @throws IllegalStateException if it is called before
      * prepare().
      */
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index 2f7d7ee..12391c8 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -158,7 +158,7 @@
     }
     sp<Camera> c = get_native_camera(env, camera, NULL);
     sp<MediaRecorder> mr = getMediaRecorder(env, thiz);
-    process_media_recorder_call(env, mr->setCamera(c->remote()),
+    process_media_recorder_call(env, mr->setCamera(c->remote(), c->getRecordingProxy()),
             "java/lang/RuntimeException", "setCamera failed.");
 }
 
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 59cd1b7..a44ef5a 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -60,12 +60,13 @@
     {
     }
 
-    status_t setCamera(const sp<ICamera>& camera)
+    status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy)
     {
-        LOGV("setCamera(%p)", camera.get());
+        LOGV("setCamera(%p,%p)", camera.get(), proxy.get());
         Parcel data, reply;
         data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
         data.writeStrongBinder(camera->asBinder());
+        data.writeStrongBinder(proxy->asBinder());
         remote()->transact(SET_CAMERA, data, &reply);
         return reply.readInt32();
     }
@@ -434,7 +435,9 @@
             LOGV("SET_CAMERA");
             CHECK_INTERFACE(IMediaRecorder, data, reply);
             sp<ICamera> camera = interface_cast<ICamera>(data.readStrongBinder());
-            reply->writeInt32(setCamera(camera));
+            sp<ICameraRecordingProxy> proxy =
+                interface_cast<ICameraRecordingProxy>(data.readStrongBinder());
+            reply->writeInt32(setCamera(camera, proxy));
             return NO_ERROR;
         } break;
         default:
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 0100a17..9e4edd0 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -28,9 +28,9 @@
 
 namespace android {
 
-status_t MediaRecorder::setCamera(const sp<ICamera>& camera)
+status_t MediaRecorder::setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy)
 {
-    LOGV("setCamera(%p)", camera.get());
+    LOGV("setCamera(%p,%p)", camera.get(), proxy.get());
     if(mMediaRecorder == NULL) {
         LOGE("media recorder is not initialized yet");
         return INVALID_OPERATION;
@@ -40,7 +40,7 @@
         return INVALID_OPERATION;
     }
 
-    status_t ret = mMediaRecorder->setCamera(camera);
+    status_t ret = mMediaRecorder->setCamera(camera, proxy);
     if (OK != ret) {
         LOGV("setCamera failed: %d", ret);
         mCurrentState = MEDIA_RECORDER_ERROR;
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 29cc019..115db1a 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -57,7 +57,8 @@
     return ok;
 }
 
-status_t MediaRecorderClient::setCamera(const sp<ICamera>& camera)
+status_t MediaRecorderClient::setCamera(const sp<ICamera>& camera,
+                                        const sp<ICameraRecordingProxy>& proxy)
 {
     LOGV("setCamera");
     Mutex::Autolock lock(mLock);
@@ -65,7 +66,7 @@
         LOGE("recorder is not initialized");
         return NO_INIT;
     }
-    return mRecorder->setCamera(camera);
+    return mRecorder->setCamera(camera, proxy);
 }
 
 status_t MediaRecorderClient::setPreviewSurface(const sp<Surface>& surface)
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index fded98e..bbca529 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -24,11 +24,13 @@
 
 class MediaRecorderBase;
 class MediaPlayerService;
+class ICameraRecordingProxy;
 
 class MediaRecorderClient : public BnMediaRecorder
 {
 public:
-    virtual     status_t        setCamera(const sp<ICamera>& camera);
+    virtual     status_t        setCamera(const sp<ICamera>& camera,
+                                          const sp<ICameraRecordingProxy>& proxy);
     virtual     status_t        setPreviewSurface(const sp<Surface>& surface);
     virtual     status_t        setVideoSource(int vs);
     virtual     status_t        setAudioSource(int as);
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 978571c..b003476 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -198,14 +198,20 @@
     return OK;
 }
 
-status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera) {
+status_t StagefrightRecorder::setCamera(const sp<ICamera> &camera,
+                                        const sp<ICameraRecordingProxy> &proxy) {
     LOGV("setCamera");
     if (camera == 0) {
         LOGE("camera is NULL");
         return BAD_VALUE;
     }
+    if (proxy == 0) {
+        LOGE("camera proxy is NULL");
+        return BAD_VALUE;
+    }
 
     mCamera = camera;
+    mCameraProxy = proxy;
     return OK;
 }
 
@@ -1235,15 +1241,17 @@
     videoSize.height = mVideoHeight;
     if (mCaptureTimeLapse) {
         mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
-                mCamera, mCameraId,
+                mCamera, mCameraProxy, mCameraId,
                 videoSize, mFrameRate, mPreviewSurface,
                 mTimeBetweenTimeLapseFrameCaptureUs);
         *cameraSource = mCameraSourceTimeLapse;
     } else {
         *cameraSource = CameraSource::CreateFromCamera(
-                mCamera, mCameraId, videoSize, mFrameRate,
+                mCamera, mCameraProxy, mCameraId, videoSize, mFrameRate,
                 mPreviewSurface, true /*storeMetaDataInVideoBuffers*/);
     }
+    mCamera.clear();
+    mCameraProxy.clear();
     if (*cameraSource == NULL) {
         return UNKNOWN_ERROR;
     }
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index aa67aa7..cb9c406 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -27,6 +27,7 @@
 namespace android {
 
 class Camera;
+class ICameraRecordingProxy;
 class CameraSource;
 class CameraSourceTimeLapse;
 class MediaSourceSplitter;
@@ -48,7 +49,7 @@
     virtual status_t setVideoEncoder(video_encoder ve);
     virtual status_t setVideoSize(int width, int height);
     virtual status_t setVideoFrameRate(int frames_per_second);
-    virtual status_t setCamera(const sp<ICamera>& camera);
+    virtual status_t setCamera(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy);
     virtual status_t setPreviewSurface(const sp<Surface>& surface);
     virtual status_t setOutputFile(const char *path);
     virtual status_t setOutputFile(int fd, int64_t offset, int64_t length);
@@ -66,6 +67,7 @@
 
 private:
     sp<ICamera> mCamera;
+    sp<ICameraRecordingProxy> mCameraProxy;
     sp<Surface> mPreviewSurface;
     sp<IMediaRecorderClient> mListener;
     sp<MediaWriter> mWriter, mWriterAux;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 61bb2a8..bdffce9 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -115,19 +115,20 @@
     size.height = -1;
 
     sp<ICamera> camera;
-    return new CameraSource(camera, 0, size, -1, NULL, false);
+    return new CameraSource(camera, NULL, 0, size, -1, NULL, false);
 }
 
 // static
 CameraSource *CameraSource::CreateFromCamera(
     const sp<ICamera>& camera,
+    const sp<ICameraRecordingProxy>& proxy,
     int32_t cameraId,
     Size videoSize,
     int32_t frameRate,
     const sp<Surface>& surface,
     bool storeMetaDataInVideoBuffers) {
 
-    CameraSource *source = new CameraSource(camera, cameraId,
+    CameraSource *source = new CameraSource(camera, proxy, cameraId,
                     videoSize, frameRate, surface,
                     storeMetaDataInVideoBuffers);
     return source;
@@ -135,6 +136,7 @@
 
 CameraSource::CameraSource(
     const sp<ICamera>& camera,
+    const sp<ICameraRecordingProxy>& proxy,
     int32_t cameraId,
     Size videoSize,
     int32_t frameRate,
@@ -153,11 +155,10 @@
       mNumGlitches(0),
       mGlitchDurationThresholdUs(200000),
       mCollectStats(false) {
-
     mVideoSize.width  = -1;
     mVideoSize.height = -1;
 
-    mInitCheck = init(camera, cameraId,
+    mInitCheck = init(camera, proxy, cameraId,
                     videoSize, frameRate,
                     storeMetaDataInVideoBuffers);
 }
@@ -167,24 +168,32 @@
 }
 
 status_t CameraSource::isCameraAvailable(
-    const sp<ICamera>& camera, int32_t cameraId) {
+    const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
+    int32_t cameraId) {
 
     if (camera == 0) {
         mCamera = Camera::connect(cameraId);
+        if (mCamera == 0) return -EBUSY;
+        // If proxy is not passed in by applications, still use the proxy of
+        // our own Camera to simplify the code.
+        mCameraRecordingProxy = mCamera->getRecordingProxy();
         mCameraFlags &= ~FLAGS_HOT_CAMERA;
     } else {
+        // We get the proxy from Camera, not ICamera. We need to get the proxy
+        // to the remote Camera owned by the application. Here mCamera is a
+        // local Camera object created by us. We cannot use the proxy from
+        // mCamera here.
         mCamera = Camera::create(camera);
+        if (mCamera == 0) return -EBUSY;
+        mCameraRecordingProxy = proxy;
         mCameraFlags |= FLAGS_HOT_CAMERA;
     }
 
-    // Is camera available?
-    if (mCamera == 0) {
-        LOGE("Camera connection could not be established.");
-        return -EBUSY;
-    }
-    if (!(mCameraFlags & FLAGS_HOT_CAMERA)) {
-        mCamera->lock();
-    }
+    mCamera->lock();
+    mDeathNotifier = new DeathNotifier();
+    // isBinderAlive needs linkToDeath to work.
+    mCameraRecordingProxy->asBinder()->linkToDeath(mDeathNotifier);
+
     return OK;
 }
 
@@ -447,6 +456,7 @@
  */
 status_t CameraSource::init(
         const sp<ICamera>& camera,
+        const sp<ICameraRecordingProxy>& proxy,
         int32_t cameraId,
         Size videoSize,
         int32_t frameRate,
@@ -455,7 +465,8 @@
     status_t err = OK;
     int64_t token = IPCThreadState::self()->clearCallingIdentity();
 
-    if ((err  = isCameraAvailable(camera, cameraId)) != OK) {
+    if ((err = isCameraAvailable(camera, proxy, cameraId)) != OK) {
+        LOGE("Camera connection could not be established.");
         return err;
     }
     CameraParameters params(mCamera->getParameters());
@@ -521,8 +532,14 @@
 }
 
 void CameraSource::startCameraRecording() {
-    CHECK_EQ(OK, mCamera->startRecording());
-    CHECK(mCamera->recordingEnabled());
+    // Reset the identity to the current thread because media server owns the
+    // camera and recording is started by the applications. The applications
+    // will connect to the camera in ICameraRecordingProxy::startRecording.
+    int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    mCamera->unlock();
+    mCamera.clear();
+    IPCThreadState::self()->restoreCallingIdentity(token);
+    CHECK_EQ(OK, mCameraRecordingProxy->startRecording(new ProxyListener(this)));
 }
 
 status_t CameraSource::start(MetaData *meta) {
@@ -544,20 +561,14 @@
         mStartTimeUs = startTimeUs;
     }
 
-    // Call setListener first before calling startCameraRecording()
-    // to avoid recording frames being dropped.
-    int64_t token = IPCThreadState::self()->clearCallingIdentity();
-    mCamera->setListener(new CameraSourceListener(this));
     startCameraRecording();
-    IPCThreadState::self()->restoreCallingIdentity(token);
 
     mStarted = true;
     return OK;
 }
 
 void CameraSource::stopCameraRecording() {
-    mCamera->setListener(NULL);
-    mCamera->stopRecording();
+    mCameraRecordingProxy->stopRecording();
 }
 
 void CameraSource::releaseCamera() {
@@ -566,9 +577,9 @@
         LOGV("Camera was cold when we started, stopping preview");
         mCamera->stopPreview();
     }
-    mCamera->unlock();
     mCamera.clear();
-    mCamera = 0;
+    mCameraRecordingProxy->asBinder()->unlinkToDeath(mDeathNotifier);
+    mCameraRecordingProxy.clear();
     mCameraFlags = 0;
 }
 
@@ -578,7 +589,6 @@
     mStarted = false;
     mFrameAvailableCondition.signal();
 
-    int64_t token = IPCThreadState::self()->clearCallingIdentity();
     releaseQueuedFrames();
     while (!mFramesBeingEncoded.empty()) {
         if (NO_ERROR !=
@@ -589,7 +599,6 @@
     }
     stopCameraRecording();
     releaseCamera();
-    IPCThreadState::self()->restoreCallingIdentity(token);
 
     if (mCollectStats) {
         LOGI("Frames received/encoded/dropped: %d/%d/%d in %lld us",
@@ -607,8 +616,8 @@
 }
 
 void CameraSource::releaseRecordingFrame(const sp<IMemory>& frame) {
-    if (mCamera != NULL) {
-        mCamera->releaseRecordingFrame(frame);
+    if (mCameraRecordingProxy != NULL) {
+        mCameraRecordingProxy->releaseRecordingFrame(frame);
     }
 }
 
@@ -627,9 +636,7 @@
 }
 
 void CameraSource::releaseOneRecordingFrame(const sp<IMemory>& frame) {
-    int64_t token = IPCThreadState::self()->clearCallingIdentity();
     releaseRecordingFrame(frame);
-    IPCThreadState::self()->restoreCallingIdentity(token);
 }
 
 void CameraSource::signalBufferReturned(MediaBuffer *buffer) {
@@ -669,7 +676,11 @@
         Mutex::Autolock autoLock(mLock);
         while (mStarted && mFramesReceived.empty()) {
             if (NO_ERROR !=
-                mFrameAvailableCondition.waitRelative(mLock, 3000000000LL)) {
+                mFrameAvailableCondition.waitRelative(mLock, 1000000000LL)) {
+                if (!mCameraRecordingProxy->asBinder()->isBinderAlive()) {
+                    LOGW("camera recording proxy is gone");
+                    return ERROR_END_OF_STREAM;
+                }
                 LOGW("Timed out waiting for incoming camera video frames: %lld us",
                     mLastFrameTimestampUs);
             }
@@ -745,4 +756,17 @@
     return mIsMetaDataStoredInVideoBuffers;
 }
 
+CameraSource::ProxyListener::ProxyListener(const sp<CameraSource>& source) {
+    mSource = source;
+}
+
+void CameraSource::ProxyListener::dataCallbackTimestamp(
+        nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) {
+    mSource->dataCallbackTimestamp(timestamp / 1000, msgType, dataPtr);
+}
+
+void CameraSource::DeathNotifier::binderDied(const wp<IBinder>& who) {
+    LOGI("Camera recording proxy died");
+}
+
 }  // namespace android
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index cc22574..fe78c46 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -39,6 +39,7 @@
 // static
 CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera(
         const sp<ICamera> &camera,
+        const sp<ICameraRecordingProxy> &proxy,
         int32_t cameraId,
         Size videoSize,
         int32_t videoFrameRate,
@@ -46,7 +47,7 @@
         int64_t timeBetweenTimeLapseFrameCaptureUs) {
 
     CameraSourceTimeLapse *source = new
-            CameraSourceTimeLapse(camera, cameraId,
+            CameraSourceTimeLapse(camera, proxy, cameraId,
                 videoSize, videoFrameRate, surface,
                 timeBetweenTimeLapseFrameCaptureUs);
 
@@ -61,12 +62,13 @@
 
 CameraSourceTimeLapse::CameraSourceTimeLapse(
         const sp<ICamera>& camera,
+        const sp<ICameraRecordingProxy>& proxy,
         int32_t cameraId,
         Size videoSize,
         int32_t videoFrameRate,
         const sp<Surface>& surface,
         int64_t timeBetweenTimeLapseFrameCaptureUs)
-    : CameraSource(camera, cameraId, videoSize, videoFrameRate, surface, true),
+    : CameraSource(camera, proxy, cameraId, videoSize, videoFrameRate, surface, true),
       mTimeBetweenTimeLapseFrameCaptureUs(timeBetweenTimeLapseFrameCaptureUs),
       mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate),
       mLastTimeLapseFrameRealTimestampUs(0),
@@ -315,7 +317,7 @@
         pthread_attr_destroy(&attr);
     } else {
         LOGV("start time lapse recording using video camera");
-        CHECK_EQ(OK, mCamera->startRecording());
+        CameraSource::startCameraRecording();
     }
 }
 
@@ -337,8 +339,7 @@
         // play the recording sound.
         mCamera->sendCommand(CAMERA_CMD_PLAY_RECORDING_SOUND, 0, 0);
     } else {
-        mCamera->setListener(NULL);
-        mCamera->stopRecording();
+        CameraSource::stopCameraRecording();
     }
     if (mLastReadBufferCopy) {
         mLastReadBufferCopy->release();
@@ -347,9 +348,8 @@
 }
 
 void CameraSourceTimeLapse::releaseRecordingFrame(const sp<IMemory>& frame) {
-    if (!mUseStillCameraForTimeLapse &&
-        mCamera != NULL) {
-        mCamera->releaseRecordingFrame(frame);
+    if (!mUseStillCameraForTimeLapse) {
+        CameraSource::releaseRecordingFrame(frame);
     }
 }
 
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 314425f..cd97302 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -4216,6 +4216,10 @@
                             mNode, OMX_IndexConfigCommonOutputCrop,
                             &rect, sizeof(rect));
 
+                CODEC_LOGI(
+                        "video dimensions are %ld x %ld",
+                        video_def->nFrameWidth, video_def->nFrameHeight);
+
                 if (err == OK) {
                     CHECK_GE(rect.nLeft, 0);
                     CHECK_GE(rect.nTop, 0);
@@ -4230,6 +4234,10 @@
                             rect.nTop,
                             rect.nLeft + rect.nWidth - 1,
                             rect.nTop + rect.nHeight - 1);
+
+                    CODEC_LOGI(
+                            "Crop rect is %ld x %ld @ (%ld, %ld)",
+                            rect.nWidth, rect.nHeight, rect.nLeft, rect.nTop);
                 } else {
                     mOutputFormat->setRect(
                             kKeyCropRect,
@@ -4238,7 +4246,6 @@
                             video_def->nFrameHeight - 1);
                 }
             }
-
             break;
         }
 
diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
index 830d2e0..ec7bd1c 100644
--- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
+++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
@@ -139,7 +139,8 @@
 }
 
 status_t SoftAVC::initDecoder() {
-    if (H264SwDecInit(&mHandle, 1) == H264SWDEC_OK) {
+    // Force decoder to output buffers in display order.
+    if (H264SwDecInit(&mHandle, 0) == H264SWDEC_OK) {
         return OK;
     }
     return UNKNOWN_ERROR;
diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
index 3439efd..1cc85e8 100644
--- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
+++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
@@ -52,7 +52,7 @@
         kInputPortIndex   = 0,
         kOutputPortIndex  = 1,
         kNumInputBuffers  = 8,
-        kNumOutputBuffers = 16,
+        kNumOutputBuffers = 2,
     };
 
     enum EOSStatus {
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index fd933cc..5cc3f78 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ColorConverter"
+#include <utils/Log.h>
+
 #include <media/stagefright/ColorConverter.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaErrors.h>
@@ -424,61 +428,30 @@
 
 status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
         const BitmapParams &src, const BitmapParams &dst) {
-
-/*
-The TIYUV420PackedSemiPlanar format is same as YUV420PackedSemiPlanar but with
-additional padding as shown in the diagram below. The padded width and padded
-height is different for different compression formats and it is read from the
-codec. In this color conversion routine, the padded resolution is obtained from
-src bitmap.
-
- ------------------------------------
-|                                    |
-|                                    |
-|      -------------------------     |
-|     |                         |    |
-|     |                         |    |
-|     |          Y DATA         |    |
-|     |                         |    |
-|     |                         |    |
-|     |                         |    |
-|      -------------------------     |
-|                                    |
-|      ------------                  |
-|     |            |                 |
-|     |            |                 |
-|     |  UV DATA   |                 |
-|     |            |                 |
-|     |            |                 |
-|     |            |                 |
-|      ------------                  |
-|                                    |
-|                                    |
- ------------------------------------
-*/
-
-    LOGV("src.mCropLeft = %d src.mCropTop =%d src.mWidth = %d src.mHeight = %d"
-        " dst.mWidth = %d dst.mHeight = %d", src.mCropLeft , src.mCropTop,
-        src.mWidth, src.mHeight, dst.mWidth, dst.mHeight);
-
-    size_t offset = (src.mWidth * src.mCropTop) + src.mCropLeft;
-
     uint8_t *kAdjustedClip = initClip();
 
-    uint32_t *dst_ptr = (uint32_t *)dst.mBits;
+    if (!((dst.mWidth & 3) == 0
+            && (src.mCropLeft & 1) == 0
+            && src.cropWidth() == dst.cropWidth()
+            && src.cropHeight() == dst.cropHeight())) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    uint32_t *dst_ptr = (uint32_t *)dst.mBits
+        + (dst.mCropTop * dst.mWidth + dst.mCropLeft) / 2;
+
     const uint8_t *src_y = (const uint8_t *)src.mBits;
-    const uint8_t *src_u = (const uint8_t *)(src_y-offset) + (src.mWidth * src.mHeight);
-    src_u += ( ( src.mWidth * (src.mCropTop/2) ) + src.mCropLeft );
-    const uint8_t *src_v = src_u + 1;
 
-    for (size_t y = 0; y < dst.mHeight; ++y) {
-        for (size_t x = 0; x < dst.mWidth; x += 2) {
+    const uint8_t *src_u =
+        (const uint8_t *)src_y + src.mWidth * (src.mHeight - src.mCropTop / 2);
 
-            signed y1 = (signed)src_y[x] - 16;    //Y pixel
-            signed y2 = (signed)src_y[x + 1] - 16; //2nd Y pixel
+    for (size_t y = 0; y < src.cropHeight(); ++y) {
+        for (size_t x = 0; x < src.cropWidth(); x += 2) {
+            signed y1 = (signed)src_y[x] - 16;
+            signed y2 = (signed)src_y[x + 1] - 16;
 
-            signed u = (signed)src_u[x & ~1] - 128;   //U component
-            signed v = (signed)src_u[(x & ~1) + 1] - 128; //V component
+            signed u = (signed)src_u[x & ~1] - 128;
+            signed v = (signed)src_u[(x & ~1) + 1] - 128;
 
             signed u_b = u * 517;
             signed u_g = -u * 100;
@@ -502,19 +475,21 @@
 
             uint32_t rgb2 =
                 ((kAdjustedClip[r2] >> 3) << 11)
-                | ((kAdjustedClip[g1] >> 2) << 5)
-                | (kAdjustedClip[b1] >> 3);
+                | ((kAdjustedClip[g2] >> 2) << 5)
+                | (kAdjustedClip[b2] >> 3);
 
             dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
         }
 
-        src_y += src.mWidth; //increment Y-pixel line
-        if (y&1) {
-          src_u += src.mWidth; //increment U-V line
+        src_y += src.mWidth;
+
+        if (y & 1) {
+            src_u += src.mWidth;
         }
 
         dst_ptr += dst.mWidth / 2;
     }
+
     return OK;
 }
 
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index a4e8ee4..a4ca32d 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -50,6 +50,9 @@
         mCropBottom = mHeight - 1;
     }
 
+    mCropWidth = mCropRight - mCropLeft + 1;
+    mCropHeight = mCropBottom - mCropTop + 1;
+
     int32_t rotationDegrees;
     if (!meta->findInt32(kKeyRotation, &rotationDegrees)) {
         rotationDegrees = 0;
@@ -60,17 +63,18 @@
 
     switch (mColorFormat) {
         case OMX_COLOR_FormatYUV420Planar:
+        case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
         {
             halFormat = HAL_PIXEL_FORMAT_YV12;
-            bufWidth = (mWidth + 1) & ~1;
-            bufHeight = (mHeight + 1) & ~1;
+            bufWidth = (mCropWidth + 1) & ~1;
+            bufHeight = (mCropHeight + 1) & ~1;
             break;
         }
 
         default:
             halFormat = HAL_PIXEL_FORMAT_RGB_565;
-            bufWidth = mWidth;
-            bufHeight = mHeight;
+            bufWidth = mCropWidth;
+            bufHeight = mCropHeight;
 
             mConverter = new ColorConverter(
                     mColorFormat, OMX_COLOR_Format16bitRGB565);
@@ -79,8 +83,8 @@
     }
 
     CHECK(mNativeWindow != NULL);
-    CHECK(mWidth > 0);
-    CHECK(mHeight > 0);
+    CHECK(mCropWidth > 0);
+    CHECK(mCropHeight > 0);
     CHECK(mConverter == NULL || mConverter->isValid());
 
     CHECK_EQ(0,
@@ -109,14 +113,6 @@
         CHECK_EQ(0, native_window_set_buffers_transform(
                     mNativeWindow.get(), transform));
     }
-
-    android_native_rect_t crop;
-    crop.left = mCropLeft;
-    crop.top = mCropTop;
-    crop.right = mCropRight + 1;
-    crop.bottom = mCropBottom + 1;
-
-    CHECK_EQ(0, native_window_set_crop(mNativeWindow.get(), &crop));
 }
 
 SoftwareRenderer::~SoftwareRenderer() {
@@ -142,7 +138,7 @@
 
     GraphicBufferMapper &mapper = GraphicBufferMapper::get();
 
-    Rect bounds(mWidth, mHeight);
+    Rect bounds(mCropWidth, mCropHeight);
 
     void *dst;
     CHECK_EQ(0, mapper.lock(
@@ -152,13 +148,11 @@
         mConverter->convert(
                 data,
                 mWidth, mHeight,
-                0, 0, mWidth - 1, mHeight - 1,
+                mCropLeft, mCropTop, mCropRight, mCropBottom,
                 dst,
                 buf->stride, buf->height,
-                0, 0, mWidth - 1, mHeight - 1);
-    } else {
-        CHECK_EQ(mColorFormat, OMX_COLOR_FormatYUV420Planar);
-
+                0, 0, mCropWidth - 1, mCropHeight - 1);
+    } else if (mColorFormat == OMX_COLOR_FormatYUV420Planar) {
         const uint8_t *src_y = (const uint8_t *)data;
         const uint8_t *src_u = (const uint8_t *)data + mWidth * mHeight;
         const uint8_t *src_v = src_u + (mWidth / 2 * mHeight / 2);
@@ -170,22 +164,57 @@
         uint8_t *dst_v = dst_y + dst_y_size;
         uint8_t *dst_u = dst_v + dst_c_size;
 
-        for (int y = 0; y < mHeight; ++y) {
-            memcpy(dst_y, src_y, mWidth);
+        for (int y = 0; y < mCropHeight; ++y) {
+            memcpy(dst_y, src_y, mCropWidth);
 
             src_y += mWidth;
             dst_y += buf->stride;
         }
 
-        for (int y = 0; y < (mHeight + 1) / 2; ++y) {
-            memcpy(dst_u, src_u, (mWidth + 1) / 2);
-            memcpy(dst_v, src_v, (mWidth + 1) / 2);
+        for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
+            memcpy(dst_u, src_u, (mCropWidth + 1) / 2);
+            memcpy(dst_v, src_v, (mCropWidth + 1) / 2);
 
             src_u += mWidth / 2;
             src_v += mWidth / 2;
             dst_u += dst_c_stride;
             dst_v += dst_c_stride;
         }
+    } else {
+        CHECK_EQ(mColorFormat, OMX_TI_COLOR_FormatYUV420PackedSemiPlanar);
+
+        const uint8_t *src_y =
+            (const uint8_t *)data;
+
+        const uint8_t *src_uv =
+            (const uint8_t *)data + mWidth * (mHeight - mCropTop / 2);
+
+        uint8_t *dst_y = (uint8_t *)dst;
+
+        size_t dst_y_size = buf->stride * buf->height;
+        size_t dst_c_stride = ALIGN(buf->stride / 2, 16);
+        size_t dst_c_size = dst_c_stride * buf->height / 2;
+        uint8_t *dst_v = dst_y + dst_y_size;
+        uint8_t *dst_u = dst_v + dst_c_size;
+
+        for (int y = 0; y < mCropHeight; ++y) {
+            memcpy(dst_y, src_y, mCropWidth);
+
+            src_y += mWidth;
+            dst_y += buf->stride;
+        }
+
+        for (int y = 0; y < (mCropHeight + 1) / 2; ++y) {
+            size_t tmp = (mCropWidth + 1) / 2;
+            for (size_t x = 0; x < tmp; ++x) {
+                dst_u[x] = src_uv[2 * x];
+                dst_v[x] = src_uv[2 * x + 1];
+            }
+
+            src_uv += mWidth;
+            dst_u += dst_c_stride;
+            dst_v += dst_c_stride;
+        }
     }
 
     CHECK_EQ(0, mapper.unlock(buf->handle));
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 78037b97..8f2ea95 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -47,6 +47,7 @@
     sp<ANativeWindow> mNativeWindow;
     int32_t mWidth, mHeight;
     int32_t mCropLeft, mCropTop, mCropRight, mCropBottom;
+    int32_t mCropWidth, mCropHeight;
 
     SoftwareRenderer(const SoftwareRenderer &);
     SoftwareRenderer &operator=(const SoftwareRenderer &);
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index a404f1f..d4354db 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -485,24 +485,20 @@
     };
     static const MimeToURL kMimeToURL[] = {
         { "video/avc",
-          "file:///sdcard/media_api/video/H264_AAC.3gp" },
-        { "video/mp4v-es", "file:///sdcard/media_api/video/gingerkids.MP4" },
+          "file:///sdcard/media_api/video/H264_500_AAC_128.3gp" },
+        { "video/mp4v-es", "file:///sdcard/media_api/video/MPEG4_320_AAC_64.mp4" },
         { "video/3gpp",
           "file:///sdcard/media_api/video/H263_500_AMRNB_12.3gp" },
         { "audio/3gpp",
           "file:///sdcard/media_api/video/H263_500_AMRNB_12.3gp" },
-        { "audio/amr-wb",
-          "file:///sdcard/media_api/music/"
-          "AI_AMR-WB_12.65kbps(13kbps)_16khz_mono_NMC.awb" },
+        { "audio/amr-wb", NULL },
         { "audio/mp4a-latm",
-          "file:///sdcard/media_api/video/H264_AAC.3gp" },
+          "file:///sdcard/media_api/video/H263_56_AAC_24.3gp" },
         { "audio/mpeg",
-          "file:///sdcard/media_api/music/MP3CBR.mp3" },
-        { "audio/vorbis",
-          "file:///sdcard/media_api/metaDataTestMedias/OGG/"
-          "When You Say Nothing At All.ogg" },
+          "file:///sdcard/media_api/music/MP3_48KHz_128kbps_s_1_17_CBR.mp3" },
+        { "audio/vorbis", NULL },
         { "video/x-vnd.on2.vp8",
-          "file:///sdcard/media_api/webm/big-buck-bunny_trailer.webm" },
+          "file:///sdcard/media_api/video/big-buck-bunny_trailer.webm" },
         { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "file:///sdcard/M1F1-Alaw-AFsp.wav" },
         { MEDIA_MIMETYPE_AUDIO_G711_MLAW,
           "file:///sdcard/M1F1-mulaw-AFsp.wav" },
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index a011ae2..9b09983 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -420,6 +420,10 @@
     // allow anyone to use camera (after they lock the camera)
     status_t result = checkPid();
     if (result == NO_ERROR) {
+        if (mHardware->recordingEnabled()) {
+            LOGE("Not allowed to unlock camera during recording.");
+            return INVALID_OPERATION;
+        }
         mClientPid = 0;
         LOG1("clear mCameraClient (pid %d)", callingPid);
         // we need to remove the reference to ICameraClient so that when the app
@@ -756,6 +760,11 @@
     status_t result = checkPidAndHardware();
     if (result != NO_ERROR) return result;
 
+    if (mHardware->recordingEnabled()) {
+        LOGE("Cannot take picture during recording.");
+        return INVALID_OPERATION;
+    }
+
     if ((msgType & CAMERA_MSG_RAW_IMAGE) &&
         (msgType & CAMERA_MSG_RAW_IMAGE_NOTIFY)) {
         LOGE("CAMERA_MSG_RAW_IMAGE and CAMERA_MSG_RAW_IMAGE_NOTIFY"
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index f6dd43a..a2019fc 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -1131,7 +1131,9 @@
             | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
             | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
             | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_SELECTED
-            | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
+            | AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED
+            | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED
+            | AccessibilityEvent.TYPE_VIEW_SCROLLED;
 
         private int mRetrievalAlowingWindowId;
 
diff --git a/voip/java/android/net/sip/ISipSessionListener.aidl b/voip/java/android/net/sip/ISipSessionListener.aidl
index 5920bca..690700c 100644
--- a/voip/java/android/net/sip/ISipSessionListener.aidl
+++ b/voip/java/android/net/sip/ISipSessionListener.aidl
@@ -72,6 +72,14 @@
     void onCallBusy(in ISipSession session);
 
     /**
+     * Called when the call is being transferred to a new one.
+     *
+     * @param newSession the new session that the call will be transferred to
+     * @param sessionDescription the new peer's session description
+     */
+    void onCallTransferring(in ISipSession newSession, String sessionDescription);
+
+    /**
      * Called when an error occurs during session initialization and
      * termination.
      *
diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java
index b46f8268..2666b69 100644
--- a/voip/java/android/net/sip/SipAudioCall.java
+++ b/voip/java/android/net/sip/SipAudioCall.java
@@ -170,6 +170,7 @@
     private SipProfile mLocalProfile;
     private SipAudioCall.Listener mListener;
     private SipSession mSipSession;
+    private SipSession mTransferringSession;
 
     private long mSessionId = System.currentTimeMillis();
     private String mPeerSd;
@@ -347,6 +348,27 @@
         }
     }
 
+    private synchronized void transferToNewSession() {
+        if (mTransferringSession == null) return;
+        SipSession origin = mSipSession;
+        mSipSession = mTransferringSession;
+        mTransferringSession = null;
+
+        // stop the replaced call.
+        if (mAudioStream != null) {
+            mAudioStream.join(null);
+        } else {
+            try {
+                mAudioStream = new AudioStream(InetAddress.getByName(
+                        getLocalIp()));
+            } catch (Throwable t) {
+                Log.i(TAG, "transferToNewSession(): " + t);
+            }
+        }
+        if (origin != null) origin.endCall();
+        startAudio();
+    }
+
     private SipSession.Listener createListener() {
         return new SipSession.Listener() {
             @Override
@@ -404,6 +426,13 @@
                 mPeerSd = sessionDescription;
                 Log.v(TAG, "onCallEstablished()" + mPeerSd);
 
+                // TODO: how to notify the UI that the remote party is changed
+                if ((mTransferringSession != null)
+                        && (session == mTransferringSession)) {
+                    transferToNewSession();
+                    return;
+                }
+
                 Listener listener = mListener;
                 if (listener != null) {
                     try {
@@ -420,7 +449,17 @@
 
             @Override
             public void onCallEnded(SipSession session) {
-                Log.d(TAG, "sip call ended: " + session);
+                Log.d(TAG, "sip call ended: " + session + " mSipSession:" + mSipSession);
+                // reset the trasnferring session if it is the one.
+                if (session == mTransferringSession) {
+                    mTransferringSession = null;
+                    return;
+                }
+                // or ignore the event if the original session is being
+                // transferred to the new one.
+                if ((mTransferringSession != null) ||
+                    (session != mSipSession)) return;
+
                 Listener listener = mListener;
                 if (listener != null) {
                     try {
@@ -489,6 +528,22 @@
             public void onRegistrationDone(SipSession session, int duration) {
                 // irrelevant
             }
+
+            @Override
+            public void onCallTransferring(SipSession newSession,
+                    String sessionDescription) {
+                Log.v(TAG, "onCallTransferring mSipSession:"
+                        + mSipSession + " newSession:" + newSession);
+                mTransferringSession = newSession;
+                // session changing request
+                try {
+                    String answer = createAnswer(sessionDescription).encode();
+                    newSession.answerCall(answer, SESSION_TIMEOUT);
+                } catch (Throwable e) {
+                    Log.e(TAG, "onCallTransferring()", e);
+                    newSession.endCall();
+                }
+            }
         };
     }
 
diff --git a/voip/java/android/net/sip/SipSession.java b/voip/java/android/net/sip/SipSession.java
index 5629b3c..5ba1626 100644
--- a/voip/java/android/net/sip/SipSession.java
+++ b/voip/java/android/net/sip/SipSession.java
@@ -160,6 +160,17 @@
         }
 
         /**
+         * Called when the call is being transferred to a new one.
+         *
+         * @hide
+         * @param newSession the new session that the call will be transferred to
+         * @param sessionDescription the new peer's session description
+         */
+        public void onCallTransferring(SipSession newSession,
+                String sessionDescription) {
+        }
+
+        /**
          * Called when an error occurs during session initialization and
          * termination.
          *
@@ -489,6 +500,16 @@
                 }
             }
 
+            public void onCallTransferring(ISipSession session,
+                    String sessionDescription) {
+                if (mListener != null) {
+                    mListener.onCallTransferring(
+                            new SipSession(session, SipSession.this.mListener),
+                            sessionDescription);
+
+                }
+            }
+
             public void onCallChangeFailed(ISipSession session, int errorCode,
                     String message) {
                 if (mListener != null) {
diff --git a/voip/java/android/net/sip/SipSessionAdapter.java b/voip/java/android/net/sip/SipSessionAdapter.java
index 86aca37..f538983 100644
--- a/voip/java/android/net/sip/SipSessionAdapter.java
+++ b/voip/java/android/net/sip/SipSessionAdapter.java
@@ -42,6 +42,10 @@
     public void onCallBusy(ISipSession session) {
     }
 
+    public void onCallTransferring(ISipSession session,
+            String sessionDescription) {
+    }
+
     public void onCallChangeFailed(ISipSession session, int errorCode,
             String message) {
     }
diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java
index 018e6de..47950e3 100644
--- a/voip/java/com/android/server/sip/SipHelper.java
+++ b/voip/java/com/android/server/sip/SipHelper.java
@@ -314,7 +314,7 @@
         }
     }
 
-    private ServerTransaction getServerTransaction(RequestEvent event)
+    public ServerTransaction getServerTransaction(RequestEvent event)
             throws SipException {
         ServerTransaction transaction = event.getServerTransaction();
         if (transaction == null) {
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 2d0dd9c..481e306 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -21,6 +21,8 @@
 import gov.nist.javax.sip.header.SIPHeaderNames;
 import gov.nist.javax.sip.header.ProxyAuthenticate;
 import gov.nist.javax.sip.header.WWWAuthenticate;
+import gov.nist.javax.sip.header.extensions.ReferredByHeader;
+import gov.nist.javax.sip.header.extensions.ReplacesHeader;
 import gov.nist.javax.sip.message.SIPMessage;
 
 import android.net.sip.ISipSession;
@@ -365,24 +367,85 @@
             super(listener);
         }
 
+        private SipSessionImpl createNewSession(RequestEvent event,
+                ISipSessionListener listener, ServerTransaction transaction)
+                throws SipException {
+            SipSessionImpl newSession = new SipSessionImpl(listener);
+            newSession.mServerTransaction = transaction;
+            newSession.mState = SipSession.State.INCOMING_CALL;
+            newSession.mDialog = newSession.mServerTransaction.getDialog();
+            newSession.mInviteReceived = event;
+            newSession.mPeerProfile = createPeerProfile(event.getRequest());
+            newSession.mPeerSessionDescription =
+                    extractContent(event.getRequest());
+            return newSession;
+        }
+
+        private int processInviteWithReplaces(RequestEvent event,
+                ReplacesHeader replaces) {
+            String callId = replaces.getCallId();
+            SipSessionImpl session = mSessionMap.get(callId);
+            if (session == null) {
+                return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
+            }
+
+            Dialog dialog = session.mDialog;
+            if (dialog == null) return Response.DECLINE;
+
+            if (!dialog.getLocalTag().equals(replaces.getToTag()) ||
+                    !dialog.getRemoteTag().equals(replaces.getFromTag())) {
+                // No match is found, returns 481.
+                return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
+            }
+
+            ReferredByHeader referredBy = (ReferredByHeader) event.getRequest()
+                    .getHeader(ReferredByHeader.NAME);
+            if ((referredBy == null) ||
+                    !dialog.getRemoteParty().equals(referredBy.getAddress())) {
+                return Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST;
+            }
+            return Response.OK;
+        }
+
+        private void processNewInviteRequest(RequestEvent event)
+                throws SipException {
+            ReplacesHeader replaces = (ReplacesHeader) event.getRequest()
+                    .getHeader(ReplacesHeader.NAME);
+            SipSessionImpl newSession = null;
+            if (replaces != null) {
+                int response = processInviteWithReplaces(event, replaces);
+                if (DEBUG) {
+                    Log.v(TAG, "ReplacesHeader: " + replaces
+                            + " response=" + response);
+                }
+                if (response == Response.OK) {
+                    SipSessionImpl replacedSession =
+                            mSessionMap.get(replaces.getCallId());
+                    // got INVITE w/ replaces request.
+                    newSession = createNewSession(event,
+                            replacedSession.mProxy.getListener(),
+                            mSipHelper.getServerTransaction(event));
+                    newSession.mProxy.onCallTransferring(newSession,
+                            newSession.mPeerSessionDescription);
+                } else {
+                    mSipHelper.sendResponse(event, response);
+                }
+            } else {
+                // New Incoming call.
+                newSession = createNewSession(event, mProxy,
+                        mSipHelper.sendRinging(event, generateTag()));
+                mProxy.onRinging(newSession, newSession.mPeerProfile,
+                        newSession.mPeerSessionDescription);
+            }
+            if (newSession != null) addSipSession(newSession);
+        }
+
         public boolean process(EventObject evt) throws SipException {
             if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~   " + this + ": "
                     + SipSession.State.toString(mState) + ": processing "
                     + log(evt));
             if (isRequestEvent(Request.INVITE, evt)) {
-                RequestEvent event = (RequestEvent) evt;
-                SipSessionImpl newSession = new SipSessionImpl(mProxy);
-                newSession.mState = SipSession.State.INCOMING_CALL;
-                newSession.mServerTransaction = mSipHelper.sendRinging(event,
-                        generateTag());
-                newSession.mDialog = newSession.mServerTransaction.getDialog();
-                newSession.mInviteReceived = event;
-                newSession.mPeerProfile = createPeerProfile(event.getRequest());
-                newSession.mPeerSessionDescription =
-                        extractContent(event.getRequest());
-                addSipSession(newSession);
-                mProxy.onRinging(newSession, newSession.mPeerProfile,
-                        newSession.mPeerSessionDescription);
+                processNewInviteRequest((RequestEvent) evt);
                 return true;
             } else if (isRequestEvent(Request.OPTIONS, evt)) {
                 mSipHelper.sendResponse((RequestEvent) evt, Response.OK);
diff --git a/voip/java/com/android/server/sip/SipSessionListenerProxy.java b/voip/java/com/android/server/sip/SipSessionListenerProxy.java
index f8be0a8..8655a3a 100644
--- a/voip/java/com/android/server/sip/SipSessionListenerProxy.java
+++ b/voip/java/com/android/server/sip/SipSessionListenerProxy.java
@@ -110,6 +110,20 @@
         });
     }
 
+    public void onCallTransferring(final ISipSession newSession,
+            final String sessionDescription) {
+        if (mListener == null) return;
+        proxy(new Runnable() {
+            public void run() {
+                try {
+                    mListener.onCallTransferring(newSession, sessionDescription);
+                } catch (Throwable t) {
+                    handle(t, "onCallTransferring()");
+                }
+            }
+        });
+    }
+
     public void onCallBusy(final ISipSession session) {
         if (mListener == null) return;
         proxy(new Runnable() {