Merge "Override equals()/hashCode() for RS BaseObj."
diff --git a/Android.mk b/Android.mk
index e987f91..90ffcd3 100644
--- a/Android.mk
+++ b/Android.mk
@@ -127,11 +127,13 @@
 	core/java/android/os/INetworkManagementService.aidl \
 	core/java/android/os/IPermissionController.aidl \
 	core/java/android/os/IPowerManager.aidl \
-    core/java/android/os/IRemoteCallback.aidl \
+	core/java/android/os/IRemoteCallback.aidl \
 	core/java/android/os/IVibratorService.aidl \
-    core/java/android/service/wallpaper/IWallpaperConnection.aidl \
-    core/java/android/service/wallpaper/IWallpaperEngine.aidl \
-    core/java/android/service/wallpaper/IWallpaperService.aidl \
+	core/java/android/service/wallpaper/IWallpaperConnection.aidl \
+	core/java/android/service/wallpaper/IWallpaperEngine.aidl \
+	core/java/android/service/wallpaper/IWallpaperService.aidl \
+	core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl\
+	core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl\
 	core/java/android/view/accessibility/IAccessibilityManager.aidl \
 	core/java/android/view/accessibility/IAccessibilityManagerClient.aidl \
 	core/java/android/view/IApplicationToken.aidl \
diff --git a/api/14.txt b/api/14.txt
index 58d49d9..5c4732e 100644
--- a/api/14.txt
+++ b/api/14.txt
@@ -1921,12 +1921,12 @@
 
   public class FloatEvaluator implements android.animation.TypeEvaluator {
     ctor public FloatEvaluator();
-    method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+    method public java.lang.Float evaluate(float, java.lang.Number, java.lang.Number);
   }
 
   public class IntEvaluator implements android.animation.TypeEvaluator {
     ctor public IntEvaluator();
-    method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+    method public java.lang.Integer evaluate(float, java.lang.Integer, java.lang.Integer);
   }
 
   public abstract class Keyframe implements java.lang.Cloneable {
@@ -2012,7 +2012,7 @@
   }
 
   public abstract interface TypeEvaluator {
-    method public abstract java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+    method public abstract T evaluate(float, T, T);
   }
 
   public class ValueAnimator extends android.animation.Animator {
@@ -14255,20 +14255,10 @@
 
 package android.preference {
 
-  public class CheckBoxPreference extends android.preference.Preference {
+    public class CheckBoxPreference extends android.preference.TwoStatePreference {
     ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
     ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
     ctor public CheckBoxPreference(android.content.Context);
-    method public boolean getDisableDependentsState();
-    method public java.lang.CharSequence getSummaryOff();
-    method public java.lang.CharSequence getSummaryOn();
-    method public boolean isChecked();
-    method public void setChecked(boolean);
-    method public void setDisableDependentsState(boolean);
-    method public void setSummaryOff(java.lang.CharSequence);
-    method public void setSummaryOff(int);
-    method public void setSummaryOn(java.lang.CharSequence);
-    method public void setSummaryOn(int);
   }
 
   public abstract class DialogPreference extends android.preference.Preference implements android.content.DialogInterface.OnClickListener android.content.DialogInterface.OnDismissListener android.preference.PreferenceManager.OnActivityDestroyListener {
@@ -22250,10 +22240,8 @@
 
   public class AccessibilityRecord {
     ctor protected AccessibilityRecord();
-    method protected void clear();
     method public int getAddedCount();
     method public java.lang.CharSequence getBeforeText();
-    method public boolean getBooleanProperty(int);
     method public java.lang.CharSequence getClassName();
     method public java.lang.CharSequence getContentDescription();
     method public int getCurrentItemIndex();
diff --git a/api/current.txt b/api/current.txt
index 8ef07de..c5cdb7b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -78,6 +78,7 @@
     field public static final java.lang.String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
     field public static final java.lang.String READ_LOGS = "android.permission.READ_LOGS";
     field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
+    field public static final java.lang.String READ_PROFILE = "android.permission.READ_PROFILE";
     field public static final java.lang.String READ_SMS = "android.permission.READ_SMS";
     field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS";
     field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS";
@@ -90,6 +91,7 @@
     field public static final java.lang.String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
     field public static final java.lang.String REORDER_TASKS = "android.permission.REORDER_TASKS";
     field public static final deprecated java.lang.String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES";
+    field public static final java.lang.String RETRIEVE_WINDOW_CONTENT = "android.permission.RETRIEVE_WINDOW_CONTENT";
     field public static final java.lang.String SEND_SMS = "android.permission.SEND_SMS";
     field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
     field public static final java.lang.String SET_ALARM = "com.android.alarm.permission.SET_ALARM";
@@ -120,6 +122,7 @@
     field public static final java.lang.String WRITE_EXTERNAL_STORAGE = "android.permission.WRITE_EXTERNAL_STORAGE";
     field public static final java.lang.String WRITE_GSERVICES = "android.permission.WRITE_GSERVICES";
     field public static final java.lang.String WRITE_HISTORY_BOOKMARKS = "com.android.browser.permission.WRITE_HISTORY_BOOKMARKS";
+    field public static final java.lang.String WRITE_PROFILE = "android.permission.WRITE_PROFILE";
     field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS";
     field public static final java.lang.String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS";
     field public static final java.lang.String WRITE_SMS = "android.permission.WRITE_SMS";
@@ -180,9 +183,9 @@
   public static final class R.attr {
     ctor public R.attr();
     field public static final int absListViewStyle = 16842858; // 0x101006a
-    field public static final int accessibilityEventTypes = 16843647; // 0x101037f
-    field public static final int accessibilityFeedbackType = 16843649; // 0x1010381
-    field public static final int accessibilityFlags = 16843651; // 0x1010383
+    field public static final int accessibilityEventTypes = 16843650; // 0x1010382
+    field public static final int accessibilityFeedbackType = 16843652; // 0x1010384
+    field public static final int accessibilityFlags = 16843654; // 0x1010386
     field public static final int accountPreferences = 16843423; // 0x101029f
     field public static final int accountType = 16843407; // 0x101028f
     field public static final int action = 16842797; // 0x101002d
@@ -202,7 +205,7 @@
     field public static final int actionModeCopyDrawable = 16843538; // 0x1010312
     field public static final int actionModeCutDrawable = 16843537; // 0x1010311
     field public static final int actionModePasteDrawable = 16843539; // 0x1010313
-    field public static final int actionModeSelectAllDrawable = 16843645; // 0x101037d
+    field public static final int actionModeSelectAllDrawable = 16843648; // 0x1010380
     field public static final int actionOverflowButtonStyle = 16843510; // 0x10102f6
     field public static final int actionViewClass = 16843516; // 0x10102fc
     field public static final int activatedBackgroundIndicator = 16843517; // 0x10102fd
@@ -274,7 +277,7 @@
     field public static final int cacheColorHint = 16843009; // 0x1010101
     field public static final int calendarViewShown = 16843596; // 0x101034c
     field public static final int calendarViewStyle = 16843613; // 0x101035d
-    field public static final int canRetrieveWindowContent = 16843652; // 0x1010384
+    field public static final int canRetrieveWindowContent = 16843655; // 0x1010387
     field public static final int candidatesTextStyleSpans = 16843312; // 0x1010230
     field public static final deprecated int capitalize = 16843113; // 0x1010169
     field public static final int centerBright = 16842956; // 0x10100cc
@@ -307,9 +310,9 @@
     field public static final int colorBackgroundCacheHint = 16843435; // 0x10102ab
     field public static final int colorForeground = 16842800; // 0x1010030
     field public static final int colorForegroundInverse = 16843270; // 0x1010206
-    field public static final int columnCount = 16843636; // 0x1010374
+    field public static final int columnCount = 16843639; // 0x1010377
     field public static final int columnDelay = 16843215; // 0x10101cf
-    field public static final int columnOrderPreserved = 16843637; // 0x1010375
+    field public static final int columnOrderPreserved = 16843640; // 0x1010378
     field public static final int columnWidth = 16843031; // 0x1010117
     field public static final int compatibleWidthLimitDp = 16843621; // 0x1010365
     field public static final int completionHint = 16843122; // 0x1010172
@@ -454,7 +457,7 @@
     field public static final int fromXScale = 16843202; // 0x10101c2
     field public static final int fromYDelta = 16843208; // 0x10101c8
     field public static final int fromYScale = 16843204; // 0x10101c4
-    field public static final int fullBackupAgent = 16843632; // 0x1010370
+    field public static final int fullBackupAgent = 16843635; // 0x1010373
     field public static final int fullBright = 16842954; // 0x10100ca
     field public static final int fullDark = 16842950; // 0x10100c6
     field public static final int functionalTest = 16842787; // 0x1010023
@@ -485,7 +488,6 @@
     field public static final int hint = 16843088; // 0x1010150
     field public static final int homeAsUpIndicator = 16843531; // 0x101030b
     field public static final int homeLayout = 16843549; // 0x101031d
-    field public static final int horizontalDirection = 16843631; // 0x101036f
     field public static final int horizontalDivider = 16843053; // 0x101012d
     field public static final int horizontalGap = 16843327; // 0x101023f
     field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
@@ -533,7 +535,7 @@
     field public static final int installLocation = 16843447; // 0x10102b7
     field public static final int interpolator = 16843073; // 0x1010141
     field public static final int isAlwaysSyncable = 16843571; // 0x1010333
-    field public static final int isAuxiliary = 16843646; // 0x101037e
+    field public static final int isAuxiliary = 16843649; // 0x1010381
     field public static final int isDefault = 16843297; // 0x1010221
     field public static final int isIndicator = 16843079; // 0x1010147
     field public static final int isModifier = 16843334; // 0x1010246
@@ -570,6 +572,7 @@
     field public static final int layerType = 16843604; // 0x1010354
     field public static final int layout = 16842994; // 0x10100f2
     field public static final int layoutAnimation = 16842988; // 0x10100ec
+    field public static final int layoutDirection = 16843634; // 0x1010372
     field public static final int layout_above = 16843140; // 0x1010184
     field public static final int layout_alignBaseline = 16843142; // 0x1010186
     field public static final int layout_alignBottom = 16843146; // 0x101018a
@@ -586,8 +589,8 @@
     field public static final int layout_centerInParent = 16843151; // 0x101018f
     field public static final int layout_centerVertical = 16843153; // 0x1010191
     field public static final int layout_column = 16843084; // 0x101014c
-    field public static final int layout_columnSpan = 16843643; // 0x101037b
-    field public static final int layout_columnWeight = 16843644; // 0x101037c
+    field public static final int layout_columnSpan = 16843646; // 0x101037e
+    field public static final int layout_columnWeight = 16843647; // 0x101037f
     field public static final int layout_gravity = 16842931; // 0x10100b3
     field public static final int layout_height = 16842997; // 0x10100f5
     field public static final int layout_margin = 16842998; // 0x10100f6
@@ -595,9 +598,9 @@
     field public static final int layout_marginLeft = 16842999; // 0x10100f7
     field public static final int layout_marginRight = 16843001; // 0x10100f9
     field public static final int layout_marginTop = 16843000; // 0x10100f8
-    field public static final int layout_row = 16843640; // 0x1010378
-    field public static final int layout_rowSpan = 16843641; // 0x1010379
-    field public static final int layout_rowWeight = 16843642; // 0x101037a
+    field public static final int layout_row = 16843643; // 0x101037b
+    field public static final int layout_rowSpan = 16843644; // 0x101037c
+    field public static final int layout_rowWeight = 16843645; // 0x101037d
     field public static final int layout_scale = 16843155; // 0x1010193
     field public static final int layout_span = 16843085; // 0x101014d
     field public static final int layout_toLeftOf = 16843138; // 0x1010182
@@ -627,7 +630,7 @@
     field public static final int loopViews = 16843527; // 0x1010307
     field public static final int manageSpaceActivity = 16842756; // 0x1010004
     field public static final int mapViewStyle = 16842890; // 0x101008a
-    field public static final int marginsIncludedInAlignment = 16843639; // 0x1010377
+    field public static final int marginsIncludedInAlignment = 16843642; // 0x101037a
     field public static final int marqueeRepeatLimit = 16843293; // 0x101021d
     field public static final int max = 16843062; // 0x1010136
     field public static final int maxDate = 16843584; // 0x1010340
@@ -664,7 +667,7 @@
     field public static final int nextFocusUp = 16842979; // 0x10100e3
     field public static final int noHistory = 16843309; // 0x101022d
     field public static final int normalScreens = 16843397; // 0x1010285
-    field public static final int notificationTimeout = 16843650; // 0x1010382
+    field public static final int notificationTimeout = 16843653; // 0x1010385
     field public static final int numColumns = 16843032; // 0x1010118
     field public static final int numStars = 16843076; // 0x1010144
     field public static final deprecated int numeric = 16843109; // 0x1010165
@@ -681,7 +684,7 @@
     field public static final int overScrollFooter = 16843459; // 0x10102c3
     field public static final int overScrollHeader = 16843458; // 0x10102c2
     field public static final int overScrollMode = 16843457; // 0x10102c1
-    field public static final int packageNames = 16843648; // 0x1010380
+    field public static final int packageNames = 16843651; // 0x1010383
     field public static final int padding = 16842965; // 0x10100d5
     field public static final int paddingBottom = 16842969; // 0x10100d9
     field public static final int paddingLeft = 16842966; // 0x10100d6
@@ -771,11 +774,11 @@
     field public static final int rotation = 16843558; // 0x1010326
     field public static final int rotationX = 16843559; // 0x1010327
     field public static final int rotationY = 16843560; // 0x1010328
-    field public static final int rowCount = 16843634; // 0x1010372
+    field public static final int rowCount = 16843637; // 0x1010375
     field public static final int rowDelay = 16843216; // 0x10101d0
     field public static final int rowEdgeFlags = 16843329; // 0x1010241
     field public static final int rowHeight = 16843058; // 0x1010132
-    field public static final int rowOrderPreserved = 16843635; // 0x1010373
+    field public static final int rowOrderPreserved = 16843638; // 0x1010376
     field public static final int saveEnabled = 16842983; // 0x10100e7
     field public static final int scaleGravity = 16843262; // 0x10101fe
     field public static final int scaleHeight = 16843261; // 0x10101fd
@@ -888,12 +891,15 @@
     field public static final int subtitleTextStyle = 16843513; // 0x10102f9
     field public static final int suggestActionMsg = 16843228; // 0x10101dc
     field public static final int suggestActionMsgColumn = 16843229; // 0x10101dd
-    field public static final int suggestionsEnabled = 16843633; // 0x1010371
+    field public static final int suggestionsEnabled = 16843636; // 0x1010374
     field public static final int summary = 16843241; // 0x10101e9
     field public static final int summaryColumn = 16843426; // 0x10102a2
     field public static final int summaryOff = 16843248; // 0x10101f0
     field public static final int summaryOn = 16843247; // 0x10101ef
     field public static final int supportsUploading = 16843419; // 0x101029b
+    field public static final int switchPreferenceStyle = 16843629; // 0x101036d
+    field public static final int switchTextOff = 16843628; // 0x101036c
+    field public static final int switchTextOn = 16843627; // 0x101036b
     field public static final int syncable = 16842777; // 0x1010019
     field public static final int tabStripEnabled = 16843453; // 0x10102bd
     field public static final int tabStripLeft = 16843451; // 0x10102bb
@@ -956,9 +962,9 @@
     field public static final int textEditPasteWindowLayout = 16843540; // 0x1010314
     field public static final int textEditSideNoPasteWindowLayout = 16843615; // 0x101035f
     field public static final int textEditSidePasteWindowLayout = 16843614; // 0x101035e
-    field public static final int textEditSuggestionItemLayout = 16843630; // 0x101036e
-    field public static final int textEditSuggestionsBottomWindowLayout = 16843628; // 0x101036c
-    field public static final int textEditSuggestionsTopWindowLayout = 16843629; // 0x101036d
+    field public static final int textEditSuggestionItemLayout = 16843633; // 0x1010371
+    field public static final int textEditSuggestionsBottomWindowLayout = 16843631; // 0x101036f
+    field public static final int textEditSuggestionsTopWindowLayout = 16843632; // 0x1010370
     field public static final int textFilterEnabled = 16843007; // 0x10100ff
     field public static final int textIsSelectable = 16843542; // 0x1010316
     field public static final int textOff = 16843045; // 0x1010125
@@ -970,7 +976,7 @@
     field public static final int textSelectHandleWindowStyle = 16843464; // 0x10102c8
     field public static final int textSize = 16842901; // 0x1010095
     field public static final int textStyle = 16842903; // 0x1010097
-    field public static final int textSuggestionsWindowStyle = 16843627; // 0x101036b
+    field public static final int textSuggestionsWindowStyle = 16843630; // 0x101036e
     field public static final int textViewStyle = 16842884; // 0x1010084
     field public static final int theme = 16842752; // 0x1010000
     field public static final int thickness = 16843360; // 0x1010260
@@ -1006,7 +1012,7 @@
     field public static final int unfocusedMonthDateColor = 16843588; // 0x1010344
     field public static final int unselectedAlpha = 16843278; // 0x101020e
     field public static final int updatePeriodMillis = 16843344; // 0x1010250
-    field public static final int useDefaultMargins = 16843638; // 0x1010376
+    field public static final int useDefaultMargins = 16843641; // 0x1010379
     field public static final int useIntrinsicSizeAsMinimum = 16843536; // 0x1010310
     field public static final int useLevel = 16843167; // 0x101019f
     field public static final int userVisible = 16843409; // 0x1010291
@@ -1515,12 +1521,13 @@
     field public static final int Theme_Holo_Light_Dialog_NoActionBar = 16973941; // 0x1030075
     field public static final int Theme_Holo_Light_Dialog_NoActionBar_MinWidth = 16973942; // 0x1030076
     field public static final int Theme_Holo_Light_NoActionBar = 16974064; // 0x10300f0
+    field public static final int Theme_Holo_Light_NoActionBar_Fullscreen = 16974065; // 0x10300f1
     field public static final int Theme_Holo_Light_Panel = 16973948; // 0x103007c
-    field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974067; // 0x10300f3
+    field public static final int Theme_Holo_Light_SplitActionBarWhenNarrow = 16974077; // 0x10300fd
     field public static final int Theme_Holo_NoActionBar = 16973932; // 0x103006c
     field public static final int Theme_Holo_NoActionBar_Fullscreen = 16973933; // 0x103006d
     field public static final int Theme_Holo_Panel = 16973947; // 0x103007b
-    field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974066; // 0x10300f2
+    field public static final int Theme_Holo_SplitActionBarWhenNarrow = 16974076; // 0x10300fc
     field public static final int Theme_Holo_Wallpaper = 16973949; // 0x103007d
     field public static final int Theme_Holo_Wallpaper_NoTitleBar = 16973950; // 0x103007e
     field public static final int Theme_InputMethod = 16973908; // 0x1030054
@@ -1545,6 +1552,9 @@
     field public static final int Widget = 16973842; // 0x1030012
     field public static final int Widget_AbsListView = 16973843; // 0x1030013
     field public static final int Widget_ActionBar = 16973954; // 0x1030082
+    field public static final int Widget_ActionBar_TabBar = 16974068; // 0x10300f4
+    field public static final int Widget_ActionBar_TabText = 16974067; // 0x10300f3
+    field public static final int Widget_ActionBar_TabView = 16974066; // 0x10300f2
     field public static final int Widget_ActionButton = 16973956; // 0x1030084
     field public static final int Widget_ActionButton_CloseMode = 16973960; // 0x1030088
     field public static final int Widget_ActionButton_Overflow = 16973959; // 0x1030087
@@ -1568,6 +1578,9 @@
     field public static final int Widget_GridView = 16973874; // 0x1030032
     field public static final int Widget_Holo = 16973962; // 0x103008a
     field public static final int Widget_Holo_ActionBar = 16974004; // 0x10300b4
+    field public static final int Widget_Holo_ActionBar_TabBar = 16974071; // 0x10300f7
+    field public static final int Widget_Holo_ActionBar_TabText = 16974070; // 0x10300f6
+    field public static final int Widget_Holo_ActionBar_TabView = 16974069; // 0x10300f5
     field public static final int Widget_Holo_ActionButton = 16973999; // 0x10300af
     field public static final int Widget_Holo_ActionButton_CloseMode = 16974003; // 0x10300b3
     field public static final int Widget_Holo_ActionButton_Overflow = 16974000; // 0x10300b0
@@ -1593,6 +1606,9 @@
     field public static final int Widget_Holo_ImageButton = 16973974; // 0x1030096
     field public static final int Widget_Holo_Light = 16974005; // 0x10300b5
     field public static final int Widget_Holo_Light_ActionBar = 16974049; // 0x10300e1
+    field public static final int Widget_Holo_Light_ActionBar_TabBar = 16974074; // 0x10300fa
+    field public static final int Widget_Holo_Light_ActionBar_TabText = 16974073; // 0x10300f9
+    field public static final int Widget_Holo_Light_ActionBar_TabView = 16974072; // 0x10300f8
     field public static final int Widget_Holo_Light_ActionButton = 16974045; // 0x10300dd
     field public static final int Widget_Holo_Light_ActionButton_CloseMode = 16974048; // 0x10300e0
     field public static final int Widget_Holo_Light_ActionButton_Overflow = 16974046; // 0x10300de
@@ -1966,12 +1982,12 @@
 
   public class FloatEvaluator implements android.animation.TypeEvaluator {
     ctor public FloatEvaluator();
-    method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+    method public java.lang.Float evaluate(float, java.lang.Number, java.lang.Number);
   }
 
   public class IntEvaluator implements android.animation.TypeEvaluator {
     ctor public IntEvaluator();
-    method public java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+    method public java.lang.Integer evaluate(float, java.lang.Integer, java.lang.Integer);
   }
 
   public abstract class Keyframe implements java.lang.Cloneable {
@@ -2032,9 +2048,13 @@
     method public java.lang.String getPropertyName();
     method public java.lang.Object getTarget();
     method public static android.animation.ObjectAnimator ofFloat(java.lang.Object, java.lang.String, float...);
+    method public static android.animation.ObjectAnimator ofFloat(T, android.util.Property<T, java.lang.Float>, float...);
     method public static android.animation.ObjectAnimator ofInt(java.lang.Object, java.lang.String, int...);
+    method public static android.animation.ObjectAnimator ofInt(T, android.util.Property<T, java.lang.Integer>, int...);
     method public static android.animation.ObjectAnimator ofObject(java.lang.Object, java.lang.String, android.animation.TypeEvaluator, java.lang.Object...);
+    method public static android.animation.ObjectAnimator ofObject(T, android.util.Property<T, V>, android.animation.TypeEvaluator<V>, V...);
     method public static android.animation.ObjectAnimator ofPropertyValuesHolder(java.lang.Object, android.animation.PropertyValuesHolder...);
+    method public void setProperty(android.util.Property);
     method public void setPropertyName(java.lang.String);
   }
 
@@ -2042,14 +2062,19 @@
     method public android.animation.PropertyValuesHolder clone();
     method public java.lang.String getPropertyName();
     method public static android.animation.PropertyValuesHolder ofFloat(java.lang.String, float...);
+    method public static android.animation.PropertyValuesHolder ofFloat(android.util.Property<?, java.lang.Float>, float...);
     method public static android.animation.PropertyValuesHolder ofInt(java.lang.String, int...);
+    method public static android.animation.PropertyValuesHolder ofInt(android.util.Property<?, java.lang.Integer>, int...);
     method public static android.animation.PropertyValuesHolder ofKeyframe(java.lang.String, android.animation.Keyframe...);
+    method public static android.animation.PropertyValuesHolder ofKeyframe(android.util.Property, android.animation.Keyframe...);
     method public static android.animation.PropertyValuesHolder ofObject(java.lang.String, android.animation.TypeEvaluator, java.lang.Object...);
+    method public static android.animation.PropertyValuesHolder ofObject(android.util.Property, android.animation.TypeEvaluator<V>, V...);
     method public void setEvaluator(android.animation.TypeEvaluator);
     method public void setFloatValues(float...);
     method public void setIntValues(int...);
     method public void setKeyframes(android.animation.Keyframe...);
     method public void setObjectValues(java.lang.Object...);
+    method public void setProperty(android.util.Property);
     method public void setPropertyName(java.lang.String);
   }
 
@@ -2058,7 +2083,7 @@
   }
 
   public abstract interface TypeEvaluator {
-    method public abstract java.lang.Object evaluate(float, java.lang.Object, java.lang.Object);
+    method public abstract T evaluate(float, T, T);
   }
 
   public class ValueAnimator extends android.animation.Animator {
@@ -4966,6 +4991,7 @@
     field public static final java.lang.String ACTION_INSERT_OR_EDIT = "android.intent.action.INSERT_OR_EDIT";
     field public static final java.lang.String ACTION_LOCALE_CHANGED = "android.intent.action.LOCALE_CHANGED";
     field public static final java.lang.String ACTION_MAIN = "android.intent.action.MAIN";
+    field public static final java.lang.String ACTION_MANAGE_NETWORK_USAGE = "android.intent.action.MANAGE_NETWORK_USAGE";
     field public static final java.lang.String ACTION_MANAGE_PACKAGE_STORAGE = "android.intent.action.MANAGE_PACKAGE_STORAGE";
     field public static final java.lang.String ACTION_MEDIA_BAD_REMOVAL = "android.intent.action.MEDIA_BAD_REMOVAL";
     field public static final java.lang.String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";
@@ -5014,8 +5040,8 @@
     field public static final java.lang.String ACTION_TIME_CHANGED = "android.intent.action.TIME_SET";
     field public static final java.lang.String ACTION_TIME_TICK = "android.intent.action.TIME_TICK";
     field public static final java.lang.String ACTION_UID_REMOVED = "android.intent.action.UID_REMOVED";
-    field public static final java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
-    field public static final java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
+    field public static final deprecated java.lang.String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
+    field public static final deprecated java.lang.String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
     field public static final java.lang.String ACTION_USER_PRESENT = "android.intent.action.USER_PRESENT";
     field public static final java.lang.String ACTION_VIEW = "android.intent.action.VIEW";
     field public static final java.lang.String ACTION_VOICE_COMMAND = "android.intent.action.VOICE_COMMAND";
@@ -14421,20 +14447,10 @@
 
 package android.preference {
 
-  public class CheckBoxPreference extends android.preference.Preference {
+  public class CheckBoxPreference extends android.preference.TwoStatePreference {
     ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet, int);
     ctor public CheckBoxPreference(android.content.Context, android.util.AttributeSet);
     ctor public CheckBoxPreference(android.content.Context);
-    method public boolean getDisableDependentsState();
-    method public java.lang.CharSequence getSummaryOff();
-    method public java.lang.CharSequence getSummaryOn();
-    method public boolean isChecked();
-    method public void setChecked(boolean);
-    method public void setDisableDependentsState(boolean);
-    method public void setSummaryOff(java.lang.CharSequence);
-    method public void setSummaryOff(int);
-    method public void setSummaryOn(java.lang.CharSequence);
-    method public void setSummaryOn(int);
   }
 
   public abstract class DialogPreference extends android.preference.Preference implements android.content.DialogInterface.OnClickListener android.content.DialogInterface.OnDismissListener android.preference.PreferenceManager.OnActivityDestroyListener {
@@ -14762,6 +14778,34 @@
     method public void setShowSilent(boolean);
   }
 
+  public class SwitchPreference extends android.preference.TwoStatePreference {
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public SwitchPreference(android.content.Context, android.util.AttributeSet);
+    ctor public SwitchPreference(android.content.Context);
+    method public java.lang.CharSequence getSwitchTextOff();
+    method public java.lang.CharSequence getSwitchTextOn();
+    method public void setSwitchTextOff(java.lang.CharSequence);
+    method public void setSwitchTextOff(int);
+    method public void setSwitchTextOn(java.lang.CharSequence);
+    method public void setSwitchTextOn(int);
+  }
+
+  public abstract class TwoStatePreference extends android.preference.Preference {
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet, int);
+    ctor public TwoStatePreference(android.content.Context, android.util.AttributeSet);
+    ctor public TwoStatePreference(android.content.Context);
+    method public boolean getDisableDependentsState();
+    method public java.lang.CharSequence getSummaryOff();
+    method public java.lang.CharSequence getSummaryOn();
+    method public boolean isChecked();
+    method public void setChecked(boolean);
+    method public void setDisableDependentsState(boolean);
+    method public void setSummaryOff(java.lang.CharSequence);
+    method public void setSummaryOff(int);
+    method public void setSummaryOn(java.lang.CharSequence);
+    method public void setSummaryOn(int);
+  }
+
 }
 
 package android.provider {
@@ -15162,6 +15206,7 @@
     field public static final android.net.Uri AUTHORITY_URI;
     field public static final java.lang.String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
     field public static final java.lang.String DIRECTORY_PARAM_KEY = "directory";
+    field public static final java.lang.String INCLUDE_PROFILE = "include_profile";
     field public static final java.lang.String LIMIT_PARAM_KEY = "limit";
   }
 
@@ -15461,6 +15506,7 @@
     field public static final java.lang.String DISPLAY_NAME = "display_name";
     field public static final java.lang.String HAS_PHONE_NUMBER = "has_phone_number";
     field public static final java.lang.String IN_VISIBLE_GROUP = "in_visible_group";
+    field public static final java.lang.String IS_USER_PROFILE = "is_user_profile";
     field public static final java.lang.String LOOKUP_KEY = "lookup";
     field public static final java.lang.String PHOTO_ID = "photo_id";
     field public static final java.lang.String PHOTO_THUMBNAIL_URI = "photo_thumb_uri";
@@ -15644,6 +15690,12 @@
     field public static final java.lang.String PROTOCOL = "protocol";
   }
 
+  public static final class ContactsContract.Profile implements android.provider.BaseColumns android.provider.ContactsContract.ContactNameColumns android.provider.ContactsContract.ContactOptionsColumns android.provider.ContactsContract.ContactStatusColumns android.provider.ContactsContract.ContactsColumns {
+    field public static final android.net.Uri CONTENT_RAW_CONTACTS_URI;
+    field public static final android.net.Uri CONTENT_URI;
+    field public static final android.net.Uri CONTENT_VCARD_URI;
+  }
+
   public static final class ContactsContract.QuickContact {
     ctor public ContactsContract.QuickContact();
     method public static void showQuickContact(android.content.Context, android.view.View, android.net.Uri, int, java.lang.String[]);
@@ -15679,6 +15731,7 @@
     field public static final java.lang.String CONTACT_ID = "contact_id";
     field public static final java.lang.String DELETED = "deleted";
     field public static final java.lang.String RAW_CONTACT_IS_READ_ONLY = "raw_contact_is_read_only";
+    field public static final java.lang.String RAW_CONTACT_IS_USER_PROFILE = "raw_contact_is_user_profile";
   }
 
   public static final class ContactsContract.RawContactsEntity implements android.provider.BaseColumns android.provider.ContactsContract.DataColumns android.provider.ContactsContract.RawContactsColumns {
@@ -16438,6 +16491,7 @@
     method public void setFromFieldPacker(int, int, android.renderscript.FieldPacker);
     method public void syncAll(int);
     field public static final int USAGE_GRAPHICS_CONSTANTS = 8; // 0x8
+    field public static final int USAGE_GRAPHICS_RENDER_TARGET = 16; // 0x10
     field public static final int USAGE_GRAPHICS_TEXTURE = 2; // 0x2
     field public static final int USAGE_GRAPHICS_VERTEX = 4; // 0x4
     field public static final int USAGE_SCRIPT = 1; // 0x1
@@ -17442,21 +17496,23 @@
 
 package android.speech.tts {
 
-  public abstract class SynthesisRequest {
-    ctor public SynthesisRequest(java.lang.String, android.os.Bundle);
+  public abstract interface SynthesisCallback {
     method public abstract int audioAvailable(byte[], int, int);
     method public abstract int completeAudioAvailable(int, int, int, byte[], int, int);
     method public abstract int done();
     method public abstract void error();
+    method public abstract int getMaxBufferSize();
+    method public abstract int start(int, int, int);
+  }
+
+  public final class SynthesisRequest {
     method public java.lang.String getCountry();
     method public java.lang.String getLanguage();
-    method public abstract int getMaxBufferSize();
     method public android.os.Bundle getParams();
     method public int getPitch();
     method public int getSpeechRate();
     method public java.lang.String getText();
     method public java.lang.String getVariant();
-    method public abstract int start(int, int, int);
   }
 
   public class TextToSpeech {
@@ -17542,7 +17598,7 @@
     method protected abstract int onIsLanguageAvailable(java.lang.String, java.lang.String, java.lang.String);
     method protected abstract int onLoadLanguage(java.lang.String, java.lang.String, java.lang.String);
     method protected abstract void onStop();
-    method protected abstract void onSynthesizeText(android.speech.tts.SynthesisRequest);
+    method protected abstract void onSynthesizeText(android.speech.tts.SynthesisRequest, android.speech.tts.SynthesisCallback);
   }
 
 }
@@ -17814,6 +17870,7 @@
     field public static final int NETWORK_TYPE_GPRS = 1; // 0x1
     field public static final int NETWORK_TYPE_HSDPA = 8; // 0x8
     field public static final int NETWORK_TYPE_HSPA = 10; // 0xa
+    field public static final int NETWORK_TYPE_HSPAP = 15; // 0xf
     field public static final int NETWORK_TYPE_HSUPA = 9; // 0x9
     field public static final int NETWORK_TYPE_IDEN = 11; // 0xb
     field public static final int NETWORK_TYPE_LTE = 13; // 0xd
@@ -20092,6 +20149,10 @@
     method public void previousMonth();
   }
 
+  public class NoSuchPropertyException extends java.lang.RuntimeException {
+    ctor public NoSuchPropertyException(java.lang.String);
+  }
+
   public class Pair {
     ctor public Pair(F, S);
     method public static android.util.Pair<A, B> create(A, B);
@@ -20127,6 +20188,16 @@
     method public abstract void println(java.lang.String);
   }
 
+  public abstract class Property {
+    ctor public Property(java.lang.Class<V>, java.lang.String);
+    method public abstract V get(T);
+    method public java.lang.String getName();
+    method public java.lang.Class<V> getType();
+    method public boolean isReadOnly();
+    method public static android.util.Property<T, V> of(java.lang.Class<T>, java.lang.Class<V>, java.lang.String);
+    method public void set(T, V);
+  }
+
   public class SparseArray {
     ctor public SparseArray();
     ctor public SparseArray(int);
@@ -20435,14 +20506,12 @@
     method public static int getAbsoluteGravity(int, boolean);
     method public static boolean isHorizontal(int);
     method public static boolean isVertical(int);
-    field public static final int AFTER = 8388613; // 0x800005
     field public static final int AXIS_CLIP = 8; // 0x8
     field public static final int AXIS_PULL_AFTER = 4; // 0x4
     field public static final int AXIS_PULL_BEFORE = 2; // 0x2
     field public static final int AXIS_SPECIFIED = 1; // 0x1
     field public static final int AXIS_X_SHIFT = 0; // 0x0
     field public static final int AXIS_Y_SHIFT = 4; // 0x4
-    field public static final int BEFORE = 8388611; // 0x800003
     field public static final int BOTTOM = 80; // 0x50
     field public static final int CENTER = 17; // 0x11
     field public static final int CENTER_HORIZONTAL = 1; // 0x1
@@ -20451,15 +20520,17 @@
     field public static final int CLIP_VERTICAL = 128; // 0x80
     field public static final int DISPLAY_CLIP_HORIZONTAL = 16777216; // 0x1000000
     field public static final int DISPLAY_CLIP_VERTICAL = 268435456; // 0x10000000
+    field public static final int END = 8388613; // 0x800005
     field public static final int FILL = 119; // 0x77
     field public static final int FILL_HORIZONTAL = 7; // 0x7
     field public static final int FILL_VERTICAL = 112; // 0x70
     field public static final int HORIZONTAL_GRAVITY_MASK = 7; // 0x7
     field public static final int LEFT = 3; // 0x3
     field public static final int NO_GRAVITY = 0; // 0x0
-    field public static final int RELATIVE_HORIZONTAL_DIRECTION = 8388608; // 0x800000
     field public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = 8388615; // 0x800007
+    field public static final int RELATIVE_LAYOUT_DIRECTION = 8388608; // 0x800000
     field public static final int RIGHT = 5; // 0x5
+    field public static final int START = 8388611; // 0x800003
     field public static final int TOP = 48; // 0x30
     field public static final int VERTICAL_GRAVITY_MASK = 112; // 0x70
   }
@@ -21477,6 +21548,7 @@
     method protected int computeVerticalScrollExtent();
     method protected int computeVerticalScrollOffset();
     method protected int computeVerticalScrollRange();
+    method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo();
     method public void createContextMenu(android.view.ContextMenu);
     method public void destroyDrawingCache();
     method public void dispatchConfigurationChanged(android.content.res.Configuration);
@@ -21505,6 +21577,7 @@
     method public android.view.View findFocus();
     method public final android.view.View findViewById(int);
     method public final android.view.View findViewWithTag(java.lang.Object);
+    method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence);
     method protected boolean fitSystemWindows(android.graphics.Rect);
     method public android.view.View focusSearch(int);
     method public void forceLayout();
@@ -21673,6 +21746,7 @@
     method public boolean onGenericMotionEvent(android.view.MotionEvent);
     method public boolean onHoverEvent(android.view.MotionEvent);
     method public void onInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
+    method public void onInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo);
     method public boolean onKeyDown(int, android.view.KeyEvent);
     method public boolean onKeyLongPress(int, android.view.KeyEvent);
     method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
@@ -21870,6 +21944,11 @@
     field protected static final int[] PRESSED_SELECTED_STATE_SET;
     field protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET;
     field protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET;
+    field public static android.util.Property ROTATION;
+    field public static android.util.Property ROTATION_X;
+    field public static android.util.Property ROTATION_Y;
+    field public static android.util.Property SCALE_X;
+    field public static android.util.Property SCALE_Y;
     field public static final int SCROLLBARS_INSIDE_INSET = 16777216; // 0x1000000
     field public static final int SCROLLBARS_INSIDE_OVERLAY = 0; // 0x0
     field public static final int SCROLLBARS_OUTSIDE_INSET = 50331648; // 0x3000000
@@ -21882,9 +21961,13 @@
     field public static final int SOUND_EFFECTS_ENABLED = 134217728; // 0x8000000
     field public static final int STATUS_BAR_HIDDEN = 1; // 0x1
     field public static final int STATUS_BAR_VISIBLE = 0; // 0x0
+    field public static android.util.Property TRANSLATION_X;
+    field public static android.util.Property TRANSLATION_Y;
     field protected static final java.lang.String VIEW_LOG_TAG = "View";
     field public static final int VISIBLE = 0; // 0x0
     field protected static final int[] WINDOW_FOCUSED_STATE_SET;
+    field public static android.util.Property X;
+    field public static android.util.Property Y;
   }
 
   public static class View.BaseSavedState extends android.view.AbsSavedState {
@@ -22566,17 +22649,21 @@
     method public void appendRecord(android.view.accessibility.AccessibilityRecord);
     method public int describeContents();
     method public static java.lang.String eventTypeToString(int);
+    method public int getAccessibilityWindowId();
     method public long getEventTime();
     method public int getEventType();
     method public java.lang.CharSequence getPackageName();
     method public android.view.accessibility.AccessibilityRecord getRecord(int);
     method public int getRecordCount();
+    method public android.view.accessibility.AccessibilityNodeInfo getSource();
     method public void initFromParcel(android.os.Parcel);
     method public static android.view.accessibility.AccessibilityEvent obtain(int);
+    method public static android.view.accessibility.AccessibilityEvent obtain(android.view.accessibility.AccessibilityEvent);
     method public static android.view.accessibility.AccessibilityEvent obtain();
     method public void setEventTime(long);
     method public void setEventType(int);
     method public void setPackageName(java.lang.CharSequence);
+    method public void setSource(android.view.View);
     method public void writeToParcel(android.os.Parcel, int);
     field public static final android.os.Parcelable.Creator CREATOR;
     field public static final int INVALID_POSITION = -1; // 0xffffffff
@@ -22601,20 +22688,75 @@
   }
 
   public final class AccessibilityManager {
+    method public boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
     method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
     method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int);
     method public java.util.List<android.accessibilityservice.AccessibilityServiceInfo> getInstalledAccessibilityServiceList();
     method public void interrupt();
     method public boolean isEnabled();
+    method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
     method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
   }
 
+  public static abstract interface AccessibilityManager.AccessibilityStateChangeListener {
+    method public abstract void onAccessibilityStateChanged(boolean);
+  }
+
+  public class AccessibilityNodeInfo implements android.os.Parcelable {
+    method public void addAction(int);
+    method public void addChild(android.view.View);
+    method public int describeContents();
+    method public int getAccessibilityWindowId();
+    method public int getActions();
+    method public void getBounds(android.graphics.Rect);
+    method public android.view.accessibility.AccessibilityNodeInfo getChild(int);
+    method public int getChildCount();
+    method public java.lang.CharSequence getClassName();
+    method public java.lang.CharSequence getContentDescription();
+    method public java.lang.CharSequence getPackageName();
+    method public android.view.accessibility.AccessibilityNodeInfo getParent();
+    method public java.lang.CharSequence getText();
+    method public boolean isCheckable();
+    method public boolean isChecked();
+    method public boolean isClickable();
+    method public boolean isEnabled();
+    method public boolean isFocusable();
+    method public boolean isFocused();
+    method public boolean isLongClickable();
+    method public boolean isPassword();
+    method public boolean isSelected();
+    method public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
+    method public static android.view.accessibility.AccessibilityNodeInfo obtain();
+    method public boolean performAction(int);
+    method public void recycle();
+    method public void setBounds(android.graphics.Rect);
+    method public void setCheckable(boolean);
+    method public void setChecked(boolean);
+    method public void setClassName(java.lang.CharSequence);
+    method public void setClickable(boolean);
+    method public void setContentDescription(java.lang.CharSequence);
+    method public void setEnabled(boolean);
+    method public void setFocusable(boolean);
+    method public void setFocused(boolean);
+    method public void setLongClickable(boolean);
+    method public void setPackageName(java.lang.CharSequence);
+    method public void setParent(android.view.View);
+    method public void setPassword(boolean);
+    method public void setSelected(boolean);
+    method public void setSource(android.view.View);
+    method public void setText(java.lang.CharSequence);
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int ACTION_CLEAR_FOCUS = 2; // 0x2
+    field public static final int ACTION_CLEAR_SELECTION = 8; // 0x8
+    field public static final int ACTION_FOCUS = 1; // 0x1
+    field public static final int ACTION_SELECT = 4; // 0x4
+    field public static final android.os.Parcelable.Creator CREATOR;
+  }
+
   public class AccessibilityRecord {
     ctor protected AccessibilityRecord();
-    method protected void clear();
     method public int getAddedCount();
     method public java.lang.CharSequence getBeforeText();
-    method public boolean getBooleanProperty(int);
     method public java.lang.CharSequence getClassName();
     method public java.lang.CharSequence getContentDescription();
     method public int getCurrentItemIndex();
@@ -22627,6 +22769,7 @@
     method public boolean isEnabled();
     method public boolean isFullScreen();
     method public boolean isPassword();
+    method public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord);
     method public static android.view.accessibility.AccessibilityRecord obtain();
     method public void recycle();
     method public void setAddedCount(int);
@@ -23211,6 +23354,8 @@
   }
 
   public final class InputMethodSubtype implements android.os.Parcelable {
+    ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String);
+    ctor public InputMethodSubtype(int, int, java.lang.String, java.lang.String, java.lang.String, boolean);
     method public boolean containsExtraValueKey(java.lang.String);
     method public int describeContents();
     method public java.lang.CharSequence getDisplayName(android.content.Context, java.lang.String, android.content.pm.ApplicationInfo);
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 38cacdd..424b70a 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -98,6 +98,8 @@
             runStart();
         } else if (op.equals("startservice")) {
             runStartService();
+        } else if (op.equals("force-stop")) {
+            runForceStop();
         } else if (op.equals("instrument")) {
             runInstrument();
         } else if (op.equals("broadcast")) {
@@ -365,6 +367,10 @@
         }
     }
 
+    private void runForceStop() throws Exception {
+        mAm.forceStopPackage(nextArgRequired());
+    }
+
     private void sendBroadcast() throws Exception {
         Intent intent = makeIntent();
         IntentReceiver receiver = new IntentReceiver();
@@ -851,7 +857,7 @@
                 wm.clearForcedDisplaySize();
             }
         } catch (RemoteException e) {
-       }
+        }
     }
 
     private class IntentReceiver extends IIntentReceiver.Stub {
@@ -1013,6 +1019,8 @@
                 "\n" +
                 "    start a Service: am startservice <INTENT>\n" +
                 "\n" +
+                "    force stop everything associated with a package: force-stop <package>\n" +
+                "\n" +
                 "    send a broadcast Intent: am broadcast <INTENT>\n" +
                 "\n" +
                 "    start an Instrumentation: am instrument [flags] <COMPONENT>\n" +
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 28fc21a..8bb305d 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -25,6 +25,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
  * An accessibility service runs in the background and receives callbacks by the system
@@ -51,7 +52,11 @@
  * enabling or disabling it in the device settings. After the system binds to a service it
  * calls {@link AccessibilityService#onServiceConnected()}. This method can be
  * overriden by clients that want to perform post binding setup.
+ * </p>
  * <p>
+ * An accessibility service can be configured to receive specific types of accessibility events,
+ * listen only to specific packages, get events from each type only once in a given time frame,
+ * retrieve window content, specify a settings activity, etc.
  * </p>
  * There are two approaches for configuring an accessibility service:
  * <ul>
@@ -59,20 +64,23 @@
  *     Providing a {@link #SERVICE_META_DATA meta-data} entry in the manifest when declaring
  *     the service. A service declaration with a meta-data tag is presented below:
  *     <p>
- *     <code>
- *       &lt;service android:name=".MyAccessibilityService"&gt;<br>
- *       &nbsp;&nbsp;&lt;intent-filter&gt;<br>
- *       &nbsp;&nbsp;&nbsp;&nbsp;&lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;<br>
- *       &nbsp;&nbsp;&lt;/intent-filter&gt;<br>
- *       &nbsp;&nbsp;&lt;meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /&gt;<br>
- *       &lt;/service&gt;<br>
- *     </code>
+ *       <code>
+ *         &lt;service android:name=".MyAccessibilityService"&gt;<br>
+ *         &nbsp;&nbsp;&lt;intent-filter&gt;<br>
+ *         &nbsp;&nbsp;&nbsp;&nbsp;&lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;<br>
+ *         &nbsp;&nbsp;&lt;/intent-filter&gt;<br>
+ *         &nbsp;&nbsp;&lt;meta-data android:name="android.accessibilityservice.as" android:resource="@xml/accessibilityservice" /&gt;<br>
+ *         &lt;/service&gt;<br>
+ *       </code>
  *     </p>
  *     <p>
  *     <strong>
  *       This approach enables setting all accessibility service properties.
  *     </strong>
  *     </p>
+ *     <p>
+ *       For more details refer to {@link #SERVICE_META_DATA}.
+ *     </p>
  *   </li>
  *   <li>
  *     Calling {@link AccessibilityService#setServiceInfo(AccessibilityServiceInfo)}. Note
@@ -88,6 +96,9 @@
  *       {@link AccessibilityServiceInfo#packageNames}
  *     </strong>
  *     </p>
+ *     <p>
+ *       For more details refer to {@link AccessibilityServiceInfo}.
+ *     </p>
  *   </li>
  * </ul>
  * <p>
@@ -151,16 +162,49 @@
      * <code>
      *   &lt;?xml version="1.0" encoding="utf-8"?&gt;<br>
      *   &lt;accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"<br>
-     *   &nbsp;&nbsp;android:eventTypes="typeViewClicked|typeViewFocused"<br>
+     *   &nbsp;&nbsp;android:accessibilityEventTypes="typeViewClicked|typeViewFocused"<br>
      *   &nbsp;&nbsp;android:packageNames="foo.bar, foo.baz"<br>
-     *   &nbsp;&nbsp;android:feedbackType="feedbackSpoken"<br>
+     *   &nbsp;&nbsp;android:accessibilityFeedbackType="feedbackSpoken"<br>
      *   &nbsp;&nbsp;android:notificationTimeout="100"<br>
-     *   &nbsp;&nbsp;android:flags="flagDefault"<br>
+     *   &nbsp;&nbsp;android:accessibilityFlags="flagDefault"<br>
      *   &nbsp;&nbsp;android:settingsActivity="foo.bar.TestBackActivity"<br>
      *   &nbsp;&nbsp;. . .<br>
      *   /&gt;
      * </code>
      * </p>
+     * <p>
+     *  <strong>Note:</strong> A service can retrieve only the content of the active window.
+     *          An active window is the source of the most recent event of type
+     *          {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START},
+     *          {@link AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END},
+     *          {@link AccessibilityEvent#TYPE_VIEW_CLICKED},
+     *          {@link AccessibilityEvent#TYPE_VIEW_FOCUSED},
+     *          {@link AccessibilityEvent#TYPE_VIEW_HOVER_ENTER},
+     *          {@link AccessibilityEvent#TYPE_VIEW_HOVER_EXIT},
+     *          {@link AccessibilityEvent#TYPE_VIEW_LONG_CLICKED},
+     *          {@link AccessibilityEvent#TYPE_VIEW_SELECTED},
+     *          {@link AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED},
+     *          {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED}.
+     *          Therefore the service should:
+     *          <ul>
+     *            <li>
+     *              Register for all event types with no notification timeout and keep track
+     *              for the active window by calling
+     *              {@link AccessibilityEvent#getAccessibilityWindowId()} of the last received
+     *              event and compare this with the
+     *              {@link AccessibilityNodeInfo#getAccessibilityWindowId()} before calling
+     *              retrieval methods on the latter.
+     *            </li>
+     *            <li>
+     *              Prepare that a retrieval method on {@link AccessibilityNodeInfo} may fail
+     *              since the active window has changed and the service did not get the 
+     *              accessibility event. Note that it is possible to have a retrieval method
+     *              failing event adopting the strategy specified in the previous bullet
+     *              because the accessibility event dispatch is asynchronous and crosses
+     *              process boundaries. 
+     *            </li>
+     *          <ul>
+     * </p>
      */
     public static final String SERVICE_META_DATA = "android.accessibilityservice";
 
@@ -224,7 +268,7 @@
 
     /**
      * Implement to return the implementation of the internal accessibility
-     * service interface.  Subclasses should not override.
+     * service interface.
      */
     @Override
     public final IBinder onBind(Intent intent) {
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 7157def..19f0bf0 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -17,14 +17,72 @@
 package android.accessibilityservice;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.view.accessibility.AccessibilityNodeInfo;
 
 /**
- * Interface AccessibilityManagerService#Service implements, and passes to an
- * AccessibilityService so it can dynamically configure how the system handles it.
+ * Interface given to an AccessibilitySerivce to talk to the AccessibilityManagerService.
  *
  * @hide
  */
-oneway interface IAccessibilityServiceConnection {
+interface IAccessibilityServiceConnection {
 
     void setServiceInfo(in AccessibilityServiceInfo info);
+
+    /**
+     * Finds an {@link AccessibilityNodeInfo} by accessibility id.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     *
+     * @param accessibilityWindowId A unique window id.
+     * @param accessibilityViewId A unique View accessibility id.
+     * @return The node info.
+     */
+    AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int accessibilityWindowId,
+        int accessibilityViewId);
+
+    /**
+     * Finds {@link AccessibilityNodeInfo}s by View text. The match is case
+     * insensitive containment.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received infos by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     *
+     * @param text The searched text.
+     * @return A list of node info.
+     */
+    List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text);
+
+    /**
+     * Finds an {@link AccessibilityNodeInfo} by View id.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     *
+     * @param id The id of the node.
+     * @return The node info.
+     */
+    AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId);
+
+    /**
+     * Performs an accessibility action on an {@link AccessibilityNodeInfo}.
+     *
+     * @param accessibilityWindowId The id of the window.
+     * @param accessibilityViewId The of a view in the .
+     * @return Whether the action was performed.
+     */
+    boolean performAccessibilityAction(int accessibilityWindowId, int accessibilityViewId,
+        int action);
 }
diff --git a/core/java/android/animation/FloatEvaluator.java b/core/java/android/animation/FloatEvaluator.java
index 9e2054d..9463aa1 100644
--- a/core/java/android/animation/FloatEvaluator.java
+++ b/core/java/android/animation/FloatEvaluator.java
@@ -19,7 +19,7 @@
 /**
  * This evaluator can be used to perform type interpolation between <code>float</code> values.
  */
-public class FloatEvaluator implements TypeEvaluator {
+public class FloatEvaluator implements TypeEvaluator<Number> {
 
     /**
      * This function returns the result of linearly interpolating the start and end values, with
@@ -35,8 +35,8 @@
      * @return A linear interpolation between the start and end values, given the
      *         <code>fraction</code> parameter.
      */
-    public Object evaluate(float fraction, Object startValue, Object endValue) {
-        float startFloat = ((Number) startValue).floatValue();
-        return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
+    public Float evaluate(float fraction, Number startValue, Number endValue) {
+        float startFloat = startValue.floatValue();
+        return startFloat + fraction * (endValue.floatValue() - startFloat);
     }
 }
\ No newline at end of file
diff --git a/core/java/android/animation/IntEvaluator.java b/core/java/android/animation/IntEvaluator.java
index 7288927..34fb0dc 100644
--- a/core/java/android/animation/IntEvaluator.java
+++ b/core/java/android/animation/IntEvaluator.java
@@ -19,7 +19,7 @@
 /**
  * This evaluator can be used to perform type interpolation between <code>int</code> values.
  */
-public class IntEvaluator implements TypeEvaluator {
+public class IntEvaluator implements TypeEvaluator<Integer> {
 
     /**
      * This function returns the result of linearly interpolating the start and end values, with
@@ -35,8 +35,8 @@
      * @return A linear interpolation between the start and end values, given the
      *         <code>fraction</code> parameter.
      */
-    public Object evaluate(float fraction, Object startValue, Object endValue) {
-        int startInt = ((Number) startValue).intValue();
-        return (int) (startInt + fraction * (((Number) endValue).intValue() - startInt));
+    public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
+        int startInt = startValue;
+        return (int)(startInt + fraction * (endValue - startInt));
     }
 }
\ No newline at end of file
diff --git a/core/java/android/animation/ObjectAnimator.java b/core/java/android/animation/ObjectAnimator.java
index b8a7cb2..31c5f8d 100644
--- a/core/java/android/animation/ObjectAnimator.java
+++ b/core/java/android/animation/ObjectAnimator.java
@@ -17,6 +17,7 @@
 package android.animation;
 
 import android.util.Log;
+import android.util.Property;
 
 import java.lang.reflect.Method;
 import java.util.ArrayList;
@@ -39,6 +40,8 @@
 
     private String mPropertyName;
 
+    private Property mProperty;
+
     /**
      * Sets the name of the property that will be animated. This name is used to derive
      * a setter function that will be called to set animated values.
@@ -63,7 +66,7 @@
      * using more than one PropertyValuesHolder objects, then setting the propertyName simply
      * sets the propertyName in the first of those PropertyValuesHolder objects.</p>
      *
-     * @param propertyName The name of the property being animated.
+     * @param propertyName The name of the property being animated. Should not be null.
      */
     public void setPropertyName(String propertyName) {
         // mValues could be null if this is being constructed piecemeal. Just record the
@@ -81,6 +84,31 @@
     }
 
     /**
+     * Sets the property that will be animated. Property objects will take precedence over
+     * properties specified by the {@link #setPropertyName(String)} method. Animations should
+     * be set up to use one or the other, not both.
+     *
+     * @param property The property being animated. Should not be null.
+     */
+    public void setProperty(Property property) {
+        // mValues could be null if this is being constructed piecemeal. Just record the
+        // propertyName to be used later when setValues() is called if so.
+        if (mValues != null) {
+            PropertyValuesHolder valuesHolder = mValues[0];
+            String oldName = valuesHolder.getPropertyName();
+            valuesHolder.setProperty(property);
+            mValuesMap.remove(oldName);
+            mValuesMap.put(mPropertyName, valuesHolder);
+        }
+        if (mProperty != null) {
+            mPropertyName = property.getName();
+        }
+        mProperty = property;
+        // New property/values/target should cause re-initialization prior to starting
+        mInitialized = false;
+    }
+
+    /**
      * Gets the name of the property that will be animated. This name will be used to derive
      * a setter function that will be called to set animated values.
      * For example, a property name of <code>foo</code> will result
@@ -93,36 +121,6 @@
     }
 
     /**
-     * Determine the setter or getter function using the JavaBeans convention of setFoo or
-     * getFoo for a property named 'foo'. This function figures out what the name of the
-     * function should be and uses reflection to find the Method with that name on the
-     * target object.
-     *
-     * @param prefix "set" or "get", depending on whether we need a setter or getter.
-     * @return Method the method associated with mPropertyName.
-     */
-    private Method getPropertyFunction(String prefix, Class valueType) {
-        // TODO: faster implementation...
-        Method returnVal = null;
-        String firstLetter = mPropertyName.substring(0, 1);
-        String theRest = mPropertyName.substring(1);
-        firstLetter = firstLetter.toUpperCase();
-        String setterName = prefix + firstLetter + theRest;
-        Class args[] = null;
-        if (valueType != null) {
-            args = new Class[1];
-            args[0] = valueType;
-        }
-        try {
-            returnVal = mTarget.getClass().getMethod(setterName, args);
-        } catch (NoSuchMethodException e) {
-            Log.e("ObjectAnimator",
-                    "Couldn't find setter/getter for property " + mPropertyName + ": " + e);
-        }
-        return returnVal;
-    }
-
-    /**
      * Creates a new ObjectAnimator object. This default constructor is primarily for
      * use internally; the other constructors which take parameters are more generally
      * useful.
@@ -131,8 +129,8 @@
     }
 
     /**
-     * A constructor that takes a single property name and set of values. This constructor is
-     * used in the simple case of animating a single property.
+     * Private utility constructor that initializes the target object and name of the
+     * property being animated.
      *
      * @param target The object whose property is to be animated. This object should
      * have a public method on it called <code>setName()</code>, where <code>name</code> is
@@ -145,19 +143,29 @@
     }
 
     /**
+     * Private utility constructor that initializes the target object and property being animated.
+     *
+     * @param target The object whose property is to be animated.
+     * @param property The property being animated.
+     */
+    private <T> ObjectAnimator(T target, Property<T, ?> property) {
+        mTarget = target;
+        setProperty(property);
+    }
+
+    /**
      * Constructs and returns an ObjectAnimator that animates between int values. A single
-     * value implies that that value is the one being animated to. However, this is not typically
-     * useful in a ValueAnimator object because there is no way for the object to determine the
-     * starting value for the animation (unlike ObjectAnimator, which can derive that value
-     * from the target object and property being animated). Therefore, there should typically
-     * be two or more values.
+     * value implies that that value is the one being animated to. Two values imply a starting
+     * and ending values. More than two values imply a starting value, values to animate through
+     * along the way, and an ending value (these values will be distributed evenly across
+     * the duration of the animation).
      *
      * @param target The object whose property is to be animated. This object should
      * have a public method on it called <code>setName()</code>, where <code>name</code> is
      * the value of the <code>propertyName</code> parameter.
      * @param propertyName The name of the property being animated.
      * @param values A set of values that the animation will animate between over time.
-     * @return A ValueAnimator object that is set up to animate between the given values.
+     * @return An ObjectAnimator object that is set up to animate between the given values.
      */
     public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
         ObjectAnimator anim = new ObjectAnimator(target, propertyName);
@@ -166,19 +174,36 @@
     }
 
     /**
+     * Constructs and returns an ObjectAnimator that animates between int values. A single
+     * value implies that that value is the one being animated to. Two values imply a starting
+     * and ending values. More than two values imply a starting value, values to animate through
+     * along the way, and an ending value (these values will be distributed evenly across
+     * the duration of the animation).
+     *
+     * @param target The object whose property is to be animated.
+     * @param property The property being animated.
+     * @param values A set of values that the animation will animate between over time.
+     * @return An ObjectAnimator object that is set up to animate between the given values.
+     */
+    public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
+        ObjectAnimator anim = new ObjectAnimator(target, property);
+        anim.setIntValues(values);
+        return anim;
+    }
+
+    /**
      * Constructs and returns an ObjectAnimator that animates between float values. A single
-     * value implies that that value is the one being animated to. However, this is not typically
-     * useful in a ValueAnimator object because there is no way for the object to determine the
-     * starting value for the animation (unlike ObjectAnimator, which can derive that value
-     * from the target object and property being animated). Therefore, there should typically
-     * be two or more values.
+     * value implies that that value is the one being animated to. Two values imply a starting
+     * and ending values. More than two values imply a starting value, values to animate through
+     * along the way, and an ending value (these values will be distributed evenly across
+     * the duration of the animation).
      *
      * @param target The object whose property is to be animated. This object should
      * have a public method on it called <code>setName()</code>, where <code>name</code> is
      * the value of the <code>propertyName</code> parameter.
      * @param propertyName The name of the property being animated.
      * @param values A set of values that the animation will animate between over time.
-     * @return A ValueAnimator object that is set up to animate between the given values.
+     * @return An ObjectAnimator object that is set up to animate between the given values.
      */
     public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
         ObjectAnimator anim = new ObjectAnimator(target, propertyName);
@@ -187,21 +212,40 @@
     }
 
     /**
-     * A constructor that takes <code>PropertyValueHolder</code> values. This constructor should
-     * be used when animating several properties at once with the same ObjectAnimator, since
-     * PropertyValuesHolder allows you to associate a set of animation values with a property
-     * name.
+     * Constructs and returns an ObjectAnimator that animates between float values. A single
+     * value implies that that value is the one being animated to. Two values imply a starting
+     * and ending values. More than two values imply a starting value, values to animate through
+     * along the way, and an ending value (these values will be distributed evenly across
+     * the duration of the animation).
+     *
+     * @param target The object whose property is to be animated.
+     * @param property The property being animated.
+     * @param values A set of values that the animation will animate between over time.
+     * @return An ObjectAnimator object that is set up to animate between the given values.
+     */
+    public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
+            float... values) {
+        ObjectAnimator anim = new ObjectAnimator(target, property);
+        anim.setFloatValues(values);
+        return anim;
+    }
+
+    /**
+     * Constructs and returns an ObjectAnimator that animates between Object values. A single
+     * value implies that that value is the one being animated to. Two values imply a starting
+     * and ending values. More than two values imply a starting value, values to animate through
+     * along the way, and an ending value (these values will be distributed evenly across
+     * the duration of the animation).
      *
      * @param target The object whose property is to be animated. This object should
-     * have public methods on it called <code>setName()</code>, where <code>name</code> is
-     * the name of the property passed in as the <code>propertyName</code> parameter for
-     * each of the PropertyValuesHolder objects.
+     * have a public method on it called <code>setName()</code>, where <code>name</code> is
+     * the value of the <code>propertyName</code> parameter.
      * @param propertyName The name of the property being animated.
      * @param evaluator A TypeEvaluator that will be called on each animation frame to
-     * provide the ncessry interpolation between the Object values to derive the animated
+     * provide the necessary interpolation between the Object values to derive the animated
      * value.
-     * @param values The PropertyValuesHolder objects which hold each the property name and values
-     * to animate that property between.
+     * @param values A set of values that the animation will animate between over time.
+     * @return An ObjectAnimator object that is set up to animate between the given values.
      */
     public static ObjectAnimator ofObject(Object target, String propertyName,
             TypeEvaluator evaluator, Object... values) {
@@ -212,19 +256,44 @@
     }
 
     /**
-     * Constructs and returns an ObjectAnimator that animates between the sets of values
-     * specifed in <code>PropertyValueHolder</code> objects. This variant should
-     * be used when animating several properties at once with the same ObjectAnimator, since
-     * PropertyValuesHolder allows you to associate a set of animation values with a property
-     * name.
+     * Constructs and returns an ObjectAnimator that animates between Object values. A single
+     * value implies that that value is the one being animated to. Two values imply a starting
+     * and ending values. More than two values imply a starting value, values to animate through
+     * along the way, and an ending value (these values will be distributed evenly across
+     * the duration of the animation).
      *
-     * @param target The object whose property is to be animated. This object should
-     * have public methods on it called <code>setName()</code>, where <code>name</code> is
-     * the name of the property passed in as the <code>propertyName</code> parameter for
-     * each of the PropertyValuesHolder objects.
-     * @param values A set of PropertyValuesHolder objects whose values will be animated
-     * between over time.
-     * @return A ValueAnimator object that is set up to animate between the given values.
+     * @param target The object whose property is to be animated.
+     * @param property The property being animated.
+     * @param evaluator A TypeEvaluator that will be called on each animation frame to
+     * provide the necessary interpolation between the Object values to derive the animated
+     * value.
+     * @param values A set of values that the animation will animate between over time.
+     * @return An ObjectAnimator object that is set up to animate between the given values.
+     */
+    public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property,
+            TypeEvaluator<V> evaluator, V... values) {
+        ObjectAnimator anim = new ObjectAnimator(target, property);
+        anim.setObjectValues(values);
+        anim.setEvaluator(evaluator);
+        return anim;
+    }
+
+    /**
+     * Constructs and returns an ObjectAnimator that animates between the sets of values specified
+     * in <code>PropertyValueHolder</code> objects. This variant should be used when animating
+     * several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
+     * you to associate a set of animation values with a property name.
+     *
+     * @param target The object whose property is to be animated. Depending on how the
+     * PropertyValuesObjects were constructed, the target object should either have the {@link
+     * android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
+     * PropertyValuesHOlder objects were created with property names) the target object should have
+     * public methods on it called <code>setName()</code>, where <code>name</code> is the name of
+     * the property passed in as the <code>propertyName</code> parameter for each of the
+     * PropertyValuesHolder objects.
+     * @param values A set of PropertyValuesHolder objects whose values will be animated between
+     * over time.
+     * @return An ObjectAnimator object that is set up to animate between the given values.
      */
     public static ObjectAnimator ofPropertyValuesHolder(Object target,
             PropertyValuesHolder... values) {
@@ -239,7 +308,11 @@
         if (mValues == null || mValues.length == 0) {
             // No values yet - this animator is being constructed piecemeal. Init the values with
             // whatever the current propertyName is
-            setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
+            if (mProperty != null) {
+                setValues(PropertyValuesHolder.ofInt(mProperty, values));
+            } else {
+                setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
+            }
         } else {
             super.setIntValues(values);
         }
@@ -250,7 +323,11 @@
         if (mValues == null || mValues.length == 0) {
             // No values yet - this animator is being constructed piecemeal. Init the values with
             // whatever the current propertyName is
-            setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
+            if (mProperty != null) {
+                setValues(PropertyValuesHolder.ofFloat(mProperty, values));
+            } else {
+                setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
+            }
         } else {
             super.setFloatValues(values);
         }
@@ -261,7 +338,11 @@
         if (mValues == null || mValues.length == 0) {
             // No values yet - this animator is being constructed piecemeal. Init the values with
             // whatever the current propertyName is
-            setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
+            if (mProperty != null) {
+                setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values));
+            } else {
+                setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
+            }
         } else {
             super.setObjectValues(values);
         }
diff --git a/core/java/android/animation/PropertyValuesHolder.java b/core/java/android/animation/PropertyValuesHolder.java
index 6f91fc0..58f23f7 100644
--- a/core/java/android/animation/PropertyValuesHolder.java
+++ b/core/java/android/animation/PropertyValuesHolder.java
@@ -16,7 +16,10 @@
 
 package android.animation;
 
+import android.util.FloatProperty;
+import android.util.IntProperty;
 import android.util.Log;
+import android.util.Property;
 
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -39,6 +42,11 @@
     String mPropertyName;
 
     /**
+     * @hide
+     */
+    protected Property mProperty;
+
+    /**
      * The setter function, if needed. ObjectAnimator hands off this functionality to
      * PropertyValuesHolder, since it holds all of the per-property information. This
      * property is automatically
@@ -124,6 +132,17 @@
     }
 
     /**
+     * Internal utility constructor, used by the factory methods to set the property.
+     * @param property The property for this holder.
+     */
+    private PropertyValuesHolder(Property property) {
+        mProperty = property;
+        if (property != null) {
+            mPropertyName = property.getName();
+        }
+    }
+
+    /**
      * Constructs and returns a PropertyValuesHolder with a given property name and
      * set of int values.
      * @param propertyName The name of the property being animated.
@@ -131,8 +150,18 @@
      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
      */
     public static PropertyValuesHolder ofInt(String propertyName, int... values) {
-        PropertyValuesHolder pvh = new IntPropertyValuesHolder(propertyName, values);
-        return pvh;
+        return new IntPropertyValuesHolder(propertyName, values);
+    }
+
+    /**
+     * Constructs and returns a PropertyValuesHolder with a given property and
+     * set of int values.
+     * @param property The property being animated. Should not be null.
+     * @param values The values that the property will animate between.
+     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+     */
+    public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
+        return new IntPropertyValuesHolder(property, values);
     }
 
     /**
@@ -143,18 +172,28 @@
      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
      */
     public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
-        PropertyValuesHolder pvh = new FloatPropertyValuesHolder(propertyName, values);
-        return pvh;
+        return new FloatPropertyValuesHolder(propertyName, values);
+    }
+
+    /**
+     * Constructs and returns a PropertyValuesHolder with a given property and
+     * set of float values.
+     * @param property The property being animated. Should not be null.
+     * @param values The values that the property will animate between.
+     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+     */
+    public static PropertyValuesHolder ofFloat(Property<?, Float> property, float... values) {
+        return new FloatPropertyValuesHolder(property, values);
     }
 
     /**
      * Constructs and returns a PropertyValuesHolder with a given property name and
      * set of Object values. This variant also takes a TypeEvaluator because the system
-     * cannot interpolate between objects of unknown type.
+     * cannot automatically interpolate between objects of unknown type.
      *
      * @param propertyName The name of the property being animated.
      * @param evaluator A TypeEvaluator that will be called on each animation frame to
-     * provide the ncessry interpolation between the Object values to derive the animated
+     * provide the necessary interpolation between the Object values to derive the animated
      * value.
      * @param values The values that the named property will animate between.
      * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
@@ -168,6 +207,26 @@
     }
 
     /**
+     * Constructs and returns a PropertyValuesHolder with a given property and
+     * set of Object values. This variant also takes a TypeEvaluator because the system
+     * cannot automatically interpolate between objects of unknown type.
+     *
+     * @param property The property being animated. Should not be null.
+     * @param evaluator A TypeEvaluator that will be called on each animation frame to
+     * provide the necessary interpolation between the Object values to derive the animated
+     * value.
+     * @param values The values that the property will animate between.
+     * @return PropertyValuesHolder The constructed PropertyValuesHolder object.
+     */
+    public static <V> PropertyValuesHolder ofObject(Property property,
+            TypeEvaluator<V> evaluator, V... values) {
+        PropertyValuesHolder pvh = new PropertyValuesHolder(property);
+        pvh.setObjectValues(values);
+        pvh.setEvaluator(evaluator);
+        return pvh;
+    }
+
+    /**
      * Constructs and returns a PropertyValuesHolder object with the specified property name and set
      * of values. These values can be of any type, but the type should be consistent so that
      * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
@@ -202,6 +261,37 @@
     }
 
     /**
+     * Constructs and returns a PropertyValuesHolder object with the specified property and set
+     * of values. These values can be of any type, but the type should be consistent so that
+     * an appropriate {@link android.animation.TypeEvaluator} can be found that matches
+     * the common type.
+     * <p>If there is only one value, it is assumed to be the end value of an animation,
+     * and an initial value will be derived, if possible, by calling the property's
+     * {@link android.util.Property#get(Object)} function.
+     * Also, if any value is null, the value will be filled in when the animation
+     * starts in the same way. This mechanism of automatically getting null values only works
+     * if the PropertyValuesHolder object is used in conjunction with
+     * {@link ObjectAnimator}, since otherwise PropertyValuesHolder has
+     * no way of determining what the value should be.
+     * @param property The property associated with this set of values. Should not be null.
+     * @param values The set of values to animate between.
+     */
+    public static PropertyValuesHolder ofKeyframe(Property property, Keyframe... values) {
+        KeyframeSet keyframeSet = KeyframeSet.ofKeyframe(values);
+        if (keyframeSet instanceof IntKeyframeSet) {
+            return new IntPropertyValuesHolder(property, (IntKeyframeSet) keyframeSet);
+        } else if (keyframeSet instanceof FloatKeyframeSet) {
+            return new FloatPropertyValuesHolder(property, (FloatKeyframeSet) keyframeSet);
+        }
+        else {
+            PropertyValuesHolder pvh = new PropertyValuesHolder(property);
+            pvh.mKeyframeSet = keyframeSet;
+            pvh.mValueType = ((Keyframe)values[0]).getType();
+            return pvh;
+        }
+    }
+
+    /**
      * Set the animated values for this object to this set of ints.
      * If there is only one value, it is assumed to be the end value of an animation,
      * and an initial value will be derived, if possible, by calling a getter function
@@ -349,7 +439,6 @@
             // Have to lock property map prior to reading it, to guard against
             // another thread putting something in there after we've checked it
             // but before we've added an entry to it
-            // TODO: can we store the setter/getter per Class instead of per Object?
             mPropertyMapLock.writeLock().lock();
             HashMap<String, Method> propertyMap = propertyMapMap.get(targetClass);
             if (propertyMap != null) {
@@ -395,6 +484,22 @@
      * @param target The object on which the setter (and possibly getter) exist.
      */
     void setupSetterAndGetter(Object target) {
+        if (mProperty != null) {
+            // check to make sure that mProperty is on the class of target
+            try {
+                Object testValue = mProperty.get(target);
+                for (Keyframe kf : mKeyframeSet.mKeyframes) {
+                    if (!kf.hasValue()) {
+                        kf.setValue(mProperty.get(target));
+                    }
+                }
+                return;
+            } catch (ClassCastException e) {
+                Log.e("PropertyValuesHolder","No such property (" + mProperty.getName() +
+                        ") on target object " + target + ". Trying reflection instead");
+                mProperty = null;
+            }
+        }
         Class targetClass = target.getClass();
         if (mSetter == null) {
             setupSetter(targetClass);
@@ -423,6 +528,9 @@
      * @param kf The keyframe which holds the property name and value.
      */
     private void setupValue(Object target, Keyframe kf) {
+        if (mProperty != null) {
+            kf.setValue(mProperty.get(target));
+        }
         try {
             if (mGetter == null) {
                 Class targetClass = target.getClass();
@@ -465,6 +573,7 @@
         try {
             PropertyValuesHolder newPVH = (PropertyValuesHolder) super.clone();
             newPVH.mPropertyName = mPropertyName;
+            newPVH.mProperty = mProperty;
             newPVH.mKeyframeSet = mKeyframeSet.clone();
             newPVH.mEvaluator = mEvaluator;
             return newPVH;
@@ -482,6 +591,9 @@
      * @param target The target object on which the value is set
      */
     void setAnimatedValue(Object target) {
+        if (mProperty != null) {
+            mProperty.set(target, getAnimatedValue());
+        }
         if (mSetter != null) {
             try {
                 mTmpValueArray[0] = getAnimatedValue();
@@ -558,6 +670,18 @@
     }
 
     /**
+     * Sets the property that will be animated.
+     *
+     * <p>Note that if this PropertyValuesHolder object is used with ObjectAnimator, the property
+     * must exist on the target object specified in that ObjectAnimator.</p>
+     *
+     * @param property The property being animated.
+     */
+    public void setProperty(Property property) {
+        mProperty = property;
+    }
+
+    /**
      * Gets the name of the property that will be animated. This name will be used to derive
      * a setter function that will be called to set animated values.
      * For example, a property name of <code>foo</code> will result
@@ -597,17 +721,22 @@
      * specified above.
      */
     static String getMethodName(String prefix, String propertyName) {
-        char firstLetter = propertyName.charAt(0);
+        if (propertyName == null || propertyName.length() == 0) {
+            // shouldn't get here
+            return prefix;
+        }
+        char firstLetter = Character.toUpperCase(propertyName.charAt(0));
         String theRest = propertyName.substring(1);
-        firstLetter = Character.toUpperCase(firstLetter);
         return prefix + firstLetter + theRest;
     }
 
     static class IntPropertyValuesHolder extends PropertyValuesHolder {
 
+        // Cache JNI functions to avoid looking them up twice
         private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
                 new HashMap<Class, HashMap<String, Integer>>();
         int mJniSetter;
+        private IntProperty mIntProperty;
 
         IntKeyframeSet mIntKeyframeSet;
         int mIntAnimatedValue;
@@ -619,11 +748,29 @@
             mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
         }
 
+        public IntPropertyValuesHolder(Property property, IntKeyframeSet keyframeSet) {
+            super(property);
+            mValueType = int.class;
+            mKeyframeSet = keyframeSet;
+            mIntKeyframeSet = (IntKeyframeSet) mKeyframeSet;
+            if (property instanceof  IntProperty) {
+                mIntProperty = (IntProperty) mProperty;
+            }
+        }
+
         public IntPropertyValuesHolder(String propertyName, int... values) {
             super(propertyName);
             setIntValues(values);
         }
 
+        public IntPropertyValuesHolder(Property property, int... values) {
+            super(property);
+            setIntValues(values);
+            if (property instanceof  IntProperty) {
+                mIntProperty = (IntProperty) mProperty;
+            }
+        }
+
         @Override
         public void setIntValues(int... values) {
             super.setIntValues(values);
@@ -656,6 +803,14 @@
          */
         @Override
         void setAnimatedValue(Object target) {
+            if (mIntProperty != null) {
+                mIntProperty.setValue(target, mIntAnimatedValue);
+                return;
+            }
+            if (mProperty != null) {
+                mProperty.set(target, mIntAnimatedValue);
+                return;
+            }
             if (mJniSetter != 0) {
                 nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
                 return;
@@ -674,6 +829,9 @@
 
         @Override
         void setupSetter(Class targetClass) {
+            if (mProperty != null) {
+                return;
+            }
             // Check new static hashmap<propName, int> for setter method
             try {
                 mPropertyMapLock.writeLock().lock();
@@ -696,7 +854,8 @@
                     }
                 }
             } catch (NoSuchMethodError e) {
-                // System.out.println("Can't find native method using JNI, use reflection" + e);
+                Log.d("PropertyValuesHolder",
+                        "Can't find native method using JNI, use reflection" + e);
             } finally {
                 mPropertyMapLock.writeLock().unlock();
             }
@@ -709,9 +868,11 @@
 
     static class FloatPropertyValuesHolder extends PropertyValuesHolder {
 
+        // Cache JNI functions to avoid looking them up twice
         private static final HashMap<Class, HashMap<String, Integer>> sJNISetterPropertyMap =
                 new HashMap<Class, HashMap<String, Integer>>();
         int mJniSetter;
+        private FloatProperty mFloatProperty;
 
         FloatKeyframeSet mFloatKeyframeSet;
         float mFloatAnimatedValue;
@@ -723,11 +884,29 @@
             mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
         }
 
+        public FloatPropertyValuesHolder(Property property, FloatKeyframeSet keyframeSet) {
+            super(property);
+            mValueType = float.class;
+            mKeyframeSet = keyframeSet;
+            mFloatKeyframeSet = (FloatKeyframeSet) mKeyframeSet;
+            if (property instanceof FloatProperty) {
+                mFloatProperty = (FloatProperty) mProperty;
+            }
+        }
+
         public FloatPropertyValuesHolder(String propertyName, float... values) {
             super(propertyName);
             setFloatValues(values);
         }
 
+        public FloatPropertyValuesHolder(Property property, float... values) {
+            super(property);
+            setFloatValues(values);
+            if (property instanceof  FloatProperty) {
+                mFloatProperty = (FloatProperty) mProperty;
+            }
+        }
+
         @Override
         public void setFloatValues(float... values) {
             super.setFloatValues(values);
@@ -760,6 +939,14 @@
          */
         @Override
         void setAnimatedValue(Object target) {
+            if (mFloatProperty != null) {
+                mFloatProperty.setValue(target, mFloatAnimatedValue);
+                return;
+            }
+            if (mProperty != null) {
+                mProperty.set(target, mFloatAnimatedValue);
+                return;
+            }
             if (mJniSetter != 0) {
                 nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
                 return;
@@ -778,6 +965,9 @@
 
         @Override
         void setupSetter(Class targetClass) {
+            if (mProperty != null) {
+                return;
+            }
             // Check new static hashmap<propName, int> for setter method
             try {
                 mPropertyMapLock.writeLock().lock();
@@ -800,7 +990,8 @@
                     }
                 }
             } catch (NoSuchMethodError e) {
-                // System.out.println("Can't find native method using JNI, use reflection" + e);
+                Log.d("PropertyValuesHolder",
+                        "Can't find native method using JNI, use reflection" + e);
             } finally {
                 mPropertyMapLock.writeLock().unlock();
             }
diff --git a/core/java/android/animation/TypeEvaluator.java b/core/java/android/animation/TypeEvaluator.java
index fa49175..e738da1 100644
--- a/core/java/android/animation/TypeEvaluator.java
+++ b/core/java/android/animation/TypeEvaluator.java
@@ -24,7 +24,7 @@
  *
  * @see ValueAnimator#setEvaluator(TypeEvaluator)
  */
-public interface TypeEvaluator {
+public interface TypeEvaluator<T> {
 
     /**
      * This function returns the result of linearly interpolating the start and end values, with
@@ -39,6 +39,6 @@
      * @return A linear interpolation between the start and end values, given the
      *         <code>fraction</code> parameter.
      */
-    public Object evaluate(float fraction, Object startValue, Object endValue);
+    public T evaluate(float fraction, T startValue, T endValue);
 
 }
\ No newline at end of file
diff --git a/core/java/android/app/backup/BackupAgent.java b/core/java/android/app/backup/BackupAgent.java
index 17f8adb..63f3258 100644
--- a/core/java/android/app/backup/BackupAgent.java
+++ b/core/java/android/app/backup/BackupAgent.java
@@ -278,7 +278,6 @@
                 int token, IBackupManager callbackBinder) throws RemoteException {
             long ident = Binder.clearCallingIdentity();
             try {
-Log.d(TAG, "doRestoreFile() => onRestoreFile()");
                 BackupAgent.this.onRestoreFile(data, size, type, domain, path, mode, mtime);
             } catch (IOException e) {
                 throw new RuntimeException(e);
diff --git a/core/java/android/app/backup/FullBackup.java b/core/java/android/app/backup/FullBackup.java
index dfb0dd7..3b70e19 100644
--- a/core/java/android/app/backup/FullBackup.java
+++ b/core/java/android/app/backup/FullBackup.java
@@ -46,7 +46,7 @@
     public static final String SHARED_STORAGE_TOKEN = "shared";
 
     public static final String APPS_PREFIX = "apps/";
-    public static final String SHARED_PREFIX = "shared/";
+    public static final String SHARED_PREFIX = SHARED_STORAGE_TOKEN + "/";
 
     public static final String FULL_BACKUP_INTENT_ACTION = "fullback";
     public static final String FULL_RESTORE_INTENT_ACTION = "fullrest";
@@ -61,7 +61,8 @@
             String linkdomain, String rootpath, String path, BackupDataOutput output);
 
     static public void restoreToFile(ParcelFileDescriptor data,
-            long size, int type, long mode, long mtime, File outFile) throws IOException {
+            long size, int type, long mode, long mtime, File outFile,
+            boolean doChmod) throws IOException {
         if (type == FullBackup.TYPE_DIRECTORY) {
             // Canonically a directory has no associated content, so we don't need to read
             // anything from the pipe in this case.  Just create the directory here and
@@ -116,7 +117,7 @@
         }
 
         // Now twiddle the state to match the backup, assuming all went well
-        if (outFile != null) {
+        if (doChmod && outFile != null) {
             try {
                 Libcore.os.chmod(outFile.getPath(), (int)mode);
             } catch (ErrnoException e) {
diff --git a/core/java/android/app/backup/FullBackupAgent.java b/core/java/android/app/backup/FullBackupAgent.java
index 4dca593..df1c363 100644
--- a/core/java/android/app/backup/FullBackupAgent.java
+++ b/core/java/android/app/backup/FullBackupAgent.java
@@ -28,8 +28,6 @@
 import libcore.io.StructStat;
 
 import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.HashSet;
 import java.util.LinkedList;
@@ -84,9 +82,10 @@
 
     @Override
     public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
-            ParcelFileDescriptor newState) {
+            ParcelFileDescriptor newState) throws IOException {
         // Filters, the scan queue, and the set of resulting entities
         HashSet<String> filterSet = new HashSet<String>();
+        String packageName = getPackageName();
 
         // Okay, start with the app's root tree, but exclude all of the canonical subdirs
         if (mLibDir != null) {
@@ -96,25 +95,28 @@
         filterSet.add(mDatabaseDir);
         filterSet.add(mSharedPrefsDir);
         filterSet.add(mFilesDir);
-        processTree(FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
+        processTree(packageName, FullBackup.ROOT_TREE_TOKEN, mMainDir, filterSet, data);
 
         // Now do the same for the files dir, db dir, and shared prefs dir
         filterSet.add(mMainDir);
         filterSet.remove(mFilesDir);
-        processTree(FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
+        processTree(packageName, FullBackup.DATA_TREE_TOKEN, mFilesDir, filterSet, data);
 
         filterSet.add(mFilesDir);
         filterSet.remove(mDatabaseDir);
-        processTree(FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
+        processTree(packageName, FullBackup.DATABASE_TREE_TOKEN, mDatabaseDir, filterSet, data);
 
         filterSet.add(mDatabaseDir);
         filterSet.remove(mSharedPrefsDir);
-        processTree(FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
+        processTree(packageName, FullBackup.SHAREDPREFS_TREE_TOKEN, mSharedPrefsDir, filterSet, data);
     }
 
-    private void processTree(String domain, String rootPath,
+    // Scan the dir tree (if it actually exists) and process each entry we find.  If the
+    // 'excludes' parameter is non-null, it is consulted each time a new file system entity
+    // is visited to see whether that entity (and its subtree, if appropriate) should be
+    // omitted from the backup process.
+    protected void processTree(String packageName, String domain, String rootPath,
             HashSet<String> excludes, BackupDataOutput data) {
-        // Scan the dir tree (if it actually exists) and process each entry we find
         File rootFile = new File(rootPath);
         if (rootFile.exists()) {
             LinkedList<File> scanQueue = new LinkedList<File>();
@@ -125,7 +127,7 @@
                 String filePath = file.getAbsolutePath();
 
                 // prune this subtree?
-                if (excludes.contains(filePath)) {
+                if (excludes != null && excludes.contains(filePath)) {
                     continue;
                 }
 
@@ -149,7 +151,7 @@
                 }
 
                 // Finally, back this file up before proceeding
-                FullBackup.backupToTar(getPackageName(), domain, null, rootPath, filePath, data);
+                FullBackup.backupToTar(packageName, domain, null, rootPath, filePath, data);
             }
         }
     }
@@ -218,6 +220,6 @@
         if (DEBUG) Log.i(TAG, "[" + domain + " : " + relpath + "] mapped to " + outFile.getPath());
 
         // Now that we've figured out where the data goes, send it on its way
-        FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+        FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
     }
 }
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 26707c9..a8c31f9 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -873,10 +873,10 @@
 
     /**
      * Create a listening, insecure RFCOMM Bluetooth socket with Service Record.
-     * <p>The link key will be unauthenticated i.e the communication is
+     * <p>The link key is not required to be authenticated, i.e the communication may be
      * vulnerable to Man In the Middle attacks. For Bluetooth 2.1 devices,
-     * the link key will be encrypted, as encryption is mandartory.
-     * For legacy devices (pre Bluetooth 2.1 devices) the link key will not
+     * the link will be encrypted, as encryption is mandartory.
+     * For legacy devices (pre Bluetooth 2.1 devices) the link will not
      * be encrypted. Use {@link #listenUsingRfcommWithServiceRecord}, if an
      * encrypted and authenticated communication channel is desired.
      * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
@@ -902,6 +902,44 @@
         return createNewRfcommSocketAndRecord(name, uuid, false, false);
     }
 
+     /**
+     * Create a listening, encrypted,
+     * RFCOMM Bluetooth socket with Service Record.
+     * <p>The link will be encrypted, but the link key is not required to be authenticated
+     * i.e the communication is vulnerable to Man In the Middle attacks. Use
+     * {@link #listenUsingRfcommWithServiceRecord}, to ensure an authenticated link key.
+     * <p> Use this socket if authentication of link key is not possible.
+     * For example, for Bluetooth 2.1 devices, if any of the devices does not have
+     * an input and output capability or just has the ability to display a numeric key,
+     * a secure socket connection is not possible and this socket can be used.
+     * Use {@link #listenUsingInsecureRfcommWithServiceRecord}, if encryption is not required.
+     * For Bluetooth 2.1 devices, the link will be encrypted, as encryption is mandartory.
+     * For more details, refer to the Security Model section 5.2 (vol 3) of
+     * Bluetooth Core Specification version 2.1 + EDR.
+     * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
+     * connections from a listening {@link BluetoothServerSocket}.
+     * <p>The system will assign an unused RFCOMM channel to listen on.
+     * <p>The system will also register a Service Discovery
+     * Protocol (SDP) record with the local SDP server containing the specified
+     * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
+     * can use the same UUID to query our SDP server and discover which channel
+     * to connect to. This SDP record will be removed when this socket is
+     * closed, or if this application closes unexpectedly.
+     * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
+     * connect to this socket from another device using the same {@link UUID}.
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+     * @param name service name for SDP record
+     * @param uuid uuid for SDP record
+     * @return a listening RFCOMM BluetoothServerSocket
+     * @throws IOException on error, for example Bluetooth not available, or
+     *                     insufficient permissions, or channel in use.
+     * @hide
+     */
+    public BluetoothServerSocket listenUsingEncryptedRfcommWithServiceRecord(
+            String name, UUID uuid) throws IOException {
+        return createNewRfcommSocketAndRecord(name, uuid, false, true);
+    }
+
     private BluetoothServerSocket createNewRfcommSocketAndRecord(String name, UUID uuid,
             boolean auth, boolean encrypt) throws IOException {
         RfcommChannelPicker picker = new RfcommChannelPicker(uuid);
@@ -973,6 +1011,28 @@
         return socket;
     }
 
+     /**
+     * Construct an encrypted, RFCOMM server socket.
+     * Call #accept to retrieve connections to this socket.
+     * @return An RFCOMM BluetoothServerSocket
+     * @throws IOException On error, for example Bluetooth not available, or
+     *                     insufficient permissions.
+     * @hide
+     */
+    public BluetoothServerSocket listenUsingEncryptedRfcommOn(int port)
+            throws IOException {
+        BluetoothServerSocket socket = new BluetoothServerSocket(
+                BluetoothSocket.TYPE_RFCOMM, false, true, port);
+        int errno = socket.mSocket.bindListen();
+        if (errno != 0) {
+            try {
+                socket.close();
+            } catch (IOException e) {}
+            socket.mSocket.throwErrnoNative(errno);
+        }
+        return socket;
+    }
+
     /**
      * Construct a SCO server socket.
      * Call #accept to retrieve connections to this socket.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3d637e9..2f9627a 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1160,6 +1160,15 @@
     public static final String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";
 
     /**
+     * Activity Action: Show settings for managing network data usage of a
+     * specific application. Applications should define an activity that offers
+     * options to control data usage.
+     */
+    @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+    public static final String ACTION_MANAGE_NETWORK_USAGE =
+            "android.intent.action.MANAGE_NETWORK_USAGE";
+
+    /**
      * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity
      * describing the last run version of the platform that was setup.
      * @hide
@@ -1654,8 +1663,9 @@
      * This is used mainly for the USB Settings panel.
      * Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified
      * when the SD card file system is mounted or unmounted
+     * @deprecated replaced by android.os.storage.StorageEventListener
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_UMS_CONNECTED = "android.intent.action.UMS_CONNECTED";
 
     /**
@@ -1663,8 +1673,9 @@
      * This is used mainly for the USB Settings panel.
      * Apps should listen for ACTION_MEDIA_MOUNTED and ACTION_MEDIA_UNMOUNTED broadcasts to be notified
      * when the SD card file system is mounted or unmounted
+     * @deprecated replaced by android.os.storage.StorageEventListener
      */
-    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    @Deprecated
     public static final String ACTION_UMS_DISCONNECTED = "android.intent.action.UMS_DISCONNECTED";
 
     /**
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 09fede0..31ad6e9 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -1517,11 +1517,12 @@
             }
         }
 
+        // fullBackupAgent is explicitly handled even if allowBackup is false
         name = sa.getNonConfigurationString(
                 com.android.internal.R.styleable.AndroidManifestApplication_fullBackupAgent, 0);
         if (name != null) {
             ai.fullBackupAgentName = buildClassName(pkgName, name, outError);
-            if (true) {
+            if (false) {
                 Log.v(TAG, "android:fullBackupAgent=" + ai.fullBackupAgentName
                         + " from " + pkgName + "+" + name);
             }
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index d5c4ace..1df3108 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -16,22 +16,22 @@
 
 package android.hardware;
 
-import java.lang.ref.WeakReference;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.StringTokenizer;
-import java.io.IOException;
-
-import android.util.Log;
-import android.view.Surface;
-import android.view.SurfaceHolder;
 import android.graphics.ImageFormat;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.StringTokenizer;
 
 /**
  * The Camera class is used to set image capture settings, start/stop preview,
@@ -173,16 +173,16 @@
         public int facing;
 
         /**
-         * The orientation of the camera image. The value is the angle that the
+         * <p>The orientation of the camera image. The value is the angle that the
          * camera image needs to be rotated clockwise so it shows correctly on
-         * the display in its natural orientation. It should be 0, 90, 180, or 270.
+         * the display in its natural orientation. It should be 0, 90, 180, or 270.</p>
          *
-         * For example, suppose a device has a naturally tall screen. The
+         * <p>For example, suppose a device has a naturally tall screen. The
          * back-facing camera sensor is mounted in landscape. You are looking at
          * the screen. If the top side of the camera sensor is aligned with the
          * right edge of the screen in natural orientation, the value should be
          * 90. If the top side of a front-facing camera sensor is aligned with
-         * the right of the screen, the value should be 270.
+         * the right of the screen, the value should be 270.</p>
          *
          * @see #setDisplayOrientation(int)
          * @see Parameters#setRotation(int)
@@ -375,7 +375,7 @@
      * The preview surface texture may not otherwise change while preview is
      * running.
      *
-     * The timestamps provided by {@link SurfaceTexture#getTimestamp()} for a
+     * <p>The timestamps provided by {@link SurfaceTexture#getTimestamp()} for a
      * SurfaceTexture set as the preview texture have an unspecified zero point,
      * and cannot be directly compared between different cameras or different
      * instances of the same camera, or across multiple runs of the same
@@ -561,12 +561,12 @@
      * is used while calling {@link #takePicture(Camera.ShutterCallback,
      * Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback)}.
      *
-     * Please note that by calling this method, the mode for application-managed
-     * callback buffers is triggered. If this method has never been called,
-     * null will be returned by the raw image callback since there is
-     * no image callback buffer available. Furthermore, When a supplied buffer
-     * is too small to hold the raw image data, raw image callback will return
-     * null and the buffer will be removed from the buffer queue.
+     * <p>Please note that by calling this method, the mode for
+     * application-managed callback buffers is triggered. If this method has
+     * never been called, null will be returned by the raw image callback since
+     * there is no image callback buffer available. Furthermore, When a supplied
+     * buffer is too small to hold the raw image data, raw image callback will
+     * return null and the buffer will be removed from the buffer queue.
      *
      * @param callbackBuffer the buffer to add to the raw image callback buffer
      *     queue. The size should be width * height * (bits per pixel) / 8. An
@@ -834,8 +834,6 @@
      * @param raw       the callback for raw (uncompressed) image data, or null
      * @param postview  callback with postview image data, may be null
      * @param jpeg      the callback for JPEG image data, or null
-     *
-     * @see #addRawImageCallbackBuffer(byte[])
      */
     public final void takePicture(ShutterCallback shutter, PictureCallback raw,
             PictureCallback postview, PictureCallback jpeg) {
@@ -1084,17 +1082,42 @@
     };
 
     /**
-     * Area class for focus.
+     * <p>The Area class is used for choosing specific metering and focus areas for
+     * the camera to use when calculating auto-exposure, auto-white balance, and
+     * auto-focus.</p>
      *
-     * @see #setFocusAreas(List)
-     * @see #getFocusAreas()
+     * <p>To find out how many simultaneous areas a given camera supports, use
+     * {@link Parameters#getMaxNumMeteringAreas()} and
+     * {@link Parameters#getMaxNumFocusAreas()}. If metering or focusing area
+     * selection is unsupported, these methods will return 0.</p>
+     *
+     * <p>Each Area consists of a rectangle specifying its bounds, and a weight
+     * that determines its importance. The bounds are relative to the camera's
+     * current field of view. The coordinates are mapped so that (-1000, -1000)
+     * is always the top-left corner of the current field of view, and (1000,
+     * 1000) is always the bottom-right corner of the current field of
+     * view. Setting Areas with bounds outside that range is not allowed. Areas
+     * with zero or negative width or height are not allowed.</p>
+     *
+     * <p>The weight must range from 1 to 1000, and represents a weight for
+     * every pixel in the area. This means that a large metering area with
+     * the same weight as a smaller area will have more effect in the
+     * metering result.  Metering areas can overlap and the driver
+     * will add the weights in the overlap region.</p>
+     *
+     * @see Parameters#setFocusAreas(List)
+     * @see Parameters#getFocusAreas()
+     * @see Parameters#getMaxNumFocusAreas()
+     * @see Parameters#setMeteringAreas(List)
+     * @see Parameters#getMeteringAreas()
+     * @see Parameters#getMaxNumMeteringAreas()
      */
     public static class Area {
         /**
          * Create an area with specified rectangle and weight.
          *
-         * @param rect the rectangle of the area
-         * @param weight the weight of the area
+         * @param rect the bounds of the area.
+         * @param weight the weight of the area.
          */
         public Area(Rect rect, int weight) {
             this.rect = rect;
@@ -1121,12 +1144,30 @@
             return weight == a.weight;
         }
 
-        /** rectangle of the area */
+        /**
+         * Bounds of the area. (-1000, -1000) represents the top-left of the
+         * camera field of view, and (1000, 1000) represents the bottom-right of
+         * the field of view. Setting bounds outside that range is not
+         * allowed. Bounds with zero or negative width or height are not
+         * allowed.
+         *
+         * @see Parameters#getFocusAreas()
+         * @see Parameters#getMeteringAreas()
+         */
         public Rect rect;
 
-        /** weight of the area */
+        /**
+         * Weight of the area. The weight must range from 1 to 1000, and
+         * represents a weight for every pixel in the area. This means that a
+         * large metering area with the same weight as a smaller area will have
+         * more effect in the metering result.  Metering areas can overlap and
+         * the driver will add the weights in the overlap region.
+         *
+         * @see Parameters#getFocusAreas()
+         * @see Parameters#getMeteringAreas()
+         */
         public int weight;
-    };
+    }
 
     /**
      * Camera service settings.
@@ -1620,15 +1661,15 @@
         }
 
         /**
-         * Gets the supported video frame sizes that can be used by
-         * MediaRecorder.
+         * <p>Gets the supported video frame sizes that can be used by
+         * MediaRecorder.</p>
          *
-         * If the returned list is not null, the returned list will contain at
+         * <p>If the returned list is not null, the returned list will contain at
          * least one Size and one of the sizes in the returned list must be
          * passed to MediaRecorder.setVideoSize() for camcorder application if
          * camera is used as the video source. In this case, the size of the
          * preview can be different from the resolution of the recorded video
-         * during video recording.
+         * during video recording.</p>
          *
          * @return a list of Size object if camera has separate preview and
          *         video output; otherwise, null is returned.
@@ -1660,12 +1701,12 @@
         }
 
         /**
-         * Sets the dimensions for EXIF thumbnail in Jpeg picture. If
+         * <p>Sets the dimensions for EXIF thumbnail in Jpeg picture. If
          * applications set both width and height to 0, EXIF will not contain
-         * thumbnail.
+         * thumbnail.</p>
          *
-         * Applications need to consider the display orientation. See {@link
-         * #setPreviewSize(int,int)} for reference.
+         * <p>Applications need to consider the display orientation. See {@link
+         * #setPreviewSize(int,int)} for reference.</p>
          *
          * @param width  the width of the thumbnail, in pixels
          * @param height the height of the thumbnail, in pixels
@@ -1885,10 +1926,10 @@
         }
 
         /**
-         * Sets the dimensions for pictures.
+         * <p>Sets the dimensions for pictures.</p>
          *
-         * Applications need to consider the display orientation. See {@link
-         * #setPreviewSize(int,int)} for reference.
+         * <p>Applications need to consider the display orientation. See {@link
+         * #setPreviewSize(int,int)} for reference.</p>
          *
          * @param width  the width for pictures, in pixels
          * @param height the height for pictures, in pixels
@@ -2714,26 +2755,26 @@
         }
 
         /**
-         * Gets the distances from the camera to where an object appears to be
+         * <p>Gets the distances from the camera to where an object appears to be
          * in focus. The object is sharpest at the optimal focus distance. The
-         * depth of field is the far focus distance minus near focus distance.
+         * depth of field is the far focus distance minus near focus distance.</p>
          *
-         * Focus distances may change after calling {@link
+         * <p>Focus distances may change after calling {@link
          * #autoFocus(AutoFocusCallback)}, {@link #cancelAutoFocus}, or {@link
          * #startPreview()}. Applications can call {@link #getParameters()}
          * and this method anytime to get the latest focus distances. If the
          * focus mode is FOCUS_MODE_CONTINUOUS_VIDEO, focus distances may change
-         * from time to time.
+         * from time to time.</p>
          *
-         * This method is intended to estimate the distance between the camera
+         * <p>This method is intended to estimate the distance between the camera
          * and the subject. After autofocus, the subject distance may be within
          * near and far focus distance. However, the precision depends on the
          * camera hardware, autofocus algorithm, the focus area, and the scene.
-         * The error can be large and it should be only used as a reference.
+         * The error can be large and it should be only used as a reference.</p>
          *
-         * Far focus distance >= optimal focus distance >= near focus distance.
+         * <p>Far focus distance >= optimal focus distance >= near focus distance.
          * If the focus distance is infinity, the value will be
-         * Float.POSITIVE_INFINITY.
+         * {@code Float.POSITIVE_INFINITY}.</p>
          *
          * @param output focus distances in meters. output must be a float
          *        array with three elements. Near focus distance, optimal focus
@@ -2763,42 +2804,43 @@
         }
 
         /**
-         * Gets the current focus areas. Camera driver uses the areas to decide
-         * focus.
+         * <p>Gets the current focus areas. Camera driver uses the areas to decide
+         * focus.</p>
          *
-         * Before using this API or {@link #setFocusAreas(List)}, apps should
+         * <p>Before using this API or {@link #setFocusAreas(List)}, apps should
          * call {@link #getMaxNumFocusAreas()} to know the maximum number of
-         * focus areas first. If the value is 0, focus area is not supported.
+         * focus areas first. If the value is 0, focus area is not supported.</p>
          *
-         * Each focus area is a rectangle with specified weight. The direction
+         * <p>Each focus area is a rectangle with specified weight. The direction
          * is relative to the sensor orientation, that is, what the sensor sees.
          * The direction is not affected by the rotation or mirroring of
          * {@link #setDisplayOrientation(int)}. Coordinates of the rectangle
          * range from -1000 to 1000. (-1000, -1000) is the upper left point.
-         * (1000, 1000) is the lower right point. The length and width of focus
-         * areas cannot be 0 or negative.
+         * (1000, 1000) is the lower right point. The width and height of focus
+         * areas cannot be 0 or negative.</p>
          *
-         * The weight must range from 1 to 1000. The weight should be
+         * <p>The weight must range from 1 to 1000. The weight should be
          * interpreted as a per-pixel weight - all pixels in the area have the
          * specified weight. This means a small area with the same weight as a
          * larger area will have less influence on the focusing than the larger
          * area. Focus areas can partially overlap and the driver will add the
-         * weights in the overlap region.
+         * weights in the overlap region.</p>
          *
-         * A special case of null focus area means driver to decide the focus
-         * area. For example, the driver may use more signals to decide focus
-         * areas and change them dynamically. Apps can set all-zero if they want
-         * the driver to decide focus areas.
+         * <p>A special case of a {@code null} focus area list means the driver is
+         * free to select focus targets as it wants. For example, the driver may
+         * use more signals to select focus areas and change them
+         * dynamically. Apps can set the focus area list to {@code null} if they
+         * want the driver to completely control focusing.</p>
          *
-         * Focus areas are relative to the current field of view
+         * <p>Focus areas are relative to the current field of view
          * ({@link #getZoom()}). No matter what the zoom level is, (-1000,-1000)
          * represents the top of the currently visible camera frame. The focus
          * area cannot be set to be outside the current field of view, even
-         * when using zoom.
+         * when using zoom.</p>
          *
-         * Focus area only has effect if the current focus mode is
+         * <p>Focus area only has effect if the current focus mode is
          * {@link #FOCUS_MODE_AUTO}, {@link #FOCUS_MODE_MACRO}, or
-         * {@link #FOCUS_MODE_CONTINUOUS_VIDEO}.
+         * {@link #FOCUS_MODE_CONTINUOUS_VIDEO}.</p>
          *
          * @return a list of current focus areas
          */
@@ -2829,41 +2871,42 @@
         }
 
         /**
-         * Gets the current metering areas. Camera driver uses these areas to
-         * decide exposure.
+         * <p>Gets the current metering areas. Camera driver uses these areas to
+         * decide exposure.</p>
          *
-         * Before using this API or {@link #setMeteringAreas(List)}, apps should
+         * <p>Before using this API or {@link #setMeteringAreas(List)}, apps should
          * call {@link #getMaxNumMeteringAreas()} to know the maximum number of
          * metering areas first. If the value is 0, metering area is not
-         * supported.
+         * supported.</p>
          *
-         * Each metering area is a rectangle with specified weight. The
+         * <p>Each metering area is a rectangle with specified weight. The
          * direction is relative to the sensor orientation, that is, what the
          * sensor sees. The direction is not affected by the rotation or
          * mirroring of {@link #setDisplayOrientation(int)}. Coordinates of the
          * rectangle range from -1000 to 1000. (-1000, -1000) is the upper left
-         * point. (1000, 1000) is the lower right point. The length and width of
-         * metering areas cannot be 0 or negative.
+         * point. (1000, 1000) is the lower right point. The width and height of
+         * metering areas cannot be 0 or negative.</p>
          *
-         * The weight must range from 1 to 1000, and represents a weight for
+         * <p>The weight must range from 1 to 1000, and represents a weight for
          * every pixel in the area. This means that a large metering area with
          * the same weight as a smaller area will have more effect in the
          * metering result.  Metering areas can partially overlap and the driver
-         * will add the weights in the overlap region.
+         * will add the weights in the overlap region.</p>
          *
-         * A special case of null metering area means driver to decide the
-         * metering area. For example, the driver may use more signals to decide
-         * metering areas and change them dynamically. Apps can set all-zero if
-         * they want the driver to decide metering areas.
+         * <p>A special case of a {@code null} metering area list means the driver
+         * is free to meter as it chooses. For example, the driver may use more
+         * signals to select metering areas and change them dynamically. Apps
+         * can set the metering area list to {@code null} if they want the
+         * driver to completely control metering.</p>
          *
-         * Metering areas are relative to the current field of view
+         * <p>Metering areas are relative to the current field of view
          * ({@link #getZoom()}). No matter what the zoom level is, (-1000,-1000)
          * represents the top of the currently visible camera frame. The
          * metering area cannot be set to be outside the current field of view,
-         * even when using zoom.
+         * even when using zoom.</p>
          *
-         * No matter what metering areas are, the final exposure are compensated
-         * by {@link #setExposureCompensation(int)}.
+         * <p>No matter what metering areas are, the final exposure are compensated
+         * by {@link #setExposureCompensation(int)}.</p>
          *
          * @return a list of current metering areas
          */
@@ -3033,7 +3076,7 @@
             if (result.size() == 0) return null;
 
             if (result.size() == 1) {
-                Area area = (Area) result.get(0);
+                Area area = result.get(0);
                 Rect rect = area.rect;
                 if (rect.left == 0 && rect.top == 0 && rect.right == 0
                         && rect.bottom == 0 && area.weight == 0) {
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index 60b37a1..5994c98 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -50,11 +50,20 @@
      * This is a sticky broadcast for clients that includes USB connected/disconnected state,
      * <ul>
      * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
-     * <li> {@link #USB_CONFIGURATION} a Bundle containing name/value pairs where the name
-     * is the name of a USB function and the value is either {@link #USB_FUNCTION_ENABLED}
-     * or {@link #USB_FUNCTION_DISABLED}.  The possible function names include
-     * {@link #USB_FUNCTION_MASS_STORAGE}, {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS},
-     * {@link #USB_FUNCTION_MTP} and {@link #USB_FUNCTION_ACCESSORY}.
+     * <li> {@link #USB_CONFIGURATION} integer containing current USB configuration
+     * currently zero if not configured, one for configured.
+     * <li> {@link #USB_FUNCTION_MASS_STORAGE} boolean extra indicating whether the
+     * mass storage function is enabled
+     * <li> {@link #USB_FUNCTION_ADB} boolean extra indicating whether the
+     * adb function is enabled
+     * <li> {@link #USB_FUNCTION_RNDIS} boolean extra indicating whether the
+     * RNDIS ethernet function is enabled
+     * <li> {@link #USB_FUNCTION_MTP} boolean extra indicating whether the
+     * MTP function is enabled
+     * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
+     * PTP function is enabled
+     * <li> {@link #USB_FUNCTION_PTP} boolean extra indicating whether the
+     * accessory function is enabled
      * </ul>
      *
      * {@hide}
@@ -159,6 +168,14 @@
     public static final String USB_FUNCTION_MTP = "mtp";
 
     /**
+     * Name of the PTP USB function.
+     * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+     *
+     * {@hide}
+     */
+    public static final String USB_FUNCTION_PTP = "ptp";
+
+    /**
      * Name of the Accessory USB function.
      * Used in extras for the {@link #ACTION_USB_STATE} broadcast
      *
@@ -167,24 +184,6 @@
     public static final String USB_FUNCTION_ACCESSORY = "accessory";
 
     /**
-     * Value indicating that a USB function is enabled.
-     * Used in {@link #USB_CONFIGURATION} extras bundle for the
-     * {@link #ACTION_USB_STATE} broadcast
-     *
-     * {@hide}
-     */
-    public static final String USB_FUNCTION_ENABLED = "enabled";
-
-    /**
-     * Value indicating that a USB function is disabled.
-     * Used in {@link #USB_CONFIGURATION} extras bundle for the
-     * {@link #ACTION_USB_STATE} broadcast
-     *
-     * {@hide}
-     */
-    public static final String USB_FUNCTION_DISABLED = "disabled";
-
-    /**
      * Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and
      * {@link #ACTION_USB_DEVICE_DETACHED} broadcasts
      * containing the UsbDevice object for the device.
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index c72c4b0..3025462 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -19,6 +19,8 @@
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.os.Binder;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
 import android.os.RemoteException;
 
 import java.net.InetAddress;
@@ -164,14 +166,12 @@
 
     /**
      * The Default Mobile data connection.  When active, all data traffic
-     * will use this connection by default.  Should not coexist with other
-     * default connections.
+     * will use this connection by default.
      */
     public static final int TYPE_MOBILE      = 0;
     /**
      * The Default WIFI data connection.  When active, all data traffic
-     * will use this connection by default.  Should not coexist with other
-     * default connections.
+     * will use this connection by default.
      */
     public static final int TYPE_WIFI        = 1;
     /**
@@ -207,13 +207,13 @@
     public static final int TYPE_MOBILE_HIPRI = 5;
     /**
      * The Default WiMAX data connection.  When active, all data traffic
-     * will use this connection by default.  Should not coexist with other
-     * default connections.
+     * will use this connection by default.
      */
     public static final int TYPE_WIMAX       = 6;
 
     /**
-     * Bluetooth data connection. This is used for Bluetooth reverse tethering.
+     * The Default Bluetooth data connection. When active, all data traffic
+     * will use this connection by default.
      */
     public static final int TYPE_BLUETOOTH   = 7;
 
@@ -223,8 +223,8 @@
     public static final int TYPE_DUMMY       = 8;
 
     /**
-     * Ethernet data connection.  This may be via USB dongle or more
-     * traditional means.
+     * The Default Ethernet data connection.  When active, all data traffic
+     * will use this connection by default.
      */
     public static final int TYPE_ETHERNET    = 9;
 
@@ -256,10 +256,61 @@
 
     private final IConnectivityManager mService;
 
-    static public boolean isNetworkTypeValid(int networkType) {
+    public static boolean isNetworkTypeValid(int networkType) {
         return networkType >= 0 && networkType <= MAX_NETWORK_TYPE;
     }
 
+    /** {@hide} */
+    public static String getNetworkTypeName(int type) {
+        switch (type) {
+            case TYPE_MOBILE:
+                return "MOBILE";
+            case TYPE_WIFI:
+                return "WIFI";
+            case TYPE_MOBILE_MMS:
+                return "MOBILE_MMS";
+            case TYPE_MOBILE_SUPL:
+                return "MOBILE_SUPL";
+            case TYPE_MOBILE_DUN:
+                return "MOBILE_DUN";
+            case TYPE_MOBILE_HIPRI:
+                return "MOBILE_HIPRI";
+            case TYPE_WIMAX:
+                return "WIMAX";
+            case TYPE_BLUETOOTH:
+                return "BLUETOOTH";
+            case TYPE_DUMMY:
+                return "DUMMY";
+            case TYPE_ETHERNET:
+                return "ETHERNET";
+            case TYPE_MOBILE_FOTA:
+                return "MOBILE_FOTA";
+            case TYPE_MOBILE_IMS:
+                return "MOBILE_IMS";
+            case TYPE_MOBILE_CBS:
+                return "MOBILE_CBS";
+            default:
+                return Integer.toString(type);
+        }
+    }
+
+    /** {@hide} */
+    public static boolean isNetworkTypeMobile(int networkType) {
+        switch (networkType) {
+            case TYPE_MOBILE:
+            case TYPE_MOBILE_MMS:
+            case TYPE_MOBILE_SUPL:
+            case TYPE_MOBILE_DUN:
+            case TYPE_MOBILE_HIPRI:
+            case TYPE_MOBILE_FOTA:
+            case TYPE_MOBILE_IMS:
+            case TYPE_MOBILE_CBS:
+                return true;
+            default:
+                return false;
+        }
+    }
+
     public void setNetworkPreference(int preference) {
         try {
             mService.setNetworkPreference(preference);
@@ -707,4 +758,43 @@
         } catch (RemoteException e) {
         }
     }
+
+    /**
+     * Protect a socket from routing changes. This method is limited to VPN
+     * applications, and it is always hidden to avoid direct use.
+     * @hide
+     */
+    public void protectVpn(ParcelFileDescriptor socket) {
+        try {
+            mService.protectVpn(socket);
+        } catch (RemoteException e) {
+        }
+    }
+
+    /**
+     * Prepare for a VPN application. This method is limited to VpnDialogs,
+     * and it is always hidden to avoid direct use.
+     * @hide
+     */
+    public String prepareVpn(String packageName) {
+        try {
+            return mService.prepareVpn(packageName);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Configure a TUN interface and return its file descriptor. Parameters
+     * are encoded and opaque to this class. This method is limited to VPN
+     * applications, and it is always hidden to avoid direct use.
+     * @hide
+     */
+    public ParcelFileDescriptor establishVpn(Bundle config) {
+        try {
+            return mService.establishVpn(config);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
 }
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 647a60a..7f3775d 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -18,8 +18,11 @@
 
 import android.net.LinkProperties;
 import android.net.NetworkInfo;
+import android.net.NetworkState;
 import android.net.ProxyProperties;
+import android.os.Bundle;
 import android.os.IBinder;
+import android.os.ParcelFileDescriptor;
 
 /**
  * Interface that answers queries about, and allows changing, the
@@ -40,6 +43,8 @@
     LinkProperties getActiveLinkProperties();
     LinkProperties getLinkProperties(int networkType);
 
+    NetworkState[] getAllNetworkState();
+
     boolean setRadios(boolean onOff);
 
     boolean setRadio(int networkType, boolean turnOn);
@@ -92,4 +97,10 @@
     ProxyProperties getProxy();
 
     void setDataDependency(int networkType, boolean met);
+
+    void protectVpn(in ParcelFileDescriptor socket);
+
+    String prepareVpn(String packageName);
+
+    ParcelFileDescriptor establishVpn(in Bundle config);
 }
diff --git a/core/java/android/net/INetworkStatsService.aidl b/core/java/android/net/INetworkStatsService.aidl
index 6d57036..d05c9d3 100644
--- a/core/java/android/net/INetworkStatsService.aidl
+++ b/core/java/android/net/INetworkStatsService.aidl
@@ -16,12 +16,18 @@
 
 package android.net;
 
+import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
 
 /** {@hide} */
 interface INetworkStatsService {
 
-    NetworkStatsHistory[] getNetworkStatsSummary(int networkType);
-    NetworkStatsHistory getNetworkStatsUid(int uid);
+    /** Return historical stats for traffic that matches template. */
+    NetworkStatsHistory getHistoryForNetwork(int networkTemplate);
+    /** Return historical stats for specific UID traffic that matches template. */
+    NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate);
+
+    /** Return usage summary per UID for traffic that matches template. */
+    NetworkStats getSummaryForAllUid(long start, long end, int networkTemplate);
 
 }
diff --git a/core/java/android/net/NetworkState.aidl b/core/java/android/net/NetworkState.aidl
new file mode 100644
index 0000000..c0b6cdc
--- /dev/null
+++ b/core/java/android/net/NetworkState.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+parcelable NetworkState;
diff --git a/core/java/android/net/NetworkState.java b/core/java/android/net/NetworkState.java
new file mode 100644
index 0000000..749039a
--- /dev/null
+++ b/core/java/android/net/NetworkState.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.net;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Snapshot of network state.
+ *
+ * @hide
+ */
+public class NetworkState implements Parcelable {
+
+    public final NetworkInfo networkInfo;
+    public final LinkProperties linkProperties;
+    public final LinkCapabilities linkCapabilities;
+
+    public NetworkState(NetworkInfo networkInfo, LinkProperties linkProperties,
+            LinkCapabilities linkCapabilities) {
+        this.networkInfo = networkInfo;
+        this.linkProperties = linkProperties;
+        this.linkCapabilities = linkCapabilities;
+    }
+
+    public NetworkState(Parcel in) {
+        networkInfo = in.readParcelable(null);
+        linkProperties = in.readParcelable(null);
+        linkCapabilities = in.readParcelable(null);
+    }
+
+    /** {@inheritDoc} */
+    public int describeContents() {
+        return 0;
+    }
+
+    /** {@inheritDoc} */
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeParcelable(networkInfo, flags);
+        out.writeParcelable(linkProperties, flags);
+        out.writeParcelable(linkCapabilities, flags);
+    }
+
+    public static final Creator<NetworkState> CREATOR = new Creator<NetworkState>() {
+        public NetworkState createFromParcel(Parcel in) {
+            return new NetworkState(in);
+        }
+
+        public NetworkState[] newArray(int size) {
+            return new NetworkState[size];
+        }
+    };
+
+}
diff --git a/core/java/android/net/NetworkStats.java b/core/java/android/net/NetworkStats.java
index 588bf64..6354e9a 100644
--- a/core/java/android/net/NetworkStats.java
+++ b/core/java/android/net/NetworkStats.java
@@ -19,6 +19,7 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.os.SystemClock;
+import android.util.SparseBooleanArray;
 
 import java.io.CharArrayWriter;
 import java.io.PrintWriter;
@@ -125,7 +126,7 @@
     /**
      * Return list of unique interfaces known by this data structure.
      */
-    public String[] getKnownIfaces() {
+    public String[] getUniqueIfaces() {
         final HashSet<String> ifaces = new HashSet<String>();
         for (String iface : this.iface) {
             if (iface != IFACE_ALL) {
@@ -136,14 +137,58 @@
     }
 
     /**
+     * Return list of unique UIDs known by this data structure.
+     */
+    public int[] getUniqueUids() {
+        final SparseBooleanArray uids = new SparseBooleanArray();
+        for (int uid : this.uid) {
+            uids.put(uid, true);
+        }
+
+        final int size = uids.size();
+        final int[] result = new int[size];
+        for (int i = 0; i < size; i++) {
+            result[i] = uids.keyAt(i);
+        }
+        return result;
+    }
+
+    /**
+     * Subtract the given {@link NetworkStats}, effectively leaving the delta
+     * between two snapshots in time. Assumes that statistics rows collect over
+     * time, and that none of them have disappeared.
+     *
+     * @throws IllegalArgumentException when given {@link NetworkStats} is
+     *             non-monotonic.
+     */
+    public NetworkStats subtract(NetworkStats value) {
+        return subtract(value, true, false);
+    }
+
+    /**
+     * Subtract the given {@link NetworkStats}, effectively leaving the delta
+     * between two snapshots in time. Assumes that statistics rows collect over
+     * time, and that none of them have disappeared.
+     * <p>
+     * Instead of throwing when counters are non-monotonic, this variant clamps
+     * results to never be negative.
+     */
+    public NetworkStats subtractClamped(NetworkStats value) {
+        return subtract(value, false, true);
+    }
+
+    /**
      * Subtract the given {@link NetworkStats}, effectively leaving the delta
      * between two snapshots in time. Assumes that statistics rows collect over
      * time, and that none of them have disappeared.
      *
      * @param enforceMonotonic Validate that incoming value is strictly
      *            monotonic compared to this object.
+     * @param clampNegative Instead of throwing like {@code enforceMonotonic},
+     *            clamp resulting counters at 0 to prevent negative values.
      */
-    public NetworkStats subtract(NetworkStats value, boolean enforceMonotonic) {
+    private NetworkStats subtract(
+            NetworkStats value, boolean enforceMonotonic, boolean clampNegative) {
         final long deltaRealtime = this.elapsedRealtime - value.elapsedRealtime;
         if (enforceMonotonic && deltaRealtime < 0) {
             throw new IllegalArgumentException("found non-monotonic realtime");
@@ -163,11 +208,15 @@
                 result.addEntry(iface, uid, this.rx[i], this.tx[i]);
             } else {
                 // existing row, subtract remote value
-                final long rx = this.rx[i] - value.rx[j];
-                final long tx = this.tx[i] - value.tx[j];
+                long rx = this.rx[i] - value.rx[j];
+                long tx = this.tx[i] - value.tx[j];
                 if (enforceMonotonic && (rx < 0 || tx < 0)) {
                     throw new IllegalArgumentException("found non-monotonic values");
                 }
+                if (clampNegative) {
+                    rx = Math.max(0, rx);
+                    tx = Math.max(0, tx);
+                }
                 result.addEntry(iface, uid, rx, tx);
             }
         }
diff --git a/core/java/android/net/NetworkStatsHistory.java b/core/java/android/net/NetworkStatsHistory.java
index b16101f..a697e96 100644
--- a/core/java/android/net/NetworkStatsHistory.java
+++ b/core/java/android/net/NetworkStatsHistory.java
@@ -24,7 +24,9 @@
 import java.io.DataOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.ProtocolException;
 import java.util.Arrays;
+import java.util.Random;
 
 /**
  * Collection of historical network statistics, recorded into equally-sized
@@ -38,28 +40,19 @@
  * @hide
  */
 public class NetworkStatsHistory implements Parcelable {
-    private static final int VERSION = 1;
-
-    /** {@link #uid} value when UID details unavailable. */
-    public static final int UID_ALL = -1;
+    private static final int VERSION_CURRENT = 1;
 
     // TODO: teach about zigzag encoding to use less disk space
     // TODO: teach how to convert between bucket sizes
 
-    public final int networkType;
-    public final String identity;
-    public final int uid;
     public final long bucketDuration;
 
-    int bucketCount;
-    long[] bucketStart;
-    long[] rx;
-    long[] tx;
+    public int bucketCount;
+    public long[] bucketStart;
+    public long[] rx;
+    public long[] tx;
 
-    public NetworkStatsHistory(int networkType, String identity, int uid, long bucketDuration) {
-        this.networkType = networkType;
-        this.identity = identity;
-        this.uid = uid;
+    public NetworkStatsHistory(long bucketDuration) {
         this.bucketDuration = bucketDuration;
         bucketStart = new long[0];
         rx = new long[0];
@@ -68,9 +61,6 @@
     }
 
     public NetworkStatsHistory(Parcel in) {
-        networkType = in.readInt();
-        identity = in.readString();
-        uid = in.readInt();
         bucketDuration = in.readLong();
         bucketStart = readLongArray(in);
         rx = in.createLongArray();
@@ -80,9 +70,6 @@
 
     /** {@inheritDoc} */
     public void writeToParcel(Parcel out, int flags) {
-        out.writeInt(networkType);
-        out.writeString(identity);
-        out.writeInt(uid);
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
         writeLongArray(out, rx, bucketCount);
@@ -91,21 +78,23 @@
 
     public NetworkStatsHistory(DataInputStream in) throws IOException {
         final int version = in.readInt();
-        networkType = in.readInt();
-        identity = in.readUTF();
-        uid = in.readInt();
-        bucketDuration = in.readLong();
-        bucketStart = readLongArray(in);
-        rx = readLongArray(in);
-        tx = readLongArray(in);
-        bucketCount = bucketStart.length;
+        switch (version) {
+            case VERSION_CURRENT: {
+                bucketDuration = in.readLong();
+                bucketStart = readLongArray(in);
+                rx = readLongArray(in);
+                tx = readLongArray(in);
+                bucketCount = bucketStart.length;
+                break;
+            }
+            default: {
+                throw new ProtocolException("unexpected version: " + version);
+            }
+        }
     }
 
     public void writeToStream(DataOutputStream out) throws IOException {
-        out.writeInt(VERSION);
-        out.writeInt(networkType);
-        out.writeUTF(identity);
-        out.writeInt(uid);
+        out.writeInt(VERSION_CURRENT);
         out.writeLong(bucketDuration);
         writeLongArray(out, bucketStart, bucketCount);
         writeLongArray(out, rx, bucketCount);
@@ -145,6 +134,18 @@
     }
 
     /**
+     * Record an entire {@link NetworkStatsHistory} into this history. Usually
+     * for combining together stats for external reporting.
+     */
+    public void recordEntireHistory(NetworkStatsHistory input) {
+        for (int i = 0; i < input.bucketCount; i++) {
+            final long start = input.bucketStart[i];
+            final long end = start + input.bucketDuration;
+            recordData(start, end, input.rx[i], input.tx[i]);
+        }
+    }
+
+    /**
      * Ensure that buckets exist for given time range, creating as needed.
      */
     private void ensureBuckets(long start, long end) {
@@ -213,15 +214,69 @@
         }
     }
 
+    /**
+     * Return interpolated data usage across the requested range. Interpolates
+     * across buckets, so values may be rounded slightly.
+     */
+    public long[] getTotalData(long start, long end, long[] outTotal) {
+        long rx = 0;
+        long tx = 0;
+
+        for (int i = bucketCount - 1; i >= 0; i--) {
+            final long curStart = bucketStart[i];
+            final long curEnd = curStart + bucketDuration;
+
+            // bucket is older than record; we're finished
+            if (curEnd < start) break;
+            // bucket is newer than record; keep looking
+            if (curStart > end) continue;
+
+            final long overlap = Math.min(curEnd, end) - Math.max(curStart, start);
+            if (overlap > 0) {
+                rx += this.rx[i] * overlap / bucketDuration;
+                tx += this.tx[i] * overlap / bucketDuration;
+            }
+        }
+
+        if (outTotal == null || outTotal.length != 2) {
+            outTotal = new long[2];
+        }
+        outTotal[0] = rx;
+        outTotal[1] = tx;
+        return outTotal;
+    }
+
+    /**
+     * @deprecated only for temporary testing
+     */
+    @Deprecated
+    public void generateRandom(long start, long end, long rx, long tx) {
+        ensureBuckets(start, end);
+
+        final Random r = new Random();
+        while (rx > 1024 && tx > 1024) {
+            final long curStart = randomLong(r, start, end);
+            final long curEnd = randomLong(r, curStart, end);
+            final long curRx = randomLong(r, 0, rx);
+            final long curTx = randomLong(r, 0, tx);
+
+            recordData(curStart, curEnd, curRx, curTx);
+
+            rx -= curRx;
+            tx -= curTx;
+        }
+    }
+
+    private static long randomLong(Random r, long start, long end) {
+        return (long) (start + (r.nextFloat() * (end - start)));
+    }
+
     public void dump(String prefix, PrintWriter pw) {
-        // TODO: consider stripping identity when dumping
         pw.print(prefix);
-        pw.print("NetworkStatsHistory: networkType="); pw.print(networkType);
-        pw.print(" identity="); pw.print(identity);
-        pw.print(" uid="); pw.println(uid);
+        pw.print("NetworkStatsHistory: bucketDuration="); pw.println(bucketDuration);
         for (int i = 0; i < bucketCount; i++) {
             pw.print(prefix);
-            pw.print("  timestamp="); pw.print(bucketStart[i]);
+            pw.print("  bucketStart="); pw.print(bucketStart[i]);
             pw.print(" rx="); pw.print(rx[i]);
             pw.print(" tx="); pw.println(tx[i]);
         }
diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java
index 3bf64b2..8e50cd5 100644
--- a/core/java/android/net/SSLCertificateSocketFactory.java
+++ b/core/java/android/net/SSLCertificateSocketFactory.java
@@ -28,6 +28,7 @@
 import javax.net.SocketFactory;
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.KeyManager;
 import javax.net.ssl.SSLException;
 import javax.net.ssl.SSLPeerUnverifiedException;
 import javax.net.ssl.SSLSession;
@@ -86,6 +87,8 @@
 
     private SSLSocketFactory mInsecureFactory = null;
     private SSLSocketFactory mSecureFactory = null;
+    private TrustManager[] mTrustManagers = null;
+    private KeyManager[] mKeyManagers = null;
 
     private final int mHandshakeTimeoutMillis;
     private final SSLClientSessionCache mSessionCache;
@@ -197,10 +200,11 @@
         }
     }
 
-    private SSLSocketFactory makeSocketFactory(TrustManager[] trustManagers) {
+    private SSLSocketFactory makeSocketFactory(
+            KeyManager[] keyManagers, TrustManager[] trustManagers) {
         try {
             OpenSSLContextImpl sslContext = new OpenSSLContextImpl();
-            sslContext.engineInit(null, trustManagers, null);
+            sslContext.engineInit(keyManagers, trustManagers, null);
             sslContext.engineGetClientSessionContext().setPersistentCache(mSessionCache);
             return sslContext.engineGetSocketFactory();
         } catch (KeyManagementException e) {
@@ -223,18 +227,44 @@
                 } else {
                     Log.w(TAG, "Bypassing SSL security checks at caller's request");
                 }
-                mInsecureFactory = makeSocketFactory(INSECURE_TRUST_MANAGER);
+                mInsecureFactory = makeSocketFactory(mKeyManagers, INSECURE_TRUST_MANAGER);
             }
             return mInsecureFactory;
         } else {
             if (mSecureFactory == null) {
-                mSecureFactory = makeSocketFactory(null);
+                mSecureFactory = makeSocketFactory(mKeyManagers, mTrustManagers);
             }
             return mSecureFactory;
         }
     }
 
     /**
+     * Sets the {@link TrustManager}s to be used for connections made by this factory.
+     * @hide
+     */
+    public void setTrustManagers(TrustManager[] trustManager) {
+        mTrustManagers = trustManager;
+
+        // Clear out all cached secure factories since configurations have changed.
+        mSecureFactory = null;
+        // Note - insecure factories only ever use the INSECURE_TRUST_MANAGER so they need not
+        // be cleared out here.
+    }
+
+    /**
+     * Sets the {@link KeyManager}s to be used for connections made by this factory.
+     * @hide
+     */
+    public void setKeyManagers(KeyManager[] keyManagers) {
+        mKeyManagers = keyManagers;
+
+        // Clear out any existing cached factories since configurations have changed.
+        mSecureFactory = null;
+        mInsecureFactory = null;
+    }
+
+
+    /**
      * {@inheritDoc}
      *
      * <p>This method verifies the peer's certificate hostname after connecting
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 8ab64fa..8a688d5 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -41,6 +41,42 @@
      */
     public final static int UNSUPPORTED = -1;
 
+    // TODO: find better home for these template constants
+
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+     * networks together. Only uses statistics for currently active IMSI.
+     *
+     * @hide
+     */
+    public static final int TEMPLATE_MOBILE_ALL = 1;
+
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+     * networks together that roughly meet a "3G" definition, or lower. Only
+     * uses statistics for currently active IMSI.
+     *
+     * @hide
+     */
+    public static final int TEMPLATE_MOBILE_3G_LOWER = 2;
+
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_MOBILE} style
+     * networks together that meet a "4G" definition. Only uses statistics for
+     * currently active IMSI.
+     *
+     * @hide
+     */
+    public static final int TEMPLATE_MOBILE_4G = 3;
+
+    /**
+     * Template to combine all {@link ConnectivityManager#TYPE_WIFI} style
+     * networks together.
+     *
+     * @hide
+     */
+    public static final int TEMPLATE_WIFI = 4;
+
     /**
      * Snapshot of {@link NetworkStats} when the currently active profiling
      * session started, or {@code null} if no session active.
@@ -141,8 +177,8 @@
 
             // subtract starting values and return delta
             final NetworkStats profilingStop = getNetworkStatsForUid(context);
-            final NetworkStats profilingDelta = profilingStop.subtract(
-                    sActiveProfilingStart, false);
+            final NetworkStats profilingDelta = profilingStop.subtractClamped(
+                    sActiveProfilingStart);
             sActiveProfilingStart = null;
             return profilingDelta;
         }
diff --git a/core/java/android/nfc/ErrorCodes.java b/core/java/android/nfc/ErrorCodes.java
index 69329df..3adcdc3 100644
--- a/core/java/android/nfc/ErrorCodes.java
+++ b/core/java/android/nfc/ErrorCodes.java
@@ -57,6 +57,7 @@
             case ERROR_SE_ALREADY_SELECTED: return "SE_ALREADY_SELECTED";
             case ERROR_SE_CONNECTED: return "SE_CONNECTED";
             case ERROR_NO_SE_CONNECTED: return "NO_SE_CONNECTED";
+            case ERROR_NOT_SUPPORTED: return "NOT_SUPPORTED";
             default: return "UNKNOWN ERROR";
         }
     }
@@ -105,4 +106,6 @@
 
     public static final int ERROR_NO_SE_CONNECTED = -20;
 
-}
\ No newline at end of file
+    public static final int ERROR_NOT_SUPPORTED = -21;
+
+}
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 57dc38c..b66035f 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -17,6 +17,7 @@
 package android.nfc;
 
 import android.nfc.NdefMessage;
+import android.nfc.Tag;
 import android.nfc.TransceiveResult;
 
 /**
@@ -40,7 +41,9 @@
     int ndefMakeReadOnly(int nativeHandle);
     boolean ndefIsWritable(int nativeHandle);
     int formatNdef(int nativeHandle, in byte[] key);
+    Tag rediscover(int nativehandle);
 
     void setIsoDepTimeout(int timeout);
-    void resetIsoDepTimeout();
+    void setFelicaTimeout(int timeout);
+    void resetTimeouts();
 }
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index fe215fd..3fd26dd 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -16,10 +16,13 @@
 
 package android.nfc;
 
+import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 
 import java.lang.UnsupportedOperationException;
+import java.nio.charset.Charsets;
+import java.util.Arrays;
 
 /**
  * Represents a logical (unchunked) NDEF (NFC Data Exchange Format) record.
@@ -142,6 +145,50 @@
     private static final byte FLAG_SR = (byte) 0x10;
     private static final byte FLAG_IL = (byte) 0x08;
 
+    /**
+     * NFC Forum "URI Record Type Definition"
+     *
+     * This is a mapping of "URI Identifier Codes" to URI string prefixes,
+     * per section 3.2.2 of the NFC Forum URI Record Type Definition document.
+     */
+    private static final String[] URI_PREFIX_MAP = new String[] {
+            "", // 0x00
+            "http://www.", // 0x01
+            "https://www.", // 0x02
+            "http://", // 0x03
+            "https://", // 0x04
+            "tel:", // 0x05
+            "mailto:", // 0x06
+            "ftp://anonymous:anonymous@", // 0x07
+            "ftp://ftp.", // 0x08
+            "ftps://", // 0x09
+            "sftp://", // 0x0A
+            "smb://", // 0x0B
+            "nfs://", // 0x0C
+            "ftp://", // 0x0D
+            "dav://", // 0x0E
+            "news:", // 0x0F
+            "telnet://", // 0x10
+            "imap:", // 0x11
+            "rtsp://", // 0x12
+            "urn:", // 0x13
+            "pop:", // 0x14
+            "sip:", // 0x15
+            "sips:", // 0x16
+            "tftp:", // 0x17
+            "btspp://", // 0x18
+            "btl2cap://", // 0x19
+            "btgoep://", // 0x1A
+            "tcpobex://", // 0x1B
+            "irdaobex://", // 0x1C
+            "file://", // 0x1D
+            "urn:epc:id:", // 0x1E
+            "urn:epc:tag:", // 0x1F
+            "urn:epc:pat:", // 0x20
+            "urn:epc:raw:", // 0x21
+            "urn:epc:", // 0x22
+    };
+
     private final byte mFlags;
     private final short mTnf;
     private final byte[] mType;
@@ -256,6 +303,50 @@
     }
 
     /**
+     * Helper to return the NdefRecord as a URI.
+     * TODO: Consider making a member method instead of static
+     * TODO: Consider more validation that this is a URI record
+     * TODO: Make a public API
+     * @hide
+     */
+    public static Uri parseWellKnownUriRecord(NdefRecord record) throws FormatException {
+        byte[] payload = record.getPayload();
+        if (payload.length < 2) {
+            throw new FormatException("Payload is not a valid URI (missing prefix)");
+        }
+
+        /*
+         * payload[0] contains the URI Identifier Code, per the
+         * NFC Forum "URI Record Type Definition" section 3.2.2.
+         *
+         * payload[1]...payload[payload.length - 1] contains the rest of
+         * the URI.
+         */
+        int prefixIndex = (payload[0] & 0xff);
+        if (prefixIndex < 0 || prefixIndex >= URI_PREFIX_MAP.length) {
+            throw new FormatException("Payload is not a valid URI (invalid prefix)");
+        }
+        String prefix = URI_PREFIX_MAP[prefixIndex];
+        byte[] fullUri = concat(prefix.getBytes(Charsets.UTF_8),
+                Arrays.copyOfRange(payload, 1, payload.length));
+        return Uri.parse(new String(fullUri, Charsets.UTF_8));
+    }
+
+    private static byte[] concat(byte[]... arrays) {
+        int length = 0;
+        for (byte[] array : arrays) {
+            length += array.length;
+        }
+        byte[] result = new byte[length];
+        int pos = 0;
+        for (byte[] array : arrays) {
+            System.arraycopy(array, 0, result, pos, array.length);
+            pos += array.length;
+        }
+        return result;
+    }
+
+    /**
      * Returns this entire NDEF Record as a byte array.
      */
     public byte[] toByteArray() {
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index b676975..54583d6 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -30,7 +30,9 @@
 import android.os.Bundle;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
 
+import java.io.IOException;
 import java.util.Arrays;
 
 /**
@@ -233,6 +235,50 @@
         return mTechStringList;
     }
 
+    /**
+     * Rediscover the technologies available on this tag.
+     * <p>
+     * The technologies that are available on a tag may change due to
+     * operations being performed on a tag. For example, formatting a
+     * tag as NDEF adds the {@link Ndef} technology. The {@link rediscover}
+     * method reenumerates the available technologies on the tag
+     * and returns a new {@link Tag} object containing these technologies.
+     * <p>
+     * You may not be connected to any of this {@link Tag}'s technologies
+     * when calling this method.
+     * This method guarantees that you will be returned the same Tag
+     * if it is still in the field.
+     * <p>May cause RF activity and may block. Must not be called
+     * from the main application thread. A blocked call will be canceled with
+     * {@link IOException} by calling {@link #close} from another thread.
+     * <p>Does not remove power from the RF field, so a tag having a random
+     * ID should not change its ID.
+     * @return the rediscovered tag object.
+     * @throws IOException if the tag cannot be rediscovered
+     * @hide
+     */
+    // TODO See if we need TagLostException
+    // TODO Unhide for ICS
+    // TODO Update documentation to make sure it matches with the final
+    //      implementation.
+    public Tag rediscover() throws IOException {
+        if (getConnectedTechnology() != -1) {
+            throw new IllegalStateException("Close connection to the technology first!");
+        }
+
+        try {
+            Tag newTag = mTagService.rediscover(getServiceHandle());
+            if (newTag != null) {
+                return newTag;
+            } else {
+                throw new IOException("Failed to rediscover tag");
+            }
+        } catch (RemoteException e) {
+            throw new IOException("NFC service dead");
+        }
+    }
+
+
     /** @hide */
     public boolean hasTech(int techType) {
         for (int tech : mTechList) {
diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java
index 7ec807a..bcb7199 100644
--- a/core/java/android/nfc/tech/BasicTagTechnology.java
+++ b/core/java/android/nfc/tech/BasicTagTechnology.java
@@ -77,6 +77,10 @@
                 // Store this in the tag object
                 mTag.setConnectedTechnology(mSelectedTechnology);
                 mIsConnected = true;
+            } else if (errorCode == ErrorCodes.ERROR_NOT_SUPPORTED) {
+                throw new UnsupportedOperationException("Connecting to " +
+                        "this technology is not supported by the NFC " +
+                        "adapter.");
             } else {
                 throw new IOException();
             }
@@ -115,6 +119,7 @@
             /* Note that we don't want to physically disconnect the tag,
              * but just reconnect to it to reset its state
              */
+            mTag.getTagService().resetTimeouts();
             mTag.getTagService().reconnect(mTag.getServiceHandle());
         } catch (RemoteException e) {
             Log.e(TAG, "NFC service dead", e);
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 9c3074b..38b2bbd 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -96,16 +96,6 @@
         }
     }
 
-    @Override
-    public void close() throws IOException {
-        try {
-            mTag.getTagService().resetIsoDepTimeout();
-        } catch (RemoteException e) {
-            Log.e(TAG, "NFC service dead", e);
-        }
-        super.close();
-    }
-
     /**
      * Return the ISO-DEP historical bytes for {@link NfcA} tags.
      * <p>Does not cause any RF activity and does not block.
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
index e0ebbe82..250c9b3 100644
--- a/core/java/android/nfc/tech/NfcF.java
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -19,6 +19,7 @@
 import android.nfc.Tag;
 import android.os.Bundle;
 import android.os.RemoteException;
+import android.util.Log;
 
 import java.io.IOException;
 
@@ -33,6 +34,8 @@
  * require the {@link android.Manifest.permission#NFC} permission.
  */
 public final class NfcF extends BasicTagTechnology {
+    private static final String TAG = "NFC";
+
     /** @hide */
     public static final String EXTRA_SC = "systemcode";
     /** @hide */
@@ -111,4 +114,26 @@
     public byte[] transceive(byte[] data) throws IOException {
         return transceive(data, true);
     }
+
+    /**
+     * Set the timeout of {@link #transceive} in milliseconds.
+     * <p>The timeout only applies to NfcF {@link #transceive}, and is
+     * reset to a default value when {@link #close} is called.
+     * <p>Setting a longer timeout may be useful when performing
+     * transactions that require a long processing time on the tag
+     * such as key generation.
+     *
+     * <p class="note">Requires the {@link android.Manifest.permission#NFC} permission.
+     *
+     * @param timeout timeout value in milliseconds
+     * @hide
+     */
+    // TODO Unhide for ICS
+    public void setTimeout(int timeout) {
+        try {
+            mTag.getTagService().setFelicaTimeout(timeout);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+        }
+    }
 }
diff --git a/core/java/android/os/storage/StorageVolume.java b/core/java/android/os/storage/StorageVolume.java
index bc4208a..792e4c1 100644
--- a/core/java/android/os/storage/StorageVolume.java
+++ b/core/java/android/os/storage/StorageVolume.java
@@ -32,6 +32,7 @@
     private final boolean mRemovable;
     private final boolean mEmulated;
     private final int mMtpReserveSpace;
+    private final boolean mAllowMassStorage;
     private int mStorageId;
 
     // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
@@ -39,23 +40,25 @@
     // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
     public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
 
-    public StorageVolume(String path, String description,
-            boolean removable, boolean emulated, int mtpReserveSpace) {
+    public StorageVolume(String path, String description, boolean removable,
+            boolean emulated, int mtpReserveSpace, boolean allowMassStorage) {
         mPath = path;
         mDescription = description;
         mRemovable = removable;
         mEmulated = emulated;
         mMtpReserveSpace = mtpReserveSpace;
+        mAllowMassStorage = allowMassStorage;
     }
 
     // for parcelling only
-    private StorageVolume(String path, String description,
-            boolean removable, boolean emulated, int mtpReserveSpace, int storageId) {
+    private StorageVolume(String path, String description, boolean removable,
+            boolean emulated, int mtpReserveSpace, int storageId, boolean allowMassStorage) {
         mPath = path;
         mDescription = description;
         mRemovable = removable;
         mEmulated = emulated;
         mMtpReserveSpace = mtpReserveSpace;
+        mAllowMassStorage = allowMassStorage;
         mStorageId = storageId;
     }
 
@@ -130,6 +133,15 @@
         return mMtpReserveSpace;
     }
 
+    /**
+     * Returns true if this volume can be shared via USB mass storage.
+     *
+     * @return whether mass storage is allowed
+     */
+    public boolean allowMassStorage() {
+        return mAllowMassStorage;
+    }
+
     @Override
     public boolean equals(Object obj) {
         if (obj instanceof StorageVolume && mPath != null) {
@@ -158,9 +170,10 @@
             int emulated = in.readInt();
             int storageId = in.readInt();
             int mtpReserveSpace = in.readInt();
+            int allowMassStorage = in.readInt();
             return new StorageVolume(path, description,
                     removable == 1, emulated == 1,
-                    mtpReserveSpace, storageId);
+                    mtpReserveSpace, storageId, allowMassStorage == 1);
         }
 
         public StorageVolume[] newArray(int size) {
@@ -179,5 +192,6 @@
         parcel.writeInt(mEmulated ? 1 : 0);
         parcel.writeInt(mStorageId);
         parcel.writeInt(mMtpReserveSpace);
+        parcel.writeInt(mAllowMassStorage ? 1 : 0);
     }
 }
diff --git a/core/java/android/preference/CheckBoxPreference.java b/core/java/android/preference/CheckBoxPreference.java
index 2bf6c7b..437e553 100644
--- a/core/java/android/preference/CheckBoxPreference.java
+++ b/core/java/android/preference/CheckBoxPreference.java
@@ -16,20 +16,11 @@
 
 package android.preference;
 
-import android.app.Service;
 import android.content.Context;
-import android.content.SharedPreferences;
 import android.content.res.TypedArray;
-import android.os.Parcel;
-import android.os.Parcelable;
 import android.util.AttributeSet;
 import android.view.View;
-import android.view.ViewGroup;
-import android.view.accessibility.AccessibilityEvent;
-import android.view.accessibility.AccessibilityManager;
-import android.widget.CheckBox;
 import android.widget.Checkable;
-import android.widget.TextView;
 
 /**
  * A {@link Preference} that provides checkbox widget
@@ -41,31 +32,18 @@
  * @attr ref android.R.styleable#CheckBoxPreference_summaryOn
  * @attr ref android.R.styleable#CheckBoxPreference_disableDependentsState
  */
-public class CheckBoxPreference extends Preference {
+public class CheckBoxPreference extends TwoStatePreference {
 
-    private CharSequence mSummaryOn;
-    private CharSequence mSummaryOff;
-    
-    private boolean mChecked;
-    private boolean mSendAccessibilityEventViewClickedType;
-
-    private AccessibilityManager mAccessibilityManager;
-    
-    private boolean mDisableDependentsState;
-    
     public CheckBoxPreference(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
         
         TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.CheckBoxPreference, defStyle, 0);
-        mSummaryOn = a.getString(com.android.internal.R.styleable.CheckBoxPreference_summaryOn);
-        mSummaryOff = a.getString(com.android.internal.R.styleable.CheckBoxPreference_summaryOff);
-        mDisableDependentsState = a.getBoolean(
-                com.android.internal.R.styleable.CheckBoxPreference_disableDependentsState, false);
+        setSummaryOn(a.getString(com.android.internal.R.styleable.CheckBoxPreference_summaryOn));
+        setSummaryOff(a.getString(com.android.internal.R.styleable.CheckBoxPreference_summaryOff));
+        setDisableDependentsState(a.getBoolean(
+                com.android.internal.R.styleable.CheckBoxPreference_disableDependentsState, false));
         a.recycle();
-
-        mAccessibilityManager =
-            (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
     }
 
     public CheckBoxPreference(Context context, AttributeSet attrs) {
@@ -84,246 +62,9 @@
         if (checkboxView != null && checkboxView instanceof Checkable) {
             ((Checkable) checkboxView).setChecked(mChecked);
 
-            // send an event to announce the value change of the CheckBox and is done here
-            // because clicking a preference does not immediately change the checked state
-            // for example when enabling the WiFi
-            if (mSendAccessibilityEventViewClickedType &&
-                    mAccessibilityManager.isEnabled() &&
-                    checkboxView.isEnabled()) {
-                mSendAccessibilityEventViewClickedType = false;
-
-                // we send an event on behalf of the check box because in onBind the latter
-                // is detached from its parent and such views do not send accessibility events
-                AccessibilityEvent event = AccessibilityEvent.obtain(
-                        AccessibilityEvent.TYPE_VIEW_CLICKED);
-                event.setClassName(checkboxView.getClass().getName());
-                event.setPackageName(getContext().getPackageName());
-                event.setEnabled(checkboxView.isEnabled());
-                event.setContentDescription(checkboxView.getContentDescription());
-                event.setChecked(((Checkable) checkboxView).isChecked());
-                mAccessibilityManager.sendAccessibilityEvent(event);
-            }
+            sendAccessibilityEventForView(checkboxView);
         }
 
-        // Sync the summary view
-        TextView summaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);
-        if (summaryView != null) {
-            boolean useDefaultSummary = true;
-            if (mChecked && mSummaryOn != null) {
-                summaryView.setText(mSummaryOn);
-                useDefaultSummary = false;
-            } else if (!mChecked && mSummaryOff != null) {
-                summaryView.setText(mSummaryOff);
-                useDefaultSummary = false;
-            }
-
-            if (useDefaultSummary) {
-                final CharSequence summary = getSummary();
-                if (summary != null) {
-                    summaryView.setText(summary);
-                    useDefaultSummary = false;
-                }
-            }
-            
-            int newVisibility = View.GONE;
-            if (!useDefaultSummary) {
-                // Someone has written to it
-                newVisibility = View.VISIBLE;
-            }
-            if (newVisibility != summaryView.getVisibility()) {
-                summaryView.setVisibility(newVisibility);
-            }
-        }
+        syncSummaryView(view);
     }
-    
-    @Override
-    protected void onClick() {
-        super.onClick();
-        
-        boolean newValue = !isChecked();
-        
-        // in onBindView() an AccessibilityEventViewClickedType is sent to announce the change
-        // not sending
-        mSendAccessibilityEventViewClickedType = true;
-
-        if (!callChangeListener(newValue)) {
-            return;
-        }
-        
-        setChecked(newValue);
-    }
-
-    /**
-     * Sets the checked state and saves it to the {@link SharedPreferences}.
-     * 
-     * @param checked The checked state.
-     */
-    public void setChecked(boolean checked) {
-        if (mChecked != checked) {
-            mChecked = checked;
-            persistBoolean(checked);
-            notifyDependencyChange(shouldDisableDependents());
-            notifyChanged();
-        }
-    }
-
-    /**
-     * Returns the checked state.
-     * 
-     * @return The checked state.
-     */
-    public boolean isChecked() {
-        return mChecked;
-    }
-    
-    @Override
-    public boolean shouldDisableDependents() {
-        boolean shouldDisable = mDisableDependentsState ? mChecked : !mChecked;
-        return shouldDisable || super.shouldDisableDependents();
-    }
-
-    /**
-     * Sets the summary to be shown when checked.
-     * 
-     * @param summary The summary to be shown when checked.
-     */
-    public void setSummaryOn(CharSequence summary) {
-        mSummaryOn = summary;
-        if (isChecked()) {
-            notifyChanged();
-        }
-    }
-
-    /**
-     * @see #setSummaryOn(CharSequence)
-     * @param summaryResId The summary as a resource.
-     */
-    public void setSummaryOn(int summaryResId) {
-        setSummaryOn(getContext().getString(summaryResId));
-    }
-    
-    /**
-     * Returns the summary to be shown when checked.
-     * @return The summary.
-     */
-    public CharSequence getSummaryOn() {
-        return mSummaryOn;
-    }
-    
-    /**
-     * Sets the summary to be shown when unchecked.
-     * 
-     * @param summary The summary to be shown when unchecked.
-     */
-    public void setSummaryOff(CharSequence summary) {
-        mSummaryOff = summary;
-        if (!isChecked()) {
-            notifyChanged();
-        }
-    }
-
-    /**
-     * @see #setSummaryOff(CharSequence)
-     * @param summaryResId The summary as a resource.
-     */
-    public void setSummaryOff(int summaryResId) {
-        setSummaryOff(getContext().getString(summaryResId));
-    }
-    
-    /**
-     * Returns the summary to be shown when unchecked.
-     * @return The summary.
-     */
-    public CharSequence getSummaryOff() {
-        return mSummaryOff;
-    }
-
-    /**
-     * Returns whether dependents are disabled when this preference is on ({@code true})
-     * or when this preference is off ({@code false}).
-     * 
-     * @return Whether dependents are disabled when this preference is on ({@code true})
-     *         or when this preference is off ({@code false}).
-     */
-    public boolean getDisableDependentsState() {
-        return mDisableDependentsState;
-    }
-
-    /**
-     * Sets whether dependents are disabled when this preference is on ({@code true})
-     * or when this preference is off ({@code false}).
-     * 
-     * @param disableDependentsState The preference state that should disable dependents.
-     */
-    public void setDisableDependentsState(boolean disableDependentsState) {
-        mDisableDependentsState = disableDependentsState;
-    }
-
-    @Override
-    protected Object onGetDefaultValue(TypedArray a, int index) {
-        return a.getBoolean(index, false);
-    }
-
-    @Override
-    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
-        setChecked(restoreValue ? getPersistedBoolean(mChecked)
-                : (Boolean) defaultValue);
-    }
-
-    @Override
-    protected Parcelable onSaveInstanceState() {
-        final Parcelable superState = super.onSaveInstanceState();
-        if (isPersistent()) {
-            // No need to save instance state since it's persistent
-            return superState;
-        }
-        
-        final SavedState myState = new SavedState(superState);
-        myState.checked = isChecked();
-        return myState;
-    }
-
-    @Override
-    protected void onRestoreInstanceState(Parcelable state) {
-        if (state == null || !state.getClass().equals(SavedState.class)) {
-            // Didn't save state for us in onSaveInstanceState
-            super.onRestoreInstanceState(state);
-            return;
-        }
-         
-        SavedState myState = (SavedState) state;
-        super.onRestoreInstanceState(myState.getSuperState());
-        setChecked(myState.checked);
-    }
-    
-    private static class SavedState extends BaseSavedState {
-        boolean checked;
-        
-        public SavedState(Parcel source) {
-            super(source);
-            checked = source.readInt() == 1;
-        }
-
-        @Override
-        public void writeToParcel(Parcel dest, int flags) {
-            super.writeToParcel(dest, flags);
-            dest.writeInt(checked ? 1 : 0);
-        }
-
-        public SavedState(Parcelable superState) {
-            super(superState);
-        }
-
-        public static final Parcelable.Creator<SavedState> CREATOR =
-                new Parcelable.Creator<SavedState>() {
-            public SavedState createFromParcel(Parcel in) {
-                return new SavedState(in);
-            }
-
-            public SavedState[] newArray(int size) {
-                return new SavedState[size];
-            }
-        };
-    }
-    
 }
diff --git a/core/java/android/preference/SwitchPreference.java b/core/java/android/preference/SwitchPreference.java
new file mode 100644
index 0000000..f681526
--- /dev/null
+++ b/core/java/android/preference/SwitchPreference.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.preference;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.Checkable;
+import android.widget.CompoundButton;
+import android.widget.Switch;
+
+/**
+ * A {@link Preference} that provides a two-state toggleable option.
+ * <p>
+ * This preference will store a boolean into the SharedPreferences.
+ *
+ * @attr ref android.R.styleable#SwitchPreference_summaryOff
+ * @attr ref android.R.styleable#SwitchPreference_summaryOn
+ * @attr ref android.R.styleable#SwitchPreference_switchTextOff
+ * @attr ref android.R.styleable#SwitchPreference_switchTextOn
+ * @attr ref android.R.styleable#SwitchPreference_disableDependentsState
+ */
+public class SwitchPreference extends TwoStatePreference {
+    // Switch text for on and off states
+    private CharSequence mSwitchOn;
+    private CharSequence mSwitchOff;
+    private final Listener mListener = new Listener();
+
+    private class Listener implements View.OnClickListener, CompoundButton.OnCheckedChangeListener {
+        @Override
+        public void onClick(View v) {
+            SwitchPreference.this.onClick();
+        }
+
+        @Override
+        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+            SwitchPreference.this.setChecked(isChecked);
+        }
+    }
+
+    /**
+     * Construct a new SwitchPreference with the given style options.
+     *
+     * @param context The Context that will style this preference
+     * @param attrs Style attributes that differ from the default
+     * @param defStyle Theme attribute defining the default style options
+     */
+    public SwitchPreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.SwitchPreference, defStyle, 0);
+        setSummaryOn(a.getString(com.android.internal.R.styleable.SwitchPreference_summaryOn));
+        setSummaryOff(a.getString(com.android.internal.R.styleable.SwitchPreference_summaryOff));
+        setSwitchTextOn(a.getString(
+                com.android.internal.R.styleable.SwitchPreference_switchTextOn));
+        setSwitchTextOff(a.getString(
+                com.android.internal.R.styleable.SwitchPreference_switchTextOff));
+        setDisableDependentsState(a.getBoolean(
+                com.android.internal.R.styleable.SwitchPreference_disableDependentsState, false));
+        a.recycle();
+    }
+
+    /**
+     * Construct a new SwitchPreference with the given style options.
+     *
+     * @param context The Context that will style this preference
+     * @param attrs Style attributes that differ from the default
+     */
+    public SwitchPreference(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.switchPreferenceStyle);
+    }
+
+    /**
+     * Construct a new SwitchPreference with default style options.
+     *
+     * @param context The Context that will style this preference
+     */
+    public SwitchPreference(Context context) {
+        this(context, null);
+    }
+
+    @Override
+    protected void onBindView(View view) {
+        super.onBindView(view);
+
+        View checkableView = view.findViewById(com.android.internal.R.id.switchWidget);
+        if (checkableView != null && checkableView instanceof Checkable) {
+            ((Checkable) checkableView).setChecked(mChecked);
+
+            sendAccessibilityEventForView(checkableView);
+
+            if (checkableView instanceof Switch) {
+                final Switch switchView = (Switch) checkableView;
+                switchView.setTextOn(mSwitchOn);
+                switchView.setTextOff(mSwitchOff);
+                switchView.setOnCheckedChangeListener(mListener);
+            }
+
+            if (checkableView.hasFocusable()) {
+                // This is a focusable list item. Attach a click handler to toggle the button
+                // for the rest of the item.
+                view.setOnClickListener(mListener);
+            }
+        }
+
+        syncSummaryView(view);
+    }
+
+    /**
+     * Set the text displayed on the switch widget in the on state.
+     * This should be a very short string; one word if possible.
+     *
+     * @param onText Text to display in the on state
+     */
+    public void setSwitchTextOn(CharSequence onText) {
+        mSwitchOn = onText;
+        notifyChanged();
+    }
+
+    /**
+     * Set the text displayed on the switch widget in the off state.
+     * This should be a very short string; one word if possible.
+     *
+     * @param offText Text to display in the off state
+     */
+    public void setSwitchTextOff(CharSequence offText) {
+        mSwitchOff = offText;
+        notifyChanged();
+    }
+
+    /**
+     * Set the text displayed on the switch widget in the on state.
+     * This should be a very short string; one word if possible.
+     *
+     * @param resId The text as a string resource ID
+     */
+    public void setSwitchTextOn(int resId) {
+        setSwitchTextOn(getContext().getString(resId));
+    }
+
+    /**
+     * Set the text displayed on the switch widget in the off state.
+     * This should be a very short string; one word if possible.
+     *
+     * @param resId The text as a string resource ID
+     */
+    public void setSwitchTextOff(int resId) {
+        setSwitchTextOff(getContext().getString(resId));
+    }
+
+    /**
+     * @return The text that will be displayed on the switch widget in the on state
+     */
+    public CharSequence getSwitchTextOn() {
+        return mSwitchOn;
+    }
+
+    /**
+     * @return The text that will be displayed on the switch widget in the off state
+     */
+    public CharSequence getSwitchTextOff() {
+        return mSwitchOff;
+    }
+}
diff --git a/core/java/android/preference/TwoStatePreference.java b/core/java/android/preference/TwoStatePreference.java
new file mode 100644
index 0000000..8e21c4c
--- /dev/null
+++ b/core/java/android/preference/TwoStatePreference.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.preference;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.res.TypedArray;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.TextView;
+
+/**
+ * Common base class for preferences that have two selectable states, persist a
+ * boolean value in SharedPreferences, and may have dependent preferences that are
+ * enabled/disabled based on the current state.
+ */
+public abstract class TwoStatePreference extends Preference {
+
+    private CharSequence mSummaryOn;
+    private CharSequence mSummaryOff;
+    boolean mChecked;
+    private boolean mSendAccessibilityEventViewClickedType;
+    private AccessibilityManager mAccessibilityManager;
+    private boolean mDisableDependentsState;
+
+    public TwoStatePreference(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+
+        mAccessibilityManager =
+                (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
+    }
+
+    public TwoStatePreference(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        mAccessibilityManager =
+                (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
+    }
+
+    public TwoStatePreference(Context context) {
+        super(context);
+
+        mAccessibilityManager =
+                (AccessibilityManager) getContext().getSystemService(Service.ACCESSIBILITY_SERVICE);
+    }
+
+    @Override
+    protected void onClick() {
+        super.onClick();
+
+        boolean newValue = !isChecked();
+
+        // in onBindView() an AccessibilityEventViewClickedType is sent to announce the change
+        // not sending
+        mSendAccessibilityEventViewClickedType = true;
+
+        if (!callChangeListener(newValue)) {
+            return;
+        }
+
+        setChecked(newValue);
+    }
+
+    /**
+     * Sets the checked state and saves it to the {@link SharedPreferences}.
+     *
+     * @param checked The checked state.
+     */
+    public void setChecked(boolean checked) {
+        if (mChecked != checked) {
+            mChecked = checked;
+            persistBoolean(checked);
+            notifyDependencyChange(shouldDisableDependents());
+            notifyChanged();
+        }
+    }
+
+    /**
+     * Returns the checked state.
+     *
+     * @return The checked state.
+     */
+    public boolean isChecked() {
+        return mChecked;
+    }
+
+    @Override
+    public boolean shouldDisableDependents() {
+        boolean shouldDisable = mDisableDependentsState ? mChecked : !mChecked;
+        return shouldDisable || super.shouldDisableDependents();
+    }
+
+    /**
+     * Sets the summary to be shown when checked.
+     *
+     * @param summary The summary to be shown when checked.
+     */
+    public void setSummaryOn(CharSequence summary) {
+        mSummaryOn = summary;
+        if (isChecked()) {
+            notifyChanged();
+        }
+    }
+
+    /**
+     * @see #setSummaryOn(CharSequence)
+     * @param summaryResId The summary as a resource.
+     */
+    public void setSummaryOn(int summaryResId) {
+        setSummaryOn(getContext().getString(summaryResId));
+    }
+
+    /**
+     * Returns the summary to be shown when checked.
+     * @return The summary.
+     */
+    public CharSequence getSummaryOn() {
+        return mSummaryOn;
+    }
+
+    /**
+     * Sets the summary to be shown when unchecked.
+     *
+     * @param summary The summary to be shown when unchecked.
+     */
+    public void setSummaryOff(CharSequence summary) {
+        mSummaryOff = summary;
+        if (!isChecked()) {
+            notifyChanged();
+        }
+    }
+
+    /**
+     * @see #setSummaryOff(CharSequence)
+     * @param summaryResId The summary as a resource.
+     */
+    public void setSummaryOff(int summaryResId) {
+        setSummaryOff(getContext().getString(summaryResId));
+    }
+
+    /**
+     * Returns the summary to be shown when unchecked.
+     * @return The summary.
+     */
+    public CharSequence getSummaryOff() {
+        return mSummaryOff;
+    }
+
+    /**
+     * Returns whether dependents are disabled when this preference is on ({@code true})
+     * or when this preference is off ({@code false}).
+     *
+     * @return Whether dependents are disabled when this preference is on ({@code true})
+     *         or when this preference is off ({@code false}).
+     */
+    public boolean getDisableDependentsState() {
+        return mDisableDependentsState;
+    }
+
+    /**
+     * Sets whether dependents are disabled when this preference is on ({@code true})
+     * or when this preference is off ({@code false}).
+     *
+     * @param disableDependentsState The preference state that should disable dependents.
+     */
+    public void setDisableDependentsState(boolean disableDependentsState) {
+        mDisableDependentsState = disableDependentsState;
+    }
+
+    @Override
+    protected Object onGetDefaultValue(TypedArray a, int index) {
+        return a.getBoolean(index, false);
+    }
+
+    @Override
+    protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
+        setChecked(restoreValue ? getPersistedBoolean(mChecked)
+                : (Boolean) defaultValue);
+    }
+
+    /**
+     * Send an accessibility event for the given view if appropriate
+     * @param view View that should send the event
+     */
+    void sendAccessibilityEventForView(View view) {
+        // send an event to announce the value change of the state. It is done here
+        // because clicking a preference does not immediately change the checked state
+        // for example when enabling the WiFi
+        if (mSendAccessibilityEventViewClickedType &&
+                mAccessibilityManager.isEnabled() &&
+                view.isEnabled()) {
+            mSendAccessibilityEventViewClickedType = false;
+
+            int eventType = AccessibilityEvent.TYPE_VIEW_CLICKED;
+            view.sendAccessibilityEventUnchecked(AccessibilityEvent.obtain(eventType));
+        }
+    }
+
+    /**
+     * Sync a summary view contained within view's subhierarchy with the correct summary text.
+     * @param view View where a summary should be located
+     */
+    void syncSummaryView(View view) {
+        // Sync the summary view
+        TextView summaryView = (TextView) view.findViewById(com.android.internal.R.id.summary);
+        if (summaryView != null) {
+            boolean useDefaultSummary = true;
+            if (mChecked && mSummaryOn != null) {
+                summaryView.setText(mSummaryOn);
+                useDefaultSummary = false;
+            } else if (!mChecked && mSummaryOff != null) {
+                summaryView.setText(mSummaryOff);
+                useDefaultSummary = false;
+            }
+
+            if (useDefaultSummary) {
+                final CharSequence summary = getSummary();
+                if (summary != null) {
+                    summaryView.setText(summary);
+                    useDefaultSummary = false;
+                }
+            }
+
+            int newVisibility = View.GONE;
+            if (!useDefaultSummary) {
+                // Someone has written to it
+                newVisibility = View.VISIBLE;
+            }
+            if (newVisibility != summaryView.getVisibility()) {
+                summaryView.setVisibility(newVisibility);
+            }
+        }
+    }
+
+    @Override
+    protected Parcelable onSaveInstanceState() {
+        final Parcelable superState = super.onSaveInstanceState();
+        if (isPersistent()) {
+            // No need to save instance state since it's persistent
+            return superState;
+        }
+
+        final SavedState myState = new SavedState(superState);
+        myState.checked = isChecked();
+        return myState;
+    }
+
+    @Override
+    protected void onRestoreInstanceState(Parcelable state) {
+        if (state == null || !state.getClass().equals(SavedState.class)) {
+            // Didn't save state for us in onSaveInstanceState
+            super.onRestoreInstanceState(state);
+            return;
+        }
+
+        SavedState myState = (SavedState) state;
+        super.onRestoreInstanceState(myState.getSuperState());
+        setChecked(myState.checked);
+    }
+
+    static class SavedState extends BaseSavedState {
+        boolean checked;
+
+        public SavedState(Parcel source) {
+            super(source);
+            checked = source.readInt() == 1;
+        }
+
+        @Override
+        public void writeToParcel(Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+            dest.writeInt(checked ? 1 : 0);
+        }
+
+        public SavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        public static final Parcelable.Creator<SavedState> CREATOR =
+                new Parcelable.Creator<SavedState>() {
+            public SavedState createFromParcel(Parcel in) {
+                return new SavedState(in);
+            }
+
+            public SavedState[] newArray(int size) {
+                return new SavedState[size];
+            }
+        };
+    }
+}
diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java
index 0ef38bf..60d9f1a 100644
--- a/core/java/android/provider/Calendar.java
+++ b/core/java/android/provider/Calendar.java
@@ -16,6 +16,7 @@
 
 package android.provider;
 
+
 import android.accounts.Account;
 import android.app.AlarmManager;
 import android.app.PendingIntent;
@@ -32,7 +33,6 @@
 import android.database.DatabaseUtils;
 import android.net.Uri;
 import android.os.RemoteException;
-import android.pim.ICalendar;
 import android.text.TextUtils;
 import android.text.format.DateUtils;
 import android.text.format.Time;
@@ -44,24 +44,30 @@
  * @hide
  */
 public final class Calendar {
-
-    public static final String TAG = "Calendar";
+    private static final String TAG = "Calendar";
 
     /**
-     * Broadcast Action: An event reminder.
+     * Broadcast Action: This is the intent that gets fired when an alarm
+     * notification needs to be posted for a reminder.
      */
     public static final String EVENT_REMINDER_ACTION = "android.intent.action.EVENT_REMINDER";
 
     /**
-     * These are the symbolic names for the keys used in the extra data
-     * passed in the intent for event reminders.
+     * Intent Extras key: The start time of an event or an instance of a
+     * recurring event. (milliseconds since epoch)
      */
     public static final String EVENT_BEGIN_TIME = "beginTime";
+
+    /**
+     * Intent Extras key: The end time of an event or an instance of a recurring
+     * event. (milliseconds since epoch)
+     */
     public static final String EVENT_END_TIME = "endTime";
 
     /**
-     * This must not be changed or horrible, unspeakable things could happen.
-     * For instance, the Calendar app might break. Also, the db might not work.
+     * This authority is used for writing to or querying from the calendar
+     * provider. Note: This is set at first run and cannot be changed without
+     * breaking apps that access the provider.
      */
     public static final String AUTHORITY = "com.android.calendar";
 
@@ -73,20 +79,37 @@
 
     /**
      * An optional insert, update or delete URI parameter that allows the caller
-     * to specify that it is a sync adapter. The default value is false. If true
-     * the dirty flag is not automatically set and the "syncToNetwork" parameter
-     * is set to false when calling
-     * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}.
+     * to specify that it is a sync adapter. The default value is false. If set
+     * to true, the modified row is not marked as "dirty" (needs to be synced)
+     * and when the provider calls
+     * {@link ContentResolver#notifyChange(android.net.Uri, android.database.ContentObserver, boolean)}
+     * , the third parameter "syncToNetwork" is set to false. Furthermore, if
+     * set to true, the caller must also include
+     * {@link SyncColumns#ACCOUNT_NAME} and {@link SyncColumns#ACCOUNT_TYPE} as
+     * query parameters.
+     *
+     * @See Uri.Builder#appendQueryParameter(java.lang.String, java.lang.String)
      */
     public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
 
+    /**
+     * A special account type for calendars not associated with any account.
+     * Normally calendars that do not match an account on the device will be
+     * removed. Setting the account_type on a calendar to this will prevent it
+     * from being wiped if it does not match an existing account.
+     *
+     * @see SyncColumns#ACCOUNT_TYPE
+     */
+    public static final String ACCOUNT_TYPE_LOCAL = "LOCAL";
 
     /**
-     * Generic columns for use by sync adapters. The specific functions of
-     * these columns are private to the sync adapter. Other clients of the API
-     * should not attempt to either read or write this column.
+     * Generic columns for use by sync adapters. The specific functions of these
+     * columns are private to the sync adapter. Other clients of the API should
+     * not attempt to either read or write this column. These columns are
+     * editable as part of the Calendars Uri, but can only be read if accessed
+     * through any other Uri.
      */
-    protected interface BaseSyncColumns {
+    protected interface CalendarSyncColumns {
 
         /** Generic column for use by sync adapters. */
         public static final String CAL_SYNC1 = "cal_sync1";
@@ -103,29 +126,41 @@
     }
 
     /**
-     * Columns for Sync information used by Calendars and Events tables.
+     * Columns for Sync information used by Calendars and Events tables. These
+     * have specific uses which are expected to be consistent by the app and
+     * sync adapter.
+     *
+     * @hide
      */
-    public interface SyncColumns extends BaseSyncColumns {
+    public interface SyncColumns extends CalendarSyncColumns {
         /**
-         * The account that was used to sync the entry to the device.
+         * The account that was used to sync the entry to the device. If the
+         * account_type is not {@link #ACCOUNT_TYPE_LOCAL} then the name and
+         * type must match an account on the device or the calendar will be
+         * deleted.
          * <P>Type: TEXT</P>
          */
         public static final String ACCOUNT_NAME = "account_name";
 
         /**
-         * The type of the account that was used to sync the entry to the device.
+         * The type of the account that was used to sync the entry to the
+         * device. A type of {@link #ACCOUNT_TYPE_LOCAL} will keep this event
+         * form being deleted if there are no matching accounts on the device.
          * <P>Type: TEXT</P>
          */
         public static final String ACCOUNT_TYPE = "account_type";
 
         /**
-         * The unique ID for a row assigned by the sync source. NULL if the row has never been synced.
+         * The unique ID for a row assigned by the sync source. NULL if the row
+         * has never been synced. This is used as a reference id for exceptions
+         * along with {@link BaseColumns#_ID}.
          * <P>Type: TEXT</P>
          */
         public static final String _SYNC_ID = "_sync_id";
 
         /**
-         * The last time, from the sync source's point of view, that this row has been synchronized.
+         * The last time, from the sync source's point of view, that this row
+         * has been synchronized.
          * <P>Type: INTEGER (long)</P>
          */
         public static final String _SYNC_TIME = "_sync_time";
@@ -148,12 +183,21 @@
          */
         public static final String DIRTY = "dirty";
 
+        /**
+         * If set to 1 this causes events on this calendar to be duplicated with
+         * {@link EventsColumns#LAST_SYNCED} set to 1 whenever the event transitions from non-dirty
+         * to dirty. The duplicated event will not be expanded in the instances table and will only
+         * show up in sync adapter queries of the events table. It will also be deleted when the
+         * originating event has its dirty flag cleared by the sync adapter.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String CAN_PARTIALLY_UPDATE = "canPartiallyUpdate";
     }
 
     /**
-     * Columns from the Calendars table that other tables join into themselves.
+     * Columns specific to the Calendars Uri that other Uris can query.
      */
-    public interface CalendarsColumns {
+    private interface CalendarsColumns {
         /**
          * The color of the calendar
          * <P>Type: INTEGER (color value)</P>
@@ -172,10 +216,17 @@
         public static final int FREEBUSY_ACCESS = 100;
         /** Can read all event details */
         public static final int READ_ACCESS = 200;
+        /** Can reply yes/no/maybe to an event */
         public static final int RESPOND_ACCESS = 300;
+        /** not used */
         public static final int OVERRIDE_ACCESS = 400;
-        /** Full access to modify the calendar, but not the access control settings */
+        /** Full access to modify the calendar, but not the access control
+         * settings
+         */
         public static final int CONTRIBUTOR_ACCESS = 500;
+        /** Full access to modify the calendar, but not the access control
+         * settings
+         */
         public static final int EDITOR_ACCESS = 600;
         /** Full access to the calendar */
         public static final int OWNER_ACCESS = 700;
@@ -184,31 +235,35 @@
 
         /**
          * Is the calendar selected to be displayed?
+         * 0 - do not show events associated with this calendar.
+         * 1 - show events associated with this calendar
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String VISIBLE = "visible";
 
         /**
-         * The timezone the calendar's events occurs in
+         * The time zone the calendar is associated with.
          * <P>Type: TEXT</P>
          */
         public static final String CALENDAR_TIMEZONE = "calendar_timezone";
 
         /**
-         * If this calendar is in the list of calendars that are selected for
-         * syncing then "sync_events" is 1, otherwise 0.
+         * Is this calendar synced and are its events stored on the device?
+         * 0 - Do not sync this calendar or store events for this calendar.
+         * 1 - Sync down events for this calendar.
          * <p>Type: INTEGER (boolean)</p>
          */
         public static final String SYNC_EVENTS = "sync_events";
 
         /**
-         * Sync state data.
+         * Sync state data. Usable by the sync adapter.
          * <p>Type: String (blob)</p>
          */
         public static final String SYNC_STATE = "sync_state";
 
         /**
-         * Whether the row has been deleted.  A deleted row should be ignored.
+         * Whether the row has been deleted but not synced to the server. A
+         * deleted row should be ignored.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String DELETED = "deleted";
@@ -216,35 +271,32 @@
 
     /**
      * Class that represents a Calendar Entity. There is one entry per calendar.
+     * This is a helper class to make batch operations easier.
      */
     public static class CalendarsEntity implements BaseColumns, SyncColumns, CalendarsColumns {
 
+        /**
+         * The default Uri used when creating a new calendar EntityIterator.
+         */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
                 "/calendar_entities");
 
-        public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
-            return new EntityIteratorImpl(cursor, resolver);
-        }
-
-        public static EntityIterator newEntityIterator(Cursor cursor,
-                ContentProviderClient provider) {
-            return new EntityIteratorImpl(cursor, provider);
+        /**
+         * Creates an entity iterator for the given cursor. It assumes the
+         * cursor contains a calendars query.
+         *
+         * @param cursor query on {@link #CONTENT_URI}
+         * @return an EntityIterator of calendars
+         */
+        public static EntityIterator newEntityIterator(Cursor cursor) {
+            return new EntityIteratorImpl(cursor);
         }
 
         private static class EntityIteratorImpl extends CursorEntityIterator {
-            private final ContentResolver mResolver;
-            private final ContentProviderClient mProvider;
 
-            public EntityIteratorImpl(Cursor cursor, ContentResolver resolver) {
+            public EntityIteratorImpl(Cursor cursor) {
                 super(cursor);
-                mResolver = resolver;
-                mProvider = null;
-            }
-
-            public EntityIteratorImpl(Cursor cursor, ContentProviderClient provider) {
-                super(cursor);
-                mResolver = null;
-                mProvider = provider;
             }
 
             @Override
@@ -274,7 +326,8 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, Calendars.NAME);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv,
                         Calendars.DISPLAY_NAME);
-                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, Calendars.CALENDAR_COLOR);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+                        Calendars.CALENDAR_COLOR);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, ACCESS_LEVEL);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, VISIBLE);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, SYNC_EVENTS);
@@ -289,6 +342,8 @@
                         Calendars.CAN_MODIFY_TIME_ZONE);
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
                         Calendars.MAX_REMINDERS);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv,
+                        Calendars.CAN_PARTIALLY_UPDATE);
 
                 DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
 
@@ -305,27 +360,40 @@
      }
 
     /**
-     * Contains a list of available calendars.
+     * Fields and helpers for interacting with Calendars.
      */
-    public static class Calendars implements BaseColumns, SyncColumns,
-            CalendarsColumns
-    {
+    public static class Calendars implements BaseColumns, SyncColumns, CalendarsColumns {
         private static final String WHERE_DELETE_FOR_ACCOUNT = Calendars.ACCOUNT_NAME + "=?"
                 + " AND "
                 + Calendars.ACCOUNT_TYPE + "=?";
 
-        public static final Cursor query(ContentResolver cr, String[] projection,
-                                       String where, String orderBy)
-        {
-            return cr.query(CONTENT_URI, projection, where,
-                                         null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+        /**
+         * Helper function for generating a calendars query. This is blocking
+         * and should not be used on the UI thread. See
+         * {@link ContentResolver#query(Uri, String[], String, String[], String)}
+         * for more details about using the parameters.
+         *
+         * @param cr The ContentResolver to query with
+         * @param projection A list of columns to return
+         * @param selection A formatted selection string
+         * @param selectionArgs arguments to the selection string
+         * @param orderBy How to order the returned rows
+         * @return
+         */
+        public static final Cursor query(ContentResolver cr, String[] projection, String selection,
+                String[] selectionArgs, String orderBy) {
+            return cr.query(CONTENT_URI, projection, selection, selectionArgs,
+                    orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
         }
 
         /**
-         * Convenience method perform a delete on the Calendar provider
+         * Convenience method perform a delete on the Calendar provider. This is
+         * a blocking call and should not be used on the UI thread.
          *
          * @param cr the ContentResolver
-         * @param selection the rows to delete
+         * @param selection A filter to apply to rows before deleting, formatted
+         *            as an SQL WHERE clause (excluding the WHERE itself).
+         * @param selectionArgs Fill in the '?'s in the selection
          * @return the count of rows that were deleted
          */
         public static int delete(ContentResolver cr, String selection, String[] selectionArgs)
@@ -335,10 +403,12 @@
 
         /**
          * Convenience method to delete all calendars that match the account.
+         * This is a blocking call and should not be used on the UI thread.
          *
          * @param cr the ContentResolver
-         * @param account the account whose rows should be deleted
-         * @return the count of rows that were deleted
+         * @param account the account whose calendars and events should be
+         *            deleted
+         * @return the count of calendar rows that were deleted
          */
         public static int deleteCalendarsForAccount(ContentResolver cr, Account account) {
             // delete all calendars that match this account
@@ -348,8 +418,9 @@
         }
 
         /**
-         * The content:// style URL for this table
+         * The content:// style URL for accessing Calendars
          */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/calendars");
 
         /**
@@ -358,43 +429,43 @@
         public static final String DEFAULT_SORT_ORDER = "displayName";
 
         /**
-         * The URL to the calendar
+         * The URL to the calendar. Column name.
          * <P>Type: TEXT (URL)</P>
          */
         public static final String URL = "url";
 
         /**
-         * The URL for the calendar itself
+         * The URL for the calendar itself. Column name.
          * <P>Type: TEXT (URL)</P>
          */
         public static final String SELF_URL = "selfUrl";
 
         /**
-         * The URL for the calendar to be edited
+         * The URL for the calendar to be edited. Column name.
          * <P>Type: TEXT (URL)</P>
          */
         public static final String EDIT_URL = "editUrl";
 
         /**
-         * The URL for the calendar events
+         * The URL for the calendar events. Column name.
          * <P>Type: TEXT (URL)</P>
          */
         public static final String EVENTS_URL = "eventsUrl";
 
         /**
-         * The name of the calendar
+         * The name of the calendar. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String NAME = "name";
 
         /**
-         * The display name of the calendar
+         * The display name of the calendar. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String DISPLAY_NAME = "displayName";
 
         /**
-         * The location the of the events in the calendar
+         * The default location for the calendar. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String CALENDAR_LOCATION = "calendar_location";
@@ -402,41 +473,45 @@
         /**
          * The owner account for this calendar, based on the calendar feed.
          * This will be different from the _SYNC_ACCOUNT for delegated calendars.
+         * Column name.
          * <P>Type: String</P>
          */
         public static final String OWNER_ACCOUNT = "ownerAccount";
 
         /**
          * Can the organizer respond to the event?  If no, the status of the
-         * organizer should not be shown by the UI.  Defaults to 1
+         * organizer should not be shown by the UI.  Defaults to 1. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String CAN_ORGANIZER_RESPOND = "canOrganizerRespond";
 
         /**
-         * Can the organizer modify the time zone of the event?
+         * Can the organizer modify the time zone of the event? Column name.
          * <P>Type: INTEGER (boolean)</P>
         */
         public static final String CAN_MODIFY_TIME_ZONE = "canModifyTimeZone";
 
         /**
-         * The maximum number of reminders allowed for an event.
+         * The maximum number of reminders allowed for an event. Column name.
          * <P>Type: INTEGER</P>
          */
         public static final String MAX_REMINDERS = "maxReminders";
 
         /**
-         * The maximum number of reminders allowed for an event.
-         * <P>
-         * Type: INTEGER
-         * </P>
+         * A comma separated list of reminder methods supported for this
+         * calendar in the format "#,#,#". Valid types are
+         * {@link Reminders#METHOD_DEFAULT}, {@link Reminders#METHOD_ALERT},
+         * {@link Reminders#METHOD_EMAIL}, {@link Reminders#METHOD_SMS}. Column
+         * name.
+         * <P>Type: TEXT</P>
          */
         public static final String ALLOWED_REMINDERS = "allowedReminders";
 
         /**
          * These fields are only writable by a sync adapter. To modify them the
-         * caller must include CALLER_IS_SYNCADAPTER, _SYNC_ACCOUNT, and
-         * _SYNC_ACCOUNT_TYPE in the query parameters.
+         * caller must include {@link #CALLER_IS_SYNCADAPTER},
+         * {@link #ACCOUNT_NAME}, and {@link #ACCOUNT_TYPE} in the query
+         * parameters.
          */
         public static final String[] SYNC_WRITABLE_COLUMNS = new String[] {
             ACCOUNT_NAME,
@@ -449,6 +524,7 @@
             MAX_REMINDERS,
             CAN_MODIFY_TIME_ZONE,
             CAN_ORGANIZER_RESPOND,
+            CAN_PARTIALLY_UPDATE,
             CALENDAR_LOCATION,
             CALENDAR_TIMEZONE,
             ACCESS_LEVEL,
@@ -457,7 +533,8 @@
             CAL_SYNC2,
             CAL_SYNC3,
             CAL_SYNC4,
- CAL_SYNC5, CAL_SYNC6,
+            CAL_SYNC5,
+            CAL_SYNC6,
             SYNC_STATE,
         };
     }
@@ -465,29 +542,29 @@
     /**
      * Columns from the Attendees table that other tables join into themselves.
      */
-    public interface AttendeesColumns {
+    private interface AttendeesColumns {
 
         /**
-         * The id of the event.
+         * The id of the event. Column name.
          * <P>Type: INTEGER</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
-         * The name of the attendee.
+         * The name of the attendee. Column name.
          * <P>Type: STRING</P>
          */
         public static final String ATTENDEE_NAME = "attendeeName";
 
         /**
-         * The email address of the attendee.
+         * The email address of the attendee. Column name.
          * <P>Type: STRING</P>
          */
         public static final String ATTENDEE_EMAIL = "attendeeEmail";
 
         /**
-         * The relationship of the attendee to the user.
-         * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}.
+         * The relationship of the attendee to the user. Column name.
+         * <P>Type: INTEGER (one of {@link #RELATIONSHIP_ATTENDEE}, ...}.</P>
          */
         public static final String ATTENDEE_RELATIONSHIP = "attendeeRelationship";
 
@@ -498,8 +575,8 @@
         public static final int RELATIONSHIP_SPEAKER = 4;
 
         /**
-         * The type of attendee.
-         * <P>Type: Integer (one of {@link #TYPE_REQUIRED}, {@link #TYPE_OPTIONAL})
+         * The type of attendee. Column name.
+         * <P>Type: Integer (one of {@link #TYPE_REQUIRED}, {@link #TYPE_OPTIONAL})</P>
          */
         public static final String ATTENDEE_TYPE = "attendeeType";
 
@@ -508,8 +585,8 @@
         public static final int TYPE_OPTIONAL = 2;
 
         /**
-         * The attendance status of the attendee.
-         * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...}.
+         * The attendance status of the attendee. Column name.
+         * <P>Type: Integer (one of {@link #ATTENDEE_STATUS_ACCEPTED}, ...).</P>
          */
         public static final String ATTENDEE_STATUS = "attendeeStatus";
 
@@ -520,59 +597,84 @@
         public static final int ATTENDEE_STATUS_TENTATIVE = 4;
     }
 
+    /**
+     * Fields and helpers for interacting with Attendees.
+     */
     public static final class Attendees implements BaseColumns, AttendeesColumns, EventsColumns {
-        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees");
 
-        // TODO: fill out this class when we actually start utilizing attendees
-        // in the calendar application.
+        /**
+         * The content:// style URL for accessing Attendees data
+         */
+        @SuppressWarnings("hiding")
+        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/attendees");
+        /**
+         * the projection used by the attendees query
+         */
+        public static final String[] PROJECTION = new String[] {
+                _ID, ATTENDEE_NAME, ATTENDEE_EMAIL, ATTENDEE_RELATIONSHIP, ATTENDEE_STATUS,};
+        private static final String ATTENDEES_WHERE = Attendees.EVENT_ID + "=?";
+
+        /**
+         * Queries all attendees associated with the given event. This is a
+         * blocking call and should not be done on the UI thread.
+         *
+         * @param cr The content resolver to use for the query
+         * @param eventId The id of the event to retrieve attendees for
+         * @return A Cursor containing all attendees for the event
+         */
+        public static final Cursor query(ContentResolver cr, long eventId) {
+            String[] attArgs = {Long.toString(eventId)};
+            return cr.query(CONTENT_URI, PROJECTION, ATTENDEES_WHERE, attArgs /* selection args */,
+                    null /* sort order */);
+        }
     }
 
     /**
      * Columns from the Events table that other tables join into themselves.
      */
-    public interface EventsColumns {
+    private interface EventsColumns {
         /**
-         * For use by sync adapter at its discretion; not modified by CalendarProvider
-         * Note that this column was formerly named _SYNC_LOCAL_ID.  We are using it to avoid a
-         * schema change.
-         * TODO Replace this with something more general in the future.
+         * For use by sync adapter at its discretion. Column name.
+         * TODO change to sync_data2
          * <P>Type: INTEGER (long)</P>
          */
         public static final String _SYNC_DATA = "_sync_local_id";
 
         /**
-         * The calendar the event belongs to
-         * <P>Type: INTEGER (foreign key to the Calendars table)</P>
+         * The {@link Calendars#_ID} of the calendar the event belongs to.
+         * Column name.
+         * <P>Type: INTEGER</P>
          */
         public static final String CALENDAR_ID = "calendar_id";
 
         /**
-         * The URI for an HTML version of this event.
+         * The URI for an HTML version of this event. Column name.
+         * TODO change to sync_data3
          * <P>Type: TEXT</P>
          */
         public static final String HTML_URI = "htmlUri";
 
         /**
-         * The title of the event
+         * The title of the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String TITLE = "title";
 
         /**
-         * The description of the event
+         * The description of the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String DESCRIPTION = "description";
 
         /**
-         * Where the event takes place.
+         * Where the event takes place. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String EVENT_LOCATION = "eventLocation";
 
         /**
-         * The event status
-         * <P>Type: INTEGER (int)</P>
+         * The event status. Column name.
+         * <P>Type: INTEGER (one of {@link #STATUS_TENTATIVE}...)</P>
          */
         public static final String STATUS = "eventStatus";
 
@@ -584,64 +686,82 @@
          * This is a copy of the attendee status for the owner of this event.
          * This field is copied here so that we can efficiently filter out
          * events that are declined without having to look in the Attendees
-         * table.
+         * table. Column name.
          *
          * <P>Type: INTEGER (int)</P>
          */
         public static final String SELF_ATTENDEE_STATUS = "selfAttendeeStatus";
 
         /**
-         * This column is available for use by sync adapters
+         * This column is available for use by sync adapters. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String SYNC_DATA1 = "sync_data1";
 
         /**
-         * The comments feed uri.
+         * This column is available for use by sync adapters
+         * <P>Type: TEXT</P>
+         */
+        public static final String SYNC_DATA7 = "sync_data7";
+
+        /**
+         * Used to indicate that a row is not a real event but an original copy of a locally
+         * modified event. A copy is made when an event changes from non-dirty to dirty and the
+         * event is on a calendar with {@link Calendars#CAN_PARTIALLY_UPDATE} set to 1. This copy
+         * does not get expanded in the instances table and is only visible in queries made by a
+         * sync adapter. The copy gets removed when the event is changed back to non-dirty by a
+         * sync adapter.
+         * <P>Type: INTEGER (boolean)</P>
+         */
+        public static final String LAST_SYNCED = "lastSynced";
+
+        /**
+         * The comments feed uri. Column name.
+         * TODO change to sync_data6
          * <P>Type: TEXT</P>
          */
         public static final String COMMENTS_URI = "commentsUri";
 
         /**
-         * The time the event starts
+         * The time the event starts in UTC millis since epoch. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String DTSTART = "dtstart";
 
         /**
-         * The time the event ends
+         * The time the event ends in UTC millis since epoch. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String DTEND = "dtend";
 
         /**
-         * The duration of the event
+         * The duration of the event in RFC2445 format. Column name.
          * <P>Type: TEXT (duration in RFC2445 format)</P>
          */
         public static final String DURATION = "duration";
 
         /**
-         * The timezone for the event.
-         * <P>Type: TEXT
+         * The timezone for the event. Column name.
+         * <P>Type: TEXT</P>
          */
         public static final String EVENT_TIMEZONE = "eventTimezone";
 
         /**
-         * The timezone for the event, allDay events will have a local tz instead of UTC
-         * <P>Type: TEXT
+         * The timezone for the end time of the event. Column name.
+         * <P>Type: TEXT</P>
          */
         public static final String EVENT_END_TIMEZONE = "eventEndTimezone";
 
         /**
-         * Whether the event lasts all day or not
+         * Is the event all day (time zone independent). Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String ALL_DAY = "allDay";
 
         /**
          * Defines how the event shows up for others when the calendar is
-         * shared.
-         * <P>Type: INTEGER</P>
+         * shared. Column name.
+         * <P>Type: INTEGER (One of {@link #ACCESS_DEFAULT}, ...)</P>
          */
         public static final String ACCESS_LEVEL = "accessLevel";
 
@@ -655,20 +775,20 @@
          */
         public static final int ACCESS_CONFIDENTIAL = 1;
         /**
-         * Private assumes the event appears as a free/busy slot with no
-         * details.
+         * Private shares the event as a free/busy slot with no details.
          */
         public static final int ACCESS_PRIVATE = 2;
         /**
-         * Public assumes the contents are visible to anyone with access to the
+         * Public makes the contents visible to anyone with access to the
          * calendar.
          */
         public static final int ACCESS_PUBLIC = 3;
 
         /**
          * If this event counts as busy time or is still free time that can be
-         * scheduled over.
-         * <P>Type: INTEGER</P>
+         * scheduled over. Column name.
+         * <P>Type: INTEGER (One of {@link #AVAILABILITY_BUSY},
+         * {@link #AVAILABILITY_FREE})</P>
          */
         public static final String AVAILABILITY = "availability";
 
@@ -684,45 +804,44 @@
         public static final int AVAILABILITY_FREE = 1;
 
         /**
-         * Whether the event has an alarm or not
+         * Whether the event has an alarm or not. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String HAS_ALARM = "hasAlarm";
 
         /**
-         * Whether the event has extended properties or not
+         * Whether the event has extended properties or not. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String HAS_EXTENDED_PROPERTIES = "hasExtendedProperties";
 
         /**
-         * The recurrence rule for the event.
-         * than one.
+         * The recurrence rule for the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String RRULE = "rrule";
 
         /**
-         * The recurrence dates for the event.
+         * The recurrence dates for the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String RDATE = "rdate";
 
         /**
-         * The recurrence exception rule for the event.
+         * The recurrence exception rule for the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String EXRULE = "exrule";
 
         /**
-         * The recurrence exception dates for the event.
+         * The recurrence exception dates for the event. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String EXDATE = "exdate";
 
         /**
-         * The _id of the original recurring event for which this event is an
-         * exception.
+         * The {@link Events#_ID} of the original recurring event for which this
+         * event is an exception. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String ORIGINAL_ID = "original_id";
@@ -730,27 +849,28 @@
         /**
          * The _sync_id of the original recurring event for which this event is
          * an exception. The provider should keep the original_id in sync when
-         * this is updated.
+         * this is updated. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String ORIGINAL_SYNC_ID = "original_sync_id";
 
         /**
          * The original instance time of the recurring event for which this
-         * event is an exception.
+         * event is an exception. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String ORIGINAL_INSTANCE_TIME = "originalInstanceTime";
 
         /**
          * The allDay status (true or false) of the original recurring event
-         * for which this event is an exception.
+         * for which this event is an exception. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String ORIGINAL_ALL_DAY = "originalAllDay";
 
         /**
-         * The last date this event repeats on, or NULL if it never ends
+         * The last date this event repeats on, or NULL if it never ends. Column
+         * name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String LAST_DATE = "lastDate";
@@ -758,61 +878,66 @@
         /**
          * Whether the event has attendee information.  True if the event
          * has full attendee data, false if the event has information about
-         * self only.
+         * self only. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String HAS_ATTENDEE_DATA = "hasAttendeeData";
 
         /**
-         * Whether guests can modify the event.
+         * Whether guests can modify the event. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String GUESTS_CAN_MODIFY = "guestsCanModify";
 
         /**
-         * Whether guests can invite other guests.
+         * Whether guests can invite other guests. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String GUESTS_CAN_INVITE_OTHERS = "guestsCanInviteOthers";
 
         /**
-         * Whether guests can see the list of attendees.
+         * Whether guests can see the list of attendees. Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String GUESTS_CAN_SEE_GUESTS = "guestsCanSeeGuests";
 
         /**
-         * Email of the organizer (owner) of the event.
+         * Email of the organizer (owner) of the event. Column name.
          * <P>Type: STRING</P>
          */
         public static final String ORGANIZER = "organizer";
 
         /**
-         * Whether the user can invite others to the event.
-         * The GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary guest,
-         * while CAN_INVITE_OTHERS indicates if the user can invite others (either through
-         * GUESTS_CAN_INVITE_OTHERS or because the user has modify access to the event).
+         * Whether the user can invite others to the event. The
+         * GUESTS_CAN_INVITE_OTHERS is a setting that applies to an arbitrary
+         * guest, while CAN_INVITE_OTHERS indicates if the user can invite
+         * others (either through GUESTS_CAN_INVITE_OTHERS or because the user
+         * has modify access to the event). Column name.
          * <P>Type: INTEGER (boolean, readonly)</P>
          */
         public static final String CAN_INVITE_OTHERS = "canInviteOthers";
 
         /**
          * The owner account for this calendar, based on the calendar (foreign
-         * key into the calendars table).
+         * key into the calendars table). Column name.
          * <P>Type: String</P>
          */
         public static final String OWNER_ACCOUNT = "ownerAccount";
 
         /**
-         * Whether the row has been deleted.  A deleted row should be ignored.
+         * Whether the row has been deleted. A deleted row should be ignored.
+         * Column name.
          * <P>Type: INTEGER (boolean)</P>
          */
         public static final String DELETED = "deleted";
     }
 
     /**
-     * Contains one entry per calendar event. Recurring events show up as a
-     * single entry.
+     * Class that represents an Event Entity. There is one entry per event.
+     * Recurring events show up as a single entry. This is a helper class to
+     * make batch operations easier. A {@link ContentResolver} or
+     * {@link ContentProviderClient} is required as the helper does additional
+     * queries to add reminders and attendees to each entry.
      */
     public static final class EventsEntity implements BaseColumns, SyncColumns, EventsColumns {
         /**
@@ -821,10 +946,26 @@
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
                 "/event_entities");
 
+        /**
+         * Creates a new iterator for events
+         *
+         * @param cursor An event query
+         * @param resolver For performing additional queries
+         * @return an EntityIterator containing one entity per event in the
+         *         cursor
+         */
         public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) {
             return new EntityIteratorImpl(cursor, resolver);
         }
 
+        /**
+         * Creates a new iterator for events
+         *
+         * @param cursor An event query
+         * @param provider For performing additional queries
+         * @return an EntityIterator containing one entity per event in the
+         *         cursor
+         */
         public static EntityIterator newEntityIterator(Cursor cursor,
                 ContentProviderClient provider) {
             return new EntityIteratorImpl(cursor, provider);
@@ -919,9 +1060,11 @@
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, ORGANIZER);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_ID);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_DATA);
+                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, SYNC_DATA7);
                 DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, DIRTY);
+                DatabaseUtils.cursorLongToContentValuesIfPresent(cursor, cv, LAST_SYNCED);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, _SYNC_VERSION);
-                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, EventsColumns.DELETED);
+                DatabaseUtils.cursorIntToContentValuesIfPresent(cursor, cv, DELETED);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC1);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC2);
                 DatabaseUtils.cursorStringToContentValuesIfPresent(cursor, cv, CAL_SYNC3);
@@ -1018,53 +1161,62 @@
     }
 
     /**
-     * Contains one entry per calendar event. Recurring events show up as a single entry.
+     * Fields and helpers for interacting with Events.
      */
     public static final class Events implements BaseColumns, SyncColumns, EventsColumns {
 
-        private static final String[] FETCH_ENTRY_COLUMNS =
-                new String[] { Events.ACCOUNT_NAME, Events._SYNC_ID };
-
-        private static final String[] ATTENDEES_COLUMNS =
-                new String[] { AttendeesColumns.ATTENDEE_NAME,
-                               AttendeesColumns.ATTENDEE_EMAIL,
-                               AttendeesColumns.ATTENDEE_RELATIONSHIP,
-                               AttendeesColumns.ATTENDEE_TYPE,
-                               AttendeesColumns.ATTENDEE_STATUS };
-
+        /**
+         * Queries all events with the given projection. This is a blocking call
+         * and should not be done on the UI thread.
+         *
+         * @param cr The content resolver to use for the query
+         * @param projection The columns to return
+         * @return A Cursor containing all events in the db
+         */
         public static final Cursor query(ContentResolver cr, String[] projection) {
             return cr.query(CONTENT_URI, projection, null, null, DEFAULT_SORT_ORDER);
         }
 
-        public static final Cursor query(ContentResolver cr, String[] projection,
-                                       String where, String orderBy) {
-            return cr.query(CONTENT_URI, projection, where,
-                                         null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-        }
-
-        private static String extractValue(ICalendar.Component component,
-                                           String propertyName) {
-            ICalendar.Property property =
-                    component.getFirstProperty(propertyName);
-            if (property != null) {
-                return property.getValue();
-            }
-            return null;
+        /**
+         * Queries events using the given projection, selection filter, and
+         * ordering. This is a blocking call and should not be done on the UI
+         * thread. For selection and selectionArgs usage see
+         * {@link ContentResolver#query(Uri, String[], String, String[], String)}
+         *
+         * @param cr The content resolver to use for the query
+         * @param projection The columns to return
+         * @param selection Filter on the query as an SQL WHERE statement
+         * @param selectionArgs Args to replace any '?'s in the selection
+         * @param orderBy How to order the rows as an SQL ORDER BY statement
+         * @return A Cursor containing the matching events
+         */
+        public static final Cursor query(ContentResolver cr, String[] projection, String selection,
+                String[] selectionArgs, String orderBy) {
+            return cr.query(CONTENT_URI, projection, selection, null,
+                    orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
         }
 
         /**
-         * The content:// style URL for this table
+         * The content:// style URL for interacting with events. Appending an
+         * event id using {@link ContentUris#withAppendedId(Uri, long)} will
+         * specify a single event.
          */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI =
                 Uri.parse("content://" + AUTHORITY + "/events");
 
-        public static final Uri DELETED_CONTENT_URI =
-                Uri.parse("content://" + AUTHORITY + "/deleted_events");
+        /**
+         * The content:// style URI for recurring event exceptions.  Insertions require an
+         * appended event ID.  Deletion of exceptions requires both the original event ID and
+         * the exception event ID (see {@link Uri.Builder#appendPath}).
+         */
+        public static final Uri EXCEPTION_CONTENT_URI =
+                Uri.parse("content://" + AUTHORITY + "/exception");
 
         /**
          * The default sort order for this table
          */
-        public static final String DEFAULT_SORT_ORDER = "";
+        private static final String DEFAULT_SORT_ORDER = "";
 
         /**
          * These are columns that should only ever be updated by the provider,
@@ -1073,7 +1225,14 @@
          */
         public static String[] PROVIDER_WRITABLE_COLUMNS = new String[] {
                 ACCOUNT_NAME,
-                ACCOUNT_TYPE
+                ACCOUNT_TYPE,
+                CAL_SYNC1,
+                CAL_SYNC2,
+                CAL_SYNC3,
+                CAL_SYNC4,
+                CAL_SYNC5,
+                CAL_SYNC6,
+                CAN_PARTIALLY_UPDATE,
         };
 
         /**
@@ -1091,13 +1250,29 @@
     }
 
     /**
-     * Contains one entry per calendar event instance. Recurring events show up every time
-     * they occur.
+     * Fields and helpers for interacting with Instances. An instance is a
+     * single occurrence of an event including time zone specific start and end
+     * days and minutes.
      */
     public static final class Instances implements BaseColumns, EventsColumns, CalendarsColumns {
 
         private static final String WHERE_CALENDARS_SELECTED = Calendars.VISIBLE + "=1";
 
+        /**
+         * Performs a query to return all visible instances in the given range.
+         * This is a blocking function and should not be done on the UI thread.
+         * This will cause an expansion of recurring events to fill this time
+         * range if they are not already expanded and will slow down for larger
+         * time ranges with many recurring events.
+         *
+         * @param cr The ContentResolver to use for the query
+         * @param projection The columns to return
+         * @param begin The start of the time range to query in UTC millis since
+         *            epoch
+         * @param end The end of the time range to query in UTC millis since
+         *            epoch
+         * @return A Cursor containing all instances in the given range
+         */
         public static final Cursor query(ContentResolver cr, String[] projection,
                                          long begin, long end) {
             Uri.Builder builder = CONTENT_URI.buildUpon();
@@ -1107,111 +1282,184 @@
                          null, DEFAULT_SORT_ORDER);
         }
 
+        /**
+         * Performs a query to return all visible instances in the given range
+         * that match the given query. This is a blocking function and should
+         * not be done on the UI thread. This will cause an expansion of
+         * recurring events to fill this time range if they are not already
+         * expanded and will slow down for larger time ranges with many
+         * recurring events.
+         *
+         * @param cr The ContentResolver to use for the query
+         * @param projection The columns to return
+         * @param begin The start of the time range to query in UTC millis since
+         *            epoch
+         * @param end The end of the time range to query in UTC millis since
+         *            epoch
+         * @param searchQuery A string of space separated search terms. Segments
+         *            enclosed by double quotes will be treated as a single
+         *            term.
+         * @return A Cursor of instances matching the search terms in the given
+         *         time range
+         */
         public static final Cursor query(ContentResolver cr, String[] projection,
                                          long begin, long end, String searchQuery) {
             Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
             ContentUris.appendId(builder, begin);
             ContentUris.appendId(builder, end);
-            return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED,
-                         new String[] { searchQuery }, DEFAULT_SORT_ORDER);
+            builder = builder.appendPath(searchQuery);
+            return cr.query(builder.build(), projection, WHERE_CALENDARS_SELECTED, null,
+                    DEFAULT_SORT_ORDER);
         }
 
-        public static final Cursor query(ContentResolver cr, String[] projection,
-                                         long begin, long end, String where, String orderBy) {
+        /**
+         * Performs a query to return all visible instances in the given range
+         * that match the given selection. This is a blocking function and
+         * should not be done on the UI thread. This will cause an expansion of
+         * recurring events to fill this time range if they are not already
+         * expanded and will slow down for larger time ranges with many
+         * recurring events.
+         *
+         * @param cr The ContentResolver to use for the query
+         * @param projection The columns to return
+         * @param begin The start of the time range to query in UTC millis since
+         *            epoch
+         * @param end The end of the time range to query in UTC millis since
+         *            epoch
+         * @param selection Filter on the query as an SQL WHERE statement
+         * @param selectionArgs Args to replace any '?'s in the selection
+         * @param orderBy How to order the rows as an SQL ORDER BY statement
+         * @return A Cursor of instances matching the selection
+         */
+        public static final Cursor query(ContentResolver cr, String[] projection, long begin,
+                long end, String selection, String[] selectionArgs, String orderBy) {
             Uri.Builder builder = CONTENT_URI.buildUpon();
             ContentUris.appendId(builder, begin);
             ContentUris.appendId(builder, end);
-            if (TextUtils.isEmpty(where)) {
-                where = WHERE_CALENDARS_SELECTED;
+            if (TextUtils.isEmpty(selection)) {
+                selection = WHERE_CALENDARS_SELECTED;
             } else {
-                where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED;
+                selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED;
             }
-            return cr.query(builder.build(), projection, where,
-                         null, orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
-        }
-
-        public static final Cursor query(ContentResolver cr, String[] projection, long begin,
-                long end, String searchQuery, String where, String orderBy) {
-            Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
-            ContentUris.appendId(builder, begin);
-            ContentUris.appendId(builder, end);
-            builder = builder.appendPath(searchQuery);
-            if (TextUtils.isEmpty(where)) {
-                where = WHERE_CALENDARS_SELECTED;
-            } else {
-                where = "(" + where + ") AND " + WHERE_CALENDARS_SELECTED;
-            }
-            return cr.query(builder.build(), projection, where, null,
+            return cr.query(builder.build(), projection, selection, selectionArgs,
                     orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
         }
 
         /**
-         * The content:// style URL for this table
+         * Performs a query to return all visible instances in the given range
+         * that match the given selection. This is a blocking function and
+         * should not be done on the UI thread. This will cause an expansion of
+         * recurring events to fill this time range if they are not already
+         * expanded and will slow down for larger time ranges with many
+         * recurring events.
+         *
+         * @param cr The ContentResolver to use for the query
+         * @param projection The columns to return
+         * @param begin The start of the time range to query in UTC millis since
+         *            epoch
+         * @param end The end of the time range to query in UTC millis since
+         *            epoch
+         * @param searchQuery A string of space separated search terms. Segments
+         *            enclosed by double quotes will be treated as a single
+         *            term.
+         * @param selection Filter on the query as an SQL WHERE statement
+         * @param selectionArgs Args to replace any '?'s in the selection
+         * @param orderBy How to order the rows as an SQL ORDER BY statement
+         * @return A Cursor of instances matching the selection
          */
+        public static final Cursor query(ContentResolver cr, String[] projection, long begin,
+                long end, String searchQuery, String selection, String[] selectionArgs,
+                String orderBy) {
+            Uri.Builder builder = CONTENT_SEARCH_URI.buildUpon();
+            ContentUris.appendId(builder, begin);
+            ContentUris.appendId(builder, end);
+            builder = builder.appendPath(searchQuery);
+            if (TextUtils.isEmpty(selection)) {
+                selection = WHERE_CALENDARS_SELECTED;
+            } else {
+                selection = "(" + selection + ") AND " + WHERE_CALENDARS_SELECTED;
+            }
+            return cr.query(builder.build(), projection, selection, selectionArgs,
+                    orderBy == null ? DEFAULT_SORT_ORDER : orderBy);
+        }
+
+        /**
+         * The content:// style URL for querying an instance range. The begin
+         * and end of the range to query should be added as path segments if
+         * this is used directly.
+         */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
                 "/instances/when");
+        /**
+         * The content:// style URL for querying an instance range by Julian
+         * Day. The start and end day should be added as path segments if this
+         * is used directly.
+         */
         public static final Uri CONTENT_BY_DAY_URI =
             Uri.parse("content://" + AUTHORITY + "/instances/whenbyday");
+        /**
+         * The content:// style URL for querying an instance range with a search
+         * term. The begin, end, and search string should be appended as path
+         * segments if this is used directly.
+         */
         public static final Uri CONTENT_SEARCH_URI = Uri.parse("content://" + AUTHORITY +
                 "/instances/search");
+        /**
+         * The content:// style URL for querying an instance range with a search
+         * term. The start day, end day, and search string should be appended as
+         * path segments if this is used directly.
+         */
         public static final Uri CONTENT_SEARCH_BY_DAY_URI =
             Uri.parse("content://" + AUTHORITY + "/instances/searchbyday");
 
         /**
          * The default sort order for this table.
          */
-        public static final String DEFAULT_SORT_ORDER = "begin ASC";
+        private static final String DEFAULT_SORT_ORDER = "begin ASC";
 
         /**
-         * The sort order is: events with an earlier start time occur
-         * first and if the start times are the same, then events with
-         * a later end time occur first. The later end time is ordered
-         * first so that long-running events in the calendar views appear
-         * first.  If the start and end times of two events are
-         * the same then we sort alphabetically on the title.  This isn't
-         * required for correctness, it just adds a nice touch.
-         */
-        public static final String SORT_CALENDAR_VIEW = "begin ASC, end DESC, title ASC";
-        /**
-         * The beginning time of the instance, in UTC milliseconds
+         * The beginning time of the instance, in UTC milliseconds. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String BEGIN = "begin";
 
         /**
-         * The ending time of the instance, in UTC milliseconds
+         * The ending time of the instance, in UTC milliseconds. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String END = "end";
 
         /**
-         * The event for this instance
+         * The _id of the event for this instance. Column name.
          * <P>Type: INTEGER (long, foreign key to the Events table)</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
-         * The Julian start day of the instance, relative to the local timezone
+         * The Julian start day of the instance, relative to the local time
+         * zone. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String START_DAY = "startDay";
 
         /**
-         * The Julian end day of the instance, relative to the local timezone
+         * The Julian end day of the instance, relative to the local time
+         * zone. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String END_DAY = "endDay";
 
         /**
          * The start minute of the instance measured from midnight in the
-         * local timezone.
+         * local time zone. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String START_MINUTE = "startMinute";
 
         /**
          * The end minute of the instance measured from midnight in the
-         * local timezone.
+         * local time zone. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String END_MINUTE = "endMinute";
@@ -1219,14 +1467,12 @@
 
     /**
      * CalendarCache stores some settings for calendar including the current
-     * time zone for the app. These settings are stored using a key/value
+     * time zone for the instaces. These settings are stored using a key/value
      * scheme.
      */
-    public interface CalendarCacheColumns {
+    private interface CalendarCacheColumns {
         /**
-         * The key for the setting. Keys are defined in CalendarChache in the
-         * Calendar provider.
-         * TODO Add keys to this file
+         * The key for the setting. Keys are defined in {@link CalendarCache}.
          */
         public static final String KEY = "key";
 
@@ -1296,7 +1542,7 @@
      * the Instances table and these are all stored in the first (and only)
      * row of the CalendarMetaData table.
      */
-    public interface CalendarMetaDataColumns {
+    private interface CalendarMetaDataColumns {
         /**
          * The local timezone that was used for precomputing the fields
          * in the Instances table.
@@ -1330,34 +1576,51 @@
         public static final String MAX_EVENTDAYS = "maxEventDays";
     }
 
+    /**
+     * @hide
+     */
     public static final class CalendarMetaData implements CalendarMetaDataColumns, BaseColumns {
     }
 
-    public interface EventDaysColumns {
+    private interface EventDaysColumns {
         /**
-         * The Julian starting day number.
+         * The Julian starting day number. Column name.
          * <P>Type: INTEGER (int)</P>
          */
         public static final String STARTDAY = "startDay";
+        /**
+         * The Julian ending day number. Column name.
+         * <P>Type: INTEGER (int)</P>
+         */
         public static final String ENDDAY = "endDay";
 
     }
 
+    /**
+     * Fields and helpers for querying for a list of days that contain events.
+     */
     public static final class EventDays implements EventDaysColumns {
-        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
-                "/instances/groupbyday");
-
-        public static final String[] PROJECTION = { STARTDAY, ENDDAY };
-        public static final String SELECTION = "selected=1";
+        private static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY
+                + "/instances/groupbyday");
 
         /**
-         * Retrieves the days with events for the Julian days starting at "startDay"
-         * for "numDays".
+         * The projection used by the EventDays query.
+         */
+        public static final String[] PROJECTION = { STARTDAY, ENDDAY };
+        private static final String SELECTION = "selected=1";
+
+        /**
+         * Retrieves the days with events for the Julian days starting at
+         * "startDay" for "numDays". It returns a cursor containing startday and
+         * endday representing the max range of days for all events beginning on
+         * each startday.This is a blocking function and should not be done on
+         * the UI thread.
          *
          * @param cr the ContentResolver
          * @param startDay the first Julian day in the range
          * @param numDays the number of days to load (must be at least 1)
-         * @return a database cursor
+         * @return a database cursor containing a list of start and end days for
+         *         events
          */
         public static final Cursor query(ContentResolver cr, int startDay, int numDays) {
             if (numDays < 1) {
@@ -1372,9 +1635,9 @@
         }
     }
 
-    public interface RemindersColumns {
+    private interface RemindersColumns {
         /**
-         * The event the reminder belongs to
+         * The event the reminder belongs to. Column name.
          * <P>Type: INTEGER (foreign key to the Events table)</P>
          */
         public static final String EVENT_ID = "event_id";
@@ -1382,17 +1645,24 @@
         /**
          * The minutes prior to the event that the alarm should ring.  -1
          * specifies that we should use the default value for the system.
+         * Column name.
          * <P>Type: INTEGER</P>
          */
         public static final String MINUTES = "minutes";
 
+        /**
+         * Passing this as a minutes value will use the default reminder
+         * minutes.
+         */
         public static final int MINUTES_DEFAULT = -1;
 
         /**
-         * The alarm method, as set on the server.  DEFAULT, ALERT, EMAIL, and
-         * SMS are possible values; the device will only process DEFAULT and
-         * ALERT reminders (the other types are simply stored so we can send the
-         * same reminder info back to the server when we make changes).
+         * The alarm method, as set on the server. {@link #METHOD_DEFAULT},
+         * {@link #METHOD_ALERT}, {@link #METHOD_EMAIL}, and {@link #METHOD_SMS}
+         * are possible values; the device will only process
+         * {@link #METHOD_DEFAULT} and {@link #METHOD_ALERT} reminders (the
+         * other types are simply stored so we can send the same reminder info
+         * back to the server when we make changes).
          */
         public static final String METHOD = "method";
 
@@ -1402,61 +1672,85 @@
         public static final int METHOD_SMS = 3;
     }
 
+    /**
+     * Fields and helpers for accessing reminders for an event.
+     */
     public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns {
-        public static final String TABLE_NAME = "Reminders";
+        private static final String REMINDERS_WHERE = Calendar.Reminders.EVENT_ID + "=?";
+        /**
+         * The projection used by the reminders query.
+         */
+        public static final String[] PROJECTION = new String[] {
+                _ID, MINUTES, METHOD,};
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/reminders");
+
+        /**
+         * Queries all reminders associated with the given event. This is a
+         * blocking call and should not be done on the UI thread.
+         *
+         * @param cr The content resolver to use for the query
+         * @param eventId The id of the event to retrieve reminders for
+         * @return A Cursor containing all reminders for the event
+         */
+        public static final Cursor query(ContentResolver cr, long eventId) {
+            String[] remArgs = {Long.toString(eventId)};
+            return cr.query(CONTENT_URI, PROJECTION, REMINDERS_WHERE, remArgs /* selection args */,
+                    null /* sort order */);
+        }
     }
 
-    public interface CalendarAlertsColumns {
+    private interface CalendarAlertsColumns {
         /**
-         * The event that the alert belongs to
+         * The event that the alert belongs to. Column name.
          * <P>Type: INTEGER (foreign key to the Events table)</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
-         * The start time of the event, in UTC
+         * The start time of the event, in UTC. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String BEGIN = "begin";
 
         /**
-         * The end time of the event, in UTC
+         * The end time of the event, in UTC. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String END = "end";
 
         /**
-         * The alarm time of the event, in UTC
+         * The alarm time of the event, in UTC. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String ALARM_TIME = "alarmTime";
 
         /**
          * The creation time of this database entry, in UTC.
-         * (Useful for debugging missed reminders.)
+         * Useful for debugging missed reminders. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String CREATION_TIME = "creationTime";
 
         /**
          * The time that the alarm broadcast was received by the Calendar app,
-         * in UTC. (Useful for debugging missed reminders.)
+         * in UTC. Useful for debugging missed reminders. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String RECEIVED_TIME = "receivedTime";
 
         /**
          * The time that the notification was created by the Calendar app,
-         * in UTC. (Useful for debugging missed reminders.)
+         * in UTC. Useful for debugging missed reminders. Column name.
          * <P>Type: INTEGER (long; millis since epoch)</P>
          */
         public static final String NOTIFY_TIME = "notifyTime";
 
         /**
-         * The state of this alert.  It starts out as SCHEDULED, then when
-         * the alarm goes off, it changes to FIRED, and then when the user
-         * dismisses the alarm it changes to DISMISSED.
+         * The state of this alert. It starts out as {@link #SCHEDULED}, then
+         * when the alarm goes off, it changes to {@link #FIRED}, and then when
+         * the user dismisses the alarm it changes to {@link #DISMISSED}. Column
+         * name.
          * <P>Type: INTEGER</P>
          */
         public static final String STATE = "state";
@@ -1466,21 +1760,33 @@
         public static final int DISMISSED = 2;
 
         /**
-         * The number of minutes that this alarm precedes the start time
-         * <P>Type: INTEGER </P>
+         * The number of minutes that this alarm precedes the start time. Column
+         * name.
+         * <P>Type: INTEGER</P>
          */
         public static final String MINUTES = "minutes";
 
         /**
-         * The default sort order for this table
+         * The default sort order for this alerts queries
          */
         public static final String DEFAULT_SORT_ORDER = "begin ASC,title ASC";
     }
 
+    /**
+     * Fields and helpers for accessing calendar alerts information. These
+     * fields are for tracking which alerts have been fired.
+     */
     public static final class CalendarAlerts implements BaseColumns,
             CalendarAlertsColumns, EventsColumns, CalendarsColumns {
 
+        /**
+         * @hide
+         */
         public static final String TABLE_NAME = "CalendarAlerts";
+        /**
+         * The Uri for querying calendar alert information
+         */
+        @SuppressWarnings("hiding")
         public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY +
                 "/calendar_alerts");
 
@@ -1507,6 +1813,11 @@
 
         private static final boolean DEBUG = true;
 
+        /**
+         * Helper for inserting an alarm time associated with an event
+         *
+         * @hide
+         */
         public static final Uri insert(ContentResolver cr, long eventId,
                 long begin, long end, long alarmTime, int minutes) {
             ContentValues values = new ContentValues();
@@ -1523,6 +1834,19 @@
             return cr.insert(CONTENT_URI, values);
         }
 
+        /**
+         * Queries alerts info using the given projection, selection filter, and
+         * ordering. This is a blocking call and should not be done on the UI
+         * thread. For selection and selectionArgs usage see
+         * {@link ContentResolver#query(Uri, String[], String, String[], String)}
+         *
+         * @param cr The content resolver to use for the query
+         * @param projection The columns to return
+         * @param selection Filter on the query as an SQL WHERE statement
+         * @param selectionArgs Args to replace any '?'s in the selection
+         * @param sortOrder How to order the rows as an SQL ORDER BY statement
+         * @return A Cursor containing the matching alerts
+         */
         public static final Cursor query(ContentResolver cr, String[] projection,
                 String selection, String[] selectionArgs, String sortOrder) {
             return cr.query(CONTENT_URI, projection, selection, selectionArgs,
@@ -1531,12 +1855,13 @@
 
         /**
          * Finds the next alarm after (or equal to) the given time and returns
-         * the time of that alarm or -1 if no such alarm exists.
+         * the time of that alarm or -1 if no such alarm exists. This is a
+         * blocking call and should not be done on the UI thread.
          *
          * @param cr the ContentResolver
          * @param millis the time in UTC milliseconds
          * @return the next alarm time greater than or equal to "millis", or -1
-         *     if no such alarm exists.
+         *         if no such alarm exists.
          */
         public static final long findNextAlarmTime(ContentResolver cr, long millis) {
             String selection = ALARM_TIME + ">=" + millis;
@@ -1619,6 +1944,17 @@
             }
         }
 
+        /**
+         * Schedules an alarm intent with the system AlarmManager that will
+         * cause the Calendar provider to recheck alarms. This is used to wake
+         * the Calendar alarm handler when an alarm is expected or to do a
+         * periodic refresh of alarm data.
+         *
+         * @param context A context for referencing system resources
+         * @param manager The AlarmManager to use or null
+         * @param alarmTime The time to fire the intent in UTC millis since
+         *            epoch
+         */
         public static void scheduleAlarm(Context context, AlarmManager manager, long alarmTime) {
             if (DEBUG) {
                 Time time = new Time();
@@ -1678,27 +2014,32 @@
         }
     }
 
-    public interface ExtendedPropertiesColumns {
+    private interface ExtendedPropertiesColumns {
         /**
-         * The event the extended property belongs to
+         * The event the extended property belongs to. Column name.
          * <P>Type: INTEGER (foreign key to the Events table)</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
          * The name of the extended property.  This is a uri of the form
-         * {scheme}#{local-name} convention.
+         * {scheme}#{local-name} convention. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String NAME = "name";
 
         /**
-         * The value of the extended property.
+         * The value of the extended property. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String VALUE = "value";
     }
 
+    /**
+     * Fields for accessing the Extended Properties. This is a generic set of
+     * name/value pairs for use by sync adapters or apps to add extra
+     * information to events.
+     */
    public static final class ExtendedProperties implements BaseColumns,
             ExtendedPropertiesColumns, EventsColumns {
         public static final Uri CONTENT_URI =
@@ -1719,7 +2060,7 @@
          */
         private SyncState() {}
 
-        public static final String CONTENT_DIRECTORY =
+        private static final String CONTENT_DIRECTORY =
                 SyncStateContract.Constants.CONTENT_DIRECTORY;
 
         /**
@@ -1732,39 +2073,43 @@
     /**
      * Columns from the EventsRawTimes table
      */
-    public interface EventsRawTimesColumns {
+    private interface EventsRawTimesColumns {
         /**
-         * The corresponding event id
+         * The corresponding event id. Column name.
          * <P>Type: INTEGER (long)</P>
          */
         public static final String EVENT_ID = "event_id";
 
         /**
-         * The RFC2445 compliant time the event starts
+         * The RFC2445 compliant time the event starts. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String DTSTART_2445 = "dtstart2445";
 
         /**
-         * The RFC2445 compliant time the event ends
+         * The RFC2445 compliant time the event ends. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String DTEND_2445 = "dtend2445";
 
         /**
-         * The RFC2445 compliant original instance time of the recurring event for which this
-         * event is an exception.
+         * The RFC2445 compliant original instance time of the recurring event
+         * for which this event is an exception. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String ORIGINAL_INSTANCE_TIME_2445 = "originalInstanceTime2445";
 
         /**
-         * The RFC2445 compliant last date this event repeats on, or NULL if it never ends
+         * The RFC2445 compliant last date this event repeats on, or NULL if it
+         * never ends. Column name.
          * <P>Type: TEXT</P>
          */
         public static final String LAST_DATE_2445 = "lastDate2445";
     }
 
+    /**
+     * @hide
+     */
     public static final class EventsRawTimes implements BaseColumns, EventsRawTimesColumns {
     }
 }
diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java
index 22b4c76..f5c852d 100644
--- a/core/java/android/provider/ContactsContract.java
+++ b/core/java/android/provider/ContactsContract.java
@@ -122,6 +122,17 @@
     public static final String CALLER_IS_SYNCADAPTER = "caller_is_syncadapter";
 
     /**
+     * An optional URI parameter for selection queries that instructs the
+     * provider to include the user's personal profile contact entry (if any)
+     * in the contact results.  If present, the user's profile will always be
+     * the first entry returned.  The default value is false.
+     *
+     * Specifying this parameter will result in a security error if the calling
+     * application does not have android.permission.READ_PROFILE permission.
+     */
+    public static final String INCLUDE_PROFILE = "include_profile";
+
+    /**
      * A query parameter key used to specify the package that is requesting a query.
      * This is used for restricting data based on package name.
      *
@@ -763,12 +774,18 @@
         public static final String PHOTO_THUMBNAIL_URI = "photo_thumb_uri";
 
         /**
-         * Lookup value that reflects the {@link Groups#GROUP_VISIBLE} state of
-         * any {@link CommonDataKinds.GroupMembership} for this contact.
+         * Flag that reflects the {@link Groups#GROUP_VISIBLE} state of any
+         * {@link CommonDataKinds.GroupMembership} for this contact.
          */
         public static final String IN_VISIBLE_GROUP = "in_visible_group";
 
         /**
+         * Flag that reflects whether this contact represents the user's
+         * personal profile entry.
+         */
+        public static final String IS_USER_PROFILE = "is_user_profile";
+
+        /**
          * An indicator of whether this contact has at least one phone number. "1" if there is
          * at least one phone number, "0" otherwise.
          * <P>Type: INTEGER</P>
@@ -1285,7 +1302,7 @@
          * Base {@link Uri} for referencing multiple {@link Contacts} entry,
          * created by appending {@link #LOOKUP_KEY} using
          * {@link Uri#withAppendedPath(Uri, String)}. The lookup keys have to be
-         * encoded and joined with the colon (":") seperator. The resulting string
+         * encoded and joined with the colon (":") separator. The resulting string
          * has to be encoded again. Provides
          * {@link OpenableColumns} columns when queried, or returns the
          * referenced contact formatted as a vCard when opened through
@@ -1738,6 +1755,88 @@
         }
     }
 
+    /**
+     * <p>
+     * Constants for the user's profile data, which is represented as a single contact on
+     * the device that represents the user.  The profile contact is not aggregated
+     * together automatically in the same way that normal contacts are; instead, each
+     * account on the device may contribute a single raw contact representing the user's
+     * personal profile data from that source.
+     * </p>
+     * <p>
+     * Access to the profile entry through these URIs (or incidental access to parts of
+     * the profile if retrieved directly via ID) requires additional permissions beyond
+     * the read/write contact permissions required by the provider.  Querying for profile
+     * data requires android.permission.READ_PROFILE permission, and inserting or
+     * updating profile data requires android.permission.WRITE_PROFILE permission.
+     * </p>
+     * <h3>Operations</h3>
+     * <dl>
+     * <dt><b>Insert</b></dt>
+     * <dd>The user's profile entry cannot be created explicitly (attempting to do so
+     * will throw an exception). When a raw contact is inserted into the profile, the
+     * provider will check for the existence of a profile on the device.  If one is
+     * found, the raw contact's {@link RawContacts#CONTACT_ID} column gets the _ID of
+     * the profile Contact. If no match is found, the profile Contact is created and
+     * its _ID is put into the {@link RawContacts#CONTACT_ID} column of the newly
+     * inserted raw contact.</dd>
+     * <dt><b>Update</b></dt>
+     * <dd>The profile Contact has the same update restrictions as Contacts in general,
+     * but requires the android.permission.WRITE_PROFILE permission.</dd>
+     * <dt><b>Delete</b></dt>
+     * <dd>The profile Contact cannot be explicitly deleted.  It will be removed
+     * automatically if all of its constituent raw contact entries are deleted.</dd>
+     * <dt><b>Query</b></dt>
+     * <dd>
+     * <ul>
+     * <li>The {@link #CONTENT_URI} for profiles behaves in much the same way as
+     * retrieving a contact by ID, except that it will only ever return the user's
+     * profile contact.
+     * </li>
+     * <li>
+     * The profile contact supports all of the same sub-paths as an individual contact
+     * does - the content of the profile contact can be retrieved as entities or
+     * data rows.  Similarly, specific raw contact entries can be retrieved by appending
+     * the desired raw contact ID within the profile.
+     * </li>
+     * </ul>
+     * </dd>
+     * </dl>
+     */
+    public static final class Profile implements BaseColumns, ContactsColumns,
+            ContactOptionsColumns, ContactNameColumns, ContactStatusColumns {
+        /**
+         * This utility class cannot be instantiated
+         */
+        private Profile() {
+        }
+
+        /**
+         * The content:// style URI for this table, which requests the contact entry
+         * representing the user's personal profile data.
+         */
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "profile");
+
+        /**
+         * {@link Uri} for referencing the user's profile {@link Contacts} entry,
+         * Provides {@link OpenableColumns} columns when queried, or returns the
+         * user's profile contact formatted as a vCard when opened through
+         * {@link ContentResolver#openAssetFileDescriptor(Uri, String)}.
+         */
+        public static final Uri CONTENT_VCARD_URI = Uri.withAppendedPath(CONTENT_URI,
+                "as_vcard");
+
+        /**
+         * {@link Uri} for referencing the raw contacts that make up the user's profile
+         * {@link Contacts} entry.  An individual raw contact entry within the profile
+         * can be addressed by appending the raw contact ID.  The entities or data within
+         * that specific raw contact can be requested by appending the entity or data
+         * path as well.
+         */
+        public static final Uri CONTENT_RAW_CONTACTS_URI = Uri.withAppendedPath(CONTENT_URI,
+                "raw_contacts");
+    }
+
     protected interface RawContactsColumns {
         /**
          * A reference to the {@link ContactsContract.Contacts#_ID} that this
@@ -1806,6 +1905,12 @@
          * <P>Type: INTEGER</P>
          */
         public static final String RAW_CONTACT_IS_READ_ONLY = "raw_contact_is_read_only";
+
+        /**
+         * Flag that reflects whether this raw contact belongs to the user's
+         * personal profile entry.
+         */
+        public static final String RAW_CONTACT_IS_USER_PROFILE = "raw_contact_is_user_profile";
     }
 
     /**
@@ -6156,6 +6261,91 @@
     }
 
     /**
+     * <p>
+     * API allowing applications to send usage information for each {@link Data} row to the
+     * Contacts Provider.
+     * </p>
+     * <p>
+     * With the feedback, Contacts Provider may return more contextually appropriate results for
+     * Data listing, typically supplied with
+     * {@link ContactsContract.Contacts#CONTENT_FILTER_URI},
+     * {@link ContactsContract.CommonDataKinds.Email#CONTENT_FILTER_URI},
+     * {@link ContactsContract.CommonDataKinds.Phone#CONTENT_FILTER_URI}, and users can benefit
+     * from better ranked (sorted) lists in applications that show auto-complete list.
+     * </p>
+     * <p>
+     * There is no guarantee for how this feedback is used, or even whether it is used at all.
+     * The ranking algorithm will make best efforts to use the feedback data, but the exact
+     * implementation, the storage data structures as well as the resulting sort order is device
+     * and version specific and can change over time.
+     * </p>
+     * <p>
+     * When updating usage information, users of this API need to use
+     * {@link ContentResolver#update(Uri, ContentValues, String, String[])} with a Uri constructed
+     * from {@link DataUsageFeedback#FEEDBACK_URI}. The Uri must contain one or more data id(s) as
+     * its last path. They also need to append a query parameter to the Uri, to specify the type of
+     * the communication, which enables the Contacts Provider to differentiate between kinds of
+     * interactions using the same contact data field (for example a phone number can be used to
+     * make phone calls or send SMS).
+     * </p>
+     * <p>
+     * Selection and selectionArgs are ignored and must be set to null. To get data ids,
+     * you may need to call {@link ContentResolver#query(Uri, String[], String, String[], String)}
+     * toward {@link Data#CONTENT_URI}.
+     * </p>
+     * <p>
+     * Example:
+     * <pre>
+     * Uri uri = DataUsageFeedback.UPDATE_URI.buildUpon()
+     *         .appendPath(TextUtils.join(",", dataIds))
+     *         .appendQueryParameter(DataUsageFeedback.METHOD, DataUsageFeedback.METHOD_CALL)
+     *         .build();
+     * resolver.update(uri, new ContentValues(), null, null);
+     * </pre>
+     * </p>
+     * @hide
+     */
+    public static final class DataUsageFeedback {
+
+        /**
+         * The content:// style URI for sending usage feedback.
+         * Must be used with {@link ContentResolver#update(Uri, ContentValues, String, String[])}.
+         */
+        public static final Uri FEEDBACK_URI =
+                Uri.withAppendedPath(Data.CONTENT_URI, "usagefeedback");
+
+        /**
+         * <p>
+         * Name for query parameter specifying the type of data usage.
+         * </p>
+         */
+        public static final String USAGE_TYPE = "method";
+
+        /**
+         * <p>
+         * Type of usage for voice interaction, which includes phone call, voice chat, and
+         * video chat.
+         * </p>
+         */
+        public static final String USAGE_TYPE_CALL = "call";
+
+        /**
+         * <p>
+         * Type of usage for text interaction involving longer messages, which includes email.
+         * </p>
+         */
+        public static final String USAGE_TYPE_LONG_TEXT = "long_text";
+
+        /**
+         * <p>
+         * Type of usage for text interaction involving shorter messages, which includes SMS,
+         * text chat with email addresses.
+         * </p>
+         */
+        public static final String USAGE_TYPE_SHORT_TEXT = "short_text";
+    }
+
+    /**
      * Helper methods to display QuickContact dialogs that allow users to pivot on
      * a specific {@link Contacts} entry.
      */
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 893947d..6ab7738 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2924,6 +2924,30 @@
         public static final String WIFI_WATCHDOG_PING_TIMEOUT_MS = "wifi_watchdog_ping_timeout_ms";
 
         /**
+         * Setting to turn off walled garden test on Wi-Fi. Feature is enabled by default and
+         * the setting needs to be set to 0 to disable it.
+         * @hide
+         */
+        public static final String WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED =
+                "wifi_watchdog_walled_garden_test_enabled";
+
+        /**
+         * The URL used for walled garden check upon a new conection. WifiWatchdogService
+         * fetches the URL and checks to see if {@link #WIFI_WATCHDOG_WALLED_GARDEN_PATTERN}
+         * is not part of the title string to notify the user on the presence of a walled garden.
+         * @hide
+         */
+        public static final String WIFI_WATCHDOG_WALLED_GARDEN_URL =
+                "wifi_watchdog_walled_garden_url";
+
+        /**
+         * The pattern string in the fetched URL used to detect a walled garden
+         * @hide
+         */
+        public static final String WIFI_WATCHDOG_WALLED_GARDEN_PATTERN =
+                "wifi_watchdog_walled_garden_pattern";
+
+        /**
          * The maximum number of times we will retry a connection to an access
          * point for which we have failed in acquiring an IP address from DHCP.
          * A value of N means that we will make N+1 connection attempts in all.
@@ -3771,6 +3795,19 @@
         public static final String DREAM_TIMEOUT =
                 "dream_timeout";
 
+        /** {@hide} */
+        public static final String NETSTATS_POLL_INTERVAL = "netstats_poll_interval";
+        /** {@hide} */
+        public static final String NETSTATS_PERSIST_THRESHOLD = "netstats_persist_threshold";
+        /** {@hide} */
+        public static final String NETSTATS_SUMMARY_BUCKET_DURATION = "netstats_summary_bucket_duration";
+        /** {@hide} */
+        public static final String NETSTATS_SUMMARY_MAX_HISTORY = "netstats_summary_max_history";
+        /** {@hide} */
+        public static final String NETSTATS_DETAIL_BUCKET_DURATION = "netstats_detail_bucket_duration";
+        /** {@hide} */
+        public static final String NETSTATS_DETAIL_MAX_HISTORY = "netstats_detail_max_history";
+
         /**
          * @hide
          */
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 91a72a5..6585e82 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -547,7 +547,7 @@
              * values:</p>
              *
              * <ul>
-             *   <li><em>pdus</em> - An Object[] od byte[]s containing the PDUs
+             *   <li><em>pdus</em> - An Object[] of byte[]s containing the PDUs
              *   that make up the message.</li>
              * </ul>
              *
@@ -591,6 +591,46 @@
                     "android.provider.Telephony.WAP_PUSH_RECEIVED";
 
             /**
+             * Broadcast Action: A new Cell Broadcast message has been received
+             * by the device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>pdus</em> - An Object[] of byte[]s containing the PDUs
+             *   that make up the message.</li>
+             * </ul>
+             *
+             * <p>The extra values can be extracted using
+             * {@link #getMessagesFromIntent(Intent)}.</p>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SMS_CB_RECEIVED_ACTION =
+                    "android.provider.Telephony.SMS_CB_RECEIVED";
+
+            /**
+             * Broadcast Action: A new Emergency Broadcast message has been received
+             * by the device. The intent will have the following extra
+             * values:</p>
+             *
+             * <ul>
+             *   <li><em>pdus</em> - An Object[] of byte[]s containing the PDUs
+             *   that make up the message.</li>
+             * </ul>
+             *
+             * <p>The extra values can be extracted using
+             * {@link #getMessagesFromIntent(Intent)}.</p>
+             *
+             * <p>If a BroadcastReceiver encounters an error while processing
+             * this intent it should set the result code appropriately.</p>
+             */
+            @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+            public static final String SMS_EMERGENCY_CB_RECEIVED_ACTION =
+                    "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED";
+
+            /**
              * Broadcast Action: The SIM storage for SMS messages is full.  If
              * space is not freed, messages targeted for the SIM (class 2) may
              * not be saved.
@@ -623,7 +663,7 @@
              * @param intent the intent to read from
              * @return an array of SmsMessages for the PDUs
              */
-            public static final SmsMessage[] getMessagesFromIntent(
+            public static SmsMessage[] getMessagesFromIntent(
                     Intent intent) {
                 Object[] messages = (Object[]) intent.getSerializableExtra("pdus");
                 byte[][] pduObjs = new byte[messages.length][];
diff --git a/core/java/android/speech/tts/AbstractSynthesisCallback.java b/core/java/android/speech/tts/AbstractSynthesisCallback.java
new file mode 100644
index 0000000..c7a4af0
--- /dev/null
+++ b/core/java/android/speech/tts/AbstractSynthesisCallback.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+/**
+ * Defines additional methods the synthesis callback must implement that
+ * are private to the TTS service implementation.
+ */
+abstract class AbstractSynthesisCallback implements SynthesisCallback {
+    /**
+     * Checks whether the synthesis request completed successfully.
+     */
+    abstract boolean isDone();
+
+    /**
+     * Aborts the speech request.
+     *
+     * Can be called from multiple threads.
+     */
+    abstract void stop();
+}
diff --git a/core/java/android/speech/tts/AudioMessageParams.java b/core/java/android/speech/tts/AudioMessageParams.java
new file mode 100644
index 0000000..68d8738
--- /dev/null
+++ b/core/java/android/speech/tts/AudioMessageParams.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+class AudioMessageParams extends MessageParams {
+    private final BlockingMediaPlayer mPlayer;
+
+    AudioMessageParams(UtteranceCompletedDispatcher dispatcher,
+            String callingApp, BlockingMediaPlayer player) {
+        super(dispatcher, callingApp);
+        mPlayer = player;
+    }
+
+    BlockingMediaPlayer getPlayer() {
+        return mPlayer;
+    }
+
+    @Override
+    int getType() {
+        return TYPE_AUDIO;
+    }
+
+}
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
new file mode 100644
index 0000000..c068b75
--- /dev/null
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -0,0 +1,528 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+import android.util.Log;
+
+import java.util.Iterator;
+import java.util.concurrent.PriorityBlockingQueue;
+import java.util.concurrent.atomic.AtomicLong;
+
+class AudioPlaybackHandler {
+    private static final String TAG = "TTS.AudioPlaybackHandler";
+    private static final boolean DBG = false;
+
+    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
+
+    private static final int SYNTHESIS_START = 1;
+    private static final int SYNTHESIS_DATA_AVAILABLE = 2;
+    private static final int SYNTHESIS_COMPLETE_DATA_AVAILABLE = 3;
+    private static final int SYNTHESIS_DONE = 4;
+
+    private static final int PLAY_AUDIO = 5;
+    private static final int PLAY_SILENCE = 6;
+
+    private static final int SHUTDOWN = -1;
+
+    private static final int DEFAULT_PRIORITY = 1;
+    private static final int HIGH_PRIORITY = 0;
+
+    private final PriorityBlockingQueue<ListEntry> mQueue =
+            new PriorityBlockingQueue<ListEntry>();
+    private final Thread mHandlerThread;
+
+    private final Object mStateLock = new Object();
+
+    // Accessed by multiple threads, synchronized by "mStateLock".
+    private MessageParams mCurrentParams = null;
+    // Used only for book keeping and error detection.
+    private volatile SynthesisMessageParams mLastSynthesisRequest = null;
+    // Used to order incoming messages in our priority queue.
+    private final AtomicLong mSequenceIdCtr = new AtomicLong(0);
+
+
+    AudioPlaybackHandler() {
+        mHandlerThread = new Thread(new MessageLoop(), "TTS.AudioPlaybackThread");
+    }
+
+    public void start() {
+        mHandlerThread.start();
+    }
+
+    /**
+     * Stops all synthesis for a given {@code token}. If the current token
+     * is currently being processed, an effort will be made to stop it but
+     * that is not guaranteed.
+     */
+    synchronized public void stop(MessageParams token) {
+        if (token == null) {
+            return;
+        }
+
+        removeMessages(token);
+
+        if (token.getType() == MessageParams.TYPE_SYNTHESIS) {
+            mQueue.add(new ListEntry(SYNTHESIS_DONE, token, HIGH_PRIORITY));
+        } else  {
+            MessageParams current = getCurrentParams();
+
+            if (current != null) {
+                if (token.getType() == MessageParams.TYPE_AUDIO) {
+                    ((AudioMessageParams) current).getPlayer().stop();
+                } else if (token.getType() == MessageParams.TYPE_SILENCE) {
+                    ((SilenceMessageParams) current).getConditionVariable().open();
+                }
+            }
+        }
+    }
+
+    synchronized public void removePlaybackItems(String callingApp) {
+        removeMessages(callingApp);
+        stop(getCurrentParams());
+    }
+
+    synchronized public void removeAllItems() {
+        removeAllMessages();
+        stop(getCurrentParams());
+    }
+
+    /**
+     * Shut down the audio playback thread.
+     */
+    synchronized public void quit() {
+        stop(getCurrentParams());
+        mQueue.add(new ListEntry(SHUTDOWN, null, HIGH_PRIORITY));
+    }
+
+    void enqueueSynthesisStart(SynthesisMessageParams token) {
+        mQueue.add(new ListEntry(SYNTHESIS_START, token));
+    }
+
+    void enqueueSynthesisDataAvailable(SynthesisMessageParams token) {
+        mQueue.add(new ListEntry(SYNTHESIS_DATA_AVAILABLE, token));
+    }
+
+    void enqueueSynthesisCompleteDataAvailable(SynthesisMessageParams token) {
+        mQueue.add(new ListEntry(SYNTHESIS_COMPLETE_DATA_AVAILABLE, token));
+    }
+
+    void enqueueSynthesisDone(SynthesisMessageParams token) {
+        mQueue.add(new ListEntry(SYNTHESIS_DONE, token));
+    }
+
+    void enqueueAudio(AudioMessageParams token) {
+        mQueue.add(new ListEntry(PLAY_AUDIO, token));
+    }
+
+    void enqueueSilence(SilenceMessageParams token) {
+        mQueue.add(new ListEntry(PLAY_SILENCE, token));
+    }
+
+    // -----------------------------------------
+    // End of public API methods.
+    // -----------------------------------------
+
+    // -----------------------------------------
+    // Methods for managing the message queue.
+    // -----------------------------------------
+
+    /*
+     * The MessageLoop is a handler like implementation that
+     * processes messages from a priority queue.
+     */
+    private final class MessageLoop implements Runnable {
+        @Override
+        public void run() {
+            while (true) {
+                ListEntry entry = null;
+                try {
+                    entry = mQueue.take();
+                } catch (InterruptedException ie) {
+                    return;
+                }
+
+                if (entry.mWhat == SHUTDOWN) {
+                    if (DBG) Log.d(TAG, "MessageLoop : Shutting down");
+                    return;
+                }
+
+                if (DBG) {
+                    Log.d(TAG, "MessageLoop : Handling message :" + entry.mWhat
+                            + " ,seqId : " + entry.mSequenceId);
+                }
+
+                setCurrentParams(entry.mMessage);
+                handleMessage(entry);
+                setCurrentParams(null);
+            }
+        }
+    }
+
+    /*
+     * Remove all messages from the queue that contain the supplied token.
+     * Note that the Iterator is thread safe, and other methods can safely
+     * continue adding to the queue at this point.
+     */
+    synchronized private void removeMessages(MessageParams token) {
+        if (token == null) {
+            return;
+        }
+
+        Iterator<ListEntry> it = mQueue.iterator();
+
+        while (it.hasNext()) {
+            final ListEntry current = it.next();
+            if (current.mMessage == token) {
+                it.remove();
+            }
+        }
+    }
+
+    /*
+     * Atomically clear the queue of all messages.
+     */
+    synchronized private void removeAllMessages() {
+        mQueue.clear();
+    }
+
+    /*
+     * Remove all messages that originate from a given calling app.
+     */
+    synchronized private void removeMessages(String callingApp) {
+        Iterator<ListEntry> it = mQueue.iterator();
+
+        while (it.hasNext()) {
+            final ListEntry current = it.next();
+            // The null check is to prevent us from removing control messages,
+            // such as a shutdown message.
+            if (current.mMessage != null &&
+                    callingApp.equals(current.mMessage.getCallingApp())) {
+                it.remove();
+            }
+        }
+    }
+
+    /*
+     * An element of our priority queue of messages. Each message has a priority,
+     * and a sequence id (defined by the order of enqueue calls). Among messages
+     * with the same priority, messages that were received earlier win out.
+     */
+    private final class ListEntry implements Comparable<ListEntry> {
+        final int mWhat;
+        final MessageParams mMessage;
+        final int mPriority;
+        final long mSequenceId;
+
+        private ListEntry(int what, MessageParams message) {
+            this(what, message, DEFAULT_PRIORITY);
+        }
+
+        private ListEntry(int what, MessageParams message, int priority) {
+            mWhat = what;
+            mMessage = message;
+            mPriority = priority;
+            mSequenceId = mSequenceIdCtr.incrementAndGet();
+        }
+
+        @Override
+        public int compareTo(ListEntry that) {
+            if (that == this) {
+                return 0;
+            }
+
+            // Note that this is always 0, 1 or -1.
+            int priorityDiff = mPriority - that.mPriority;
+            if (priorityDiff == 0) {
+                // The == case cannot occur.
+                return (mSequenceId < that.mSequenceId) ? -1 : 1;
+            }
+
+            return priorityDiff;
+        }
+    }
+
+    private void setCurrentParams(MessageParams p) {
+        synchronized (mStateLock) {
+            mCurrentParams = p;
+        }
+    }
+
+    private MessageParams getCurrentParams() {
+        synchronized (mStateLock) {
+            return mCurrentParams;
+        }
+    }
+
+    // -----------------------------------------
+    // Methods for dealing with individual messages, the methods
+    // below do the actual work.
+    // -----------------------------------------
+
+    private void handleMessage(ListEntry entry) {
+        final MessageParams msg = entry.mMessage;
+        if (entry.mWhat == SYNTHESIS_START) {
+            handleSynthesisStart(msg);
+        } else if (entry.mWhat == SYNTHESIS_DATA_AVAILABLE) {
+            handleSynthesisDataAvailable(msg);
+        } else if (entry.mWhat == SYNTHESIS_DONE) {
+            handleSynthesisDone(msg);
+        } else if (entry.mWhat == SYNTHESIS_COMPLETE_DATA_AVAILABLE) {
+            handleSynthesisCompleteDataAvailable(msg);
+        } else if (entry.mWhat == PLAY_AUDIO) {
+            handleAudio(msg);
+        } else if (entry.mWhat == PLAY_SILENCE) {
+            handleSilence(msg);
+        }
+    }
+
+    // Currently implemented as blocking the audio playback thread for the
+    // specified duration. If a call to stop() is made, the thread
+    // unblocks.
+    private void handleSilence(MessageParams msg) {
+        if (DBG) Log.d(TAG, "handleSilence()");
+        SilenceMessageParams params = (SilenceMessageParams) msg;
+        if (params.getSilenceDurationMs() > 0) {
+            params.getConditionVariable().block(params.getSilenceDurationMs());
+        }
+        params.getDispatcher().dispatchUtteranceCompleted();
+        if (DBG) Log.d(TAG, "handleSilence() done.");
+    }
+
+    // Plays back audio from a given URI. No TTS engine involvement here.
+    private void handleAudio(MessageParams msg) {
+        if (DBG) Log.d(TAG, "handleAudio()");
+        AudioMessageParams params = (AudioMessageParams) msg;
+        // Note that the BlockingMediaPlayer spawns a separate thread.
+        //
+        // TODO: This can be avoided.
+        params.getPlayer().startAndWait();
+        params.getDispatcher().dispatchUtteranceCompleted();
+        if (DBG) Log.d(TAG, "handleAudio() done.");
+    }
+
+    // Denotes the start of a new synthesis request. We create a new
+    // audio track, and prepare it for incoming data.
+    //
+    // Note that since all TTS synthesis happens on a single thread, we
+    // should ALWAYS see the following order :
+    //
+    // handleSynthesisStart -> handleSynthesisDataAvailable(*) -> handleSynthesisDone
+    // OR
+    // handleSynthesisCompleteDataAvailable.
+    private void handleSynthesisStart(MessageParams msg) {
+        if (DBG) Log.d(TAG, "handleSynthesisStart()");
+        final SynthesisMessageParams param = (SynthesisMessageParams) msg;
+
+        // Oops, looks like the engine forgot to call done(). We go through
+        // extra trouble to clean the data to prevent the AudioTrack resources
+        // from being leaked.
+        if (mLastSynthesisRequest != null) {
+            Log.w(TAG, "Error : Missing call to done() for request : " +
+                    mLastSynthesisRequest);
+            handleSynthesisDone(mLastSynthesisRequest);
+        }
+
+        mLastSynthesisRequest = param;
+
+        // Create the audio track.
+        final AudioTrack audioTrack = createStreamingAudioTrack(
+                param.mStreamType, param.mSampleRateInHz, param.mAudioFormat,
+                param.mChannelCount, param.mVolume, param.mPan);
+
+        if (DBG) Log.d(TAG, "Created audio track [" + audioTrack.hashCode() + "]");
+
+        param.setAudioTrack(audioTrack);
+    }
+
+    // More data available to be flushed to the audio track.
+    private void handleSynthesisDataAvailable(MessageParams msg) {
+        final SynthesisMessageParams param = (SynthesisMessageParams) msg;
+        if (param.getAudioTrack() == null) {
+            Log.w(TAG, "Error : null audio track in handleDataAvailable.");
+            return;
+        }
+
+        if (param != mLastSynthesisRequest) {
+            Log.e(TAG, "Call to dataAvailable without done() / start()");
+            return;
+        }
+
+        final AudioTrack audioTrack = param.getAudioTrack();
+        final SynthesisMessageParams.ListEntry bufferCopy = param.getNextBuffer();
+
+        if (bufferCopy == null) {
+            Log.e(TAG, "No buffers available to play.");
+            return;
+        }
+
+        int playState = audioTrack.getPlayState();
+        if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+            if (DBG) Log.d(TAG, "AudioTrack stopped, restarting : " + audioTrack.hashCode());
+            audioTrack.play();
+        }
+        int count = 0;
+        while (count < bufferCopy.mLength) {
+            // Note that we don't take bufferCopy.mOffset into account because
+            // it is guaranteed to be 0.
+            int written = audioTrack.write(bufferCopy.mBytes, count, bufferCopy.mLength);
+            if (written <= 0) {
+                break;
+            }
+            count += written;
+        }
+    }
+
+    private void handleSynthesisDone(MessageParams msg) {
+        final SynthesisMessageParams params = (SynthesisMessageParams) msg;
+        handleSynthesisDone(params);
+    }
+
+    // Flush all remaining data to the audio track, stop it and release
+    // all it's resources.
+    private void handleSynthesisDone(SynthesisMessageParams params) {
+        if (DBG) Log.d(TAG, "handleSynthesisDone()");
+        final AudioTrack audioTrack = params.getAudioTrack();
+
+        try {
+            if (audioTrack != null) {
+                audioTrack.flush();
+                audioTrack.stop();
+                if (DBG) Log.d(TAG, "Releasing audio track [" + audioTrack.hashCode() + "]");
+                audioTrack.release();
+            }
+        } finally {
+            params.setAudioTrack(null);
+            params.getDispatcher().dispatchUtteranceCompleted();
+            mLastSynthesisRequest = null;
+        }
+    }
+
+    private void handleSynthesisCompleteDataAvailable(MessageParams msg) {
+        final SynthesisMessageParams params = (SynthesisMessageParams) msg;
+        if (DBG) Log.d(TAG, "completeAudioAvailable(" + params + ")");
+
+        // Channel config and bytes per frame are checked before
+        // this message is sent.
+        int channelConfig = AudioPlaybackHandler.getChannelConfig(params.mChannelCount);
+        int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(params.mAudioFormat);
+
+        SynthesisMessageParams.ListEntry entry = params.getNextBuffer();
+
+        if (entry == null) {
+            Log.w(TAG, "completeDataAvailable : No buffers available to play.");
+            return;
+        }
+
+        final AudioTrack audioTrack = new AudioTrack(params.mStreamType, params.mSampleRateInHz,
+                channelConfig, params.mAudioFormat, entry.mLength, AudioTrack.MODE_STATIC);
+
+        // So that handleDone can access this correctly.
+        params.mAudioTrack = audioTrack;
+
+        try {
+            audioTrack.write(entry.mBytes, entry.mOffset, entry.mLength);
+            setupVolume(audioTrack, params.mVolume, params.mPan);
+            audioTrack.play();
+            blockUntilDone(audioTrack, bytesPerFrame, entry.mLength);
+            if (DBG) Log.d(TAG, "Wrote data to audio track successfully : " + entry.mLength);
+        } catch (IllegalStateException ex) {
+            Log.e(TAG, "Playback error", ex);
+        } finally {
+            handleSynthesisDone(msg);
+        }
+    }
+
+
+    private static void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
+        int lengthInFrames = length / bytesPerFrame;
+        int currentPosition = 0;
+        while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
+            long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
+                    audioTrack.getSampleRate();
+            audioTrack.getPlayState();
+            if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
+                    " Playback position : " + currentPosition);
+            try {
+                Thread.sleep(estimatedTimeMs);
+            } catch (InterruptedException ie) {
+                break;
+            }
+        }
+    }
+
+    private static AudioTrack createStreamingAudioTrack(int streamType, int sampleRateInHz,
+            int audioFormat, int channelCount, float volume, float pan) {
+        int channelConfig = getChannelConfig(channelCount);
+
+        int minBufferSizeInBytes
+                = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
+        int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
+
+        AudioTrack audioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig,
+                audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
+        if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+            Log.w(TAG, "Unable to create audio track.");
+            audioTrack.release();
+            return null;
+        }
+
+        setupVolume(audioTrack, volume, pan);
+        return audioTrack;
+    }
+
+    static int getChannelConfig(int channelCount) {
+        if (channelCount == 1) {
+            return AudioFormat.CHANNEL_OUT_MONO;
+        } else if (channelCount == 2){
+            return AudioFormat.CHANNEL_OUT_STEREO;
+        }
+
+        return 0;
+    }
+
+    static int getBytesPerFrame(int audioFormat) {
+        if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
+            return 1;
+        } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
+            return 2;
+        }
+
+        return -1;
+    }
+
+    private static void setupVolume(AudioTrack audioTrack, float volume, float pan) {
+        float vol = clip(volume, 0.0f, 1.0f);
+        float panning = clip(pan, -1.0f, 1.0f);
+        float volLeft = vol;
+        float volRight = vol;
+        if (panning > 0.0f) {
+            volLeft *= (1.0f - panning);
+        } else if (panning < 0.0f) {
+            volRight *= (1.0f + panning);
+        }
+        if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
+        if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
+            Log.e(TAG, "Failed to set volume");
+        }
+    }
+
+    private static float clip(float value, float min, float max) {
+        return value > max ? max : (value < min ? min : value);
+    }
+
+}
diff --git a/core/java/android/speech/tts/FileSynthesisRequest.java b/core/java/android/speech/tts/FileSynthesisCallback.java
similarity index 97%
rename from core/java/android/speech/tts/FileSynthesisRequest.java
rename to core/java/android/speech/tts/FileSynthesisCallback.java
index 62be2bf..4f4b3fb 100644
--- a/core/java/android/speech/tts/FileSynthesisRequest.java
+++ b/core/java/android/speech/tts/FileSynthesisCallback.java
@@ -16,7 +16,6 @@
 package android.speech.tts;
 
 import android.media.AudioFormat;
-import android.os.Bundle;
 import android.util.Log;
 
 import java.io.File;
@@ -29,7 +28,7 @@
 /**
  * Speech synthesis request that writes the audio to a WAV file.
  */
-class FileSynthesisRequest extends SynthesisRequest {
+class FileSynthesisCallback extends AbstractSynthesisCallback {
 
     private static final String TAG = "FileSynthesisRequest";
     private static final boolean DBG = false;
@@ -48,8 +47,7 @@
     private boolean mStopped = false;
     private boolean mDone = false;
 
-    FileSynthesisRequest(String text, Bundle params, File fileName) {
-        super(text, params);
+    FileSynthesisCallback(File fileName) {
         mFileName = fileName;
     }
 
diff --git a/core/java/android/speech/tts/MessageParams.java b/core/java/android/speech/tts/MessageParams.java
new file mode 100644
index 0000000..4c1b6d2
--- /dev/null
+++ b/core/java/android/speech/tts/MessageParams.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+abstract class MessageParams {
+    static final int TYPE_SYNTHESIS = 1;
+    static final int TYPE_AUDIO = 2;
+    static final int TYPE_SILENCE = 3;
+
+    private final UtteranceCompletedDispatcher mDispatcher;
+    private final String mCallingApp;
+
+    MessageParams(UtteranceCompletedDispatcher dispatcher, String callingApp) {
+        mDispatcher = dispatcher;
+        mCallingApp = callingApp;
+    }
+
+    UtteranceCompletedDispatcher getDispatcher() {
+        return mDispatcher;
+    }
+
+    String getCallingApp() {
+        return mCallingApp;
+    }
+
+    abstract int getType();
+}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
new file mode 100644
index 0000000..bdaa1b8
--- /dev/null
+++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+import android.util.Log;
+
+/**
+ * Speech synthesis request that plays the audio as it is received.
+ */
+class PlaybackSynthesisCallback extends AbstractSynthesisCallback {
+
+    private static final String TAG = "PlaybackSynthesisRequest";
+    private static final boolean DBG = false;
+
+    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
+
+    /**
+     * Audio stream type. Must be one of the STREAM_ contants defined in
+     * {@link android.media.AudioManager}.
+     */
+    private final int mStreamType;
+
+    /**
+     * Volume, in the range [0.0f, 1.0f]. The default value is
+     * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
+     */
+    private final float mVolume;
+
+    /**
+     * Left/right position of the audio, in the range [-1.0f, 1.0f].
+     * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
+     */
+    private final float mPan;
+
+    /**
+     * Guards {@link #mAudioTrackHandler}, {@link #mToken} and {@link #mStopped}.
+     */
+    private final Object mStateLock = new Object();
+
+    // Handler associated with a thread that plays back audio requests.
+    private final AudioPlaybackHandler mAudioTrackHandler;
+    // A request "token", which will be non null after start() or
+    // completeAudioAvailable() have been called.
+    private SynthesisMessageParams mToken = null;
+    // Whether this request has been stopped. This is useful for keeping
+    // track whether stop() has been called before start(). In all other cases,
+    // a non-null value of mToken will provide the same information.
+    private boolean mStopped = false;
+
+    private volatile boolean mDone = false;
+
+    private final UtteranceCompletedDispatcher mDispatcher;
+    private final String mCallingApp;
+
+    PlaybackSynthesisCallback(int streamType, float volume, float pan,
+            AudioPlaybackHandler audioTrackHandler, UtteranceCompletedDispatcher dispatcher,
+            String callingApp) {
+        mStreamType = streamType;
+        mVolume = volume;
+        mPan = pan;
+        mAudioTrackHandler = audioTrackHandler;
+        mDispatcher = dispatcher;
+        mCallingApp = callingApp;
+    }
+
+    @Override
+    void stop() {
+        if (DBG) Log.d(TAG, "stop()");
+
+        synchronized (mStateLock) {
+            if (mToken == null || mStopped) {
+                Log.w(TAG, "stop() called twice, before start(), or after done()");
+                return;
+            }
+            mAudioTrackHandler.stop(mToken);
+            mToken = null;
+            mStopped = true;
+        }
+    }
+
+    @Override
+    public int getMaxBufferSize() {
+        // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
+        // a safe buffer size to pass in.
+        return MIN_AUDIO_BUFFER_SIZE;
+    }
+
+    @Override
+    boolean isDone() {
+        return mDone;
+    }
+
+    @Override
+    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
+        if (DBG) {
+            Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
+                    + "," + channelCount + ")");
+        }
+
+        int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
+        if (channelConfig == 0) {
+            Log.e(TAG, "Unsupported number of channels :" + channelCount);
+            return TextToSpeech.ERROR;
+        }
+
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "stop() called before start(), returning.");
+                return TextToSpeech.ERROR;
+            }
+            SynthesisMessageParams params = new SynthesisMessageParams(
+                    mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
+                    mDispatcher, mCallingApp);
+            mAudioTrackHandler.enqueueSynthesisStart(params);
+
+            mToken = params;
+        }
+
+        return TextToSpeech.SUCCESS;
+    }
+
+
+    @Override
+    public int audioAvailable(byte[] buffer, int offset, int length) {
+        if (DBG) {
+            Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
+                    + offset + "," + length + ")");
+        }
+        if (length > getMaxBufferSize() || length <= 0) {
+            throw new IllegalArgumentException("buffer is too large or of zero length (" +
+                    + length + " bytes)");
+        }
+
+        synchronized (mStateLock) {
+            if (mToken == null) {
+                return TextToSpeech.ERROR;
+            }
+
+            // Sigh, another copy.
+            final byte[] bufferCopy = new byte[length];
+            System.arraycopy(buffer, offset, bufferCopy, 0, length);
+            mToken.addBuffer(bufferCopy);
+            mAudioTrackHandler.enqueueSynthesisDataAvailable(mToken);
+        }
+
+        return TextToSpeech.SUCCESS;
+    }
+
+    @Override
+    public int done() {
+        if (DBG) Log.d(TAG, "done()");
+
+        synchronized (mStateLock) {
+            if (mDone) {
+                Log.w(TAG, "Duplicate call to done()");
+                return TextToSpeech.ERROR;
+            }
+
+            mDone = true;
+
+            if (mToken == null) {
+                return TextToSpeech.ERROR;
+            }
+
+            mAudioTrackHandler.enqueueSynthesisDone(mToken);
+        }
+        return TextToSpeech.SUCCESS;
+    }
+
+    @Override
+    public void error() {
+        if (DBG) Log.d(TAG, "error() [will call stop]");
+        stop();
+    }
+
+    @Override
+    public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
+            byte[] buffer, int offset, int length) {
+        int channelConfig = AudioPlaybackHandler.getChannelConfig(channelCount);
+        if (channelConfig == 0) {
+            Log.e(TAG, "Unsupported number of channels :" + channelCount);
+            return TextToSpeech.ERROR;
+        }
+
+        int bytesPerFrame = AudioPlaybackHandler.getBytesPerFrame(audioFormat);
+        if (bytesPerFrame < 0) {
+            Log.e(TAG, "Unsupported audio format :" + audioFormat);
+            return TextToSpeech.ERROR;
+        }
+
+        synchronized (mStateLock) {
+            if (mStopped) {
+                return TextToSpeech.ERROR;
+            }
+            SynthesisMessageParams params = new SynthesisMessageParams(
+                    mStreamType, sampleRateInHz, audioFormat, channelCount, mVolume, mPan,
+                    mDispatcher, mCallingApp);
+            params.addBuffer(buffer, offset, length);
+
+            mAudioTrackHandler.enqueueSynthesisCompleteDataAvailable(params);
+            mToken = params;
+        }
+
+        return TextToSpeech.SUCCESS;
+    }
+
+}
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
deleted file mode 100644
index d698b54..0000000
--- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java
+++ /dev/null
@@ -1,363 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License"); you may not
- * use this file except in compliance with the License. You may obtain a copy of
- * the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- * License for the specific language governing permissions and limitations under
- * the License.
- */
-package android.speech.tts;
-
-import android.media.AudioFormat;
-import android.media.AudioTrack;
-import android.os.Bundle;
-import android.os.Handler;
-import android.util.Log;
-
-/**
- * Speech synthesis request that plays the audio as it is received.
- */
-class PlaybackSynthesisRequest extends SynthesisRequest {
-
-    private static final String TAG = "PlaybackSynthesisRequest";
-    private static final boolean DBG = false;
-
-    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
-
-    /**
-     * Audio stream type. Must be one of the STREAM_ contants defined in
-     * {@link android.media.AudioManager}.
-     */
-    private final int mStreamType;
-
-    /**
-     * Volume, in the range [0.0f, 1.0f]. The default value is
-     * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
-     */
-    private final float mVolume;
-
-    /**
-     * Left/right position of the audio, in the range [-1.0f, 1.0f].
-     * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
-     */
-    private final float mPan;
-
-    private final Object mStateLock = new Object();
-    private final Handler mAudioTrackHandler;
-    private volatile AudioTrack mAudioTrack = null;
-    private boolean mStopped = false;
-    private boolean mDone = false;
-    private volatile boolean mWriteErrorOccured;
-
-    PlaybackSynthesisRequest(String text, Bundle params,
-            int streamType, float volume, float pan, Handler audioTrackHandler) {
-        super(text, params);
-        mStreamType = streamType;
-        mVolume = volume;
-        mPan = pan;
-        mAudioTrackHandler = audioTrackHandler;
-        mWriteErrorOccured = false;
-    }
-
-    @Override
-    void stop() {
-        if (DBG) Log.d(TAG, "stop()");
-        synchronized (mStateLock) {
-            mStopped = true;
-            cleanUp();
-        }
-    }
-
-    // Always guarded by mStateLock.
-    private void cleanUp() {
-        if (DBG) Log.d(TAG, "cleanUp()");
-        if (mAudioTrack == null) {
-            return;
-        }
-
-        final AudioTrack audioTrack = mAudioTrack;
-        mAudioTrack = null;
-
-        // Clean up on the audiotrack handler thread.
-        //
-        // NOTE: It isn't very clear whether AudioTrack is thread safe.
-        // If it is we can clean up on the current (synthesis) thread.
-        mAudioTrackHandler.post(new Runnable() {
-            @Override
-            public void run() {
-                audioTrack.flush();
-                audioTrack.stop();
-                audioTrack.release();
-            }
-        });
-    }
-
-    @Override
-    public int getMaxBufferSize() {
-        // The AudioTrack buffer will be at least MIN_AUDIO_BUFFER_SIZE, so that should always be
-        // a safe buffer size to pass in.
-        return MIN_AUDIO_BUFFER_SIZE;
-    }
-
-    @Override
-    boolean isDone() {
-        return mDone;
-    }
-
-    // TODO: add a thread that writes to the AudioTrack?
-    @Override
-    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
-        if (DBG) {
-            Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
-                    + "," + channelCount + ")");
-        }
-
-        synchronized (mStateLock) {
-            if (mStopped) {
-                if (DBG) Log.d(TAG, "Request has been aborted.");
-                return TextToSpeech.ERROR;
-            }
-            if (mAudioTrack != null) {
-                Log.e(TAG, "start() called twice");
-                cleanUp();
-                return TextToSpeech.ERROR;
-            }
-
-            mAudioTrack = createStreamingAudioTrack(sampleRateInHz, audioFormat, channelCount);
-            if (mAudioTrack == null) {
-                return TextToSpeech.ERROR;
-            }
-        }
-
-        return TextToSpeech.SUCCESS;
-    }
-
-    private void setupVolume(AudioTrack audioTrack, float volume, float pan) {
-        float vol = clip(volume, 0.0f, 1.0f);
-        float panning = clip(pan, -1.0f, 1.0f);
-        float volLeft = vol;
-        float volRight = vol;
-        if (panning > 0.0f) {
-            volLeft *= (1.0f - panning);
-        } else if (panning < 0.0f) {
-            volRight *= (1.0f + panning);
-        }
-        if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
-        if (audioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
-            Log.e(TAG, "Failed to set volume");
-        }
-    }
-
-    private float clip(float value, float min, float max) {
-        return value > max ? max : (value < min ? min : value);
-    }
-
-    @Override
-    public int audioAvailable(byte[] buffer, int offset, int length) {
-        if (DBG) {
-            Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
-                    + offset + "," + length + ")");
-        }
-        if (length > getMaxBufferSize() || length <= 0) {
-            throw new IllegalArgumentException("buffer is too large or of zero length (" +
-                    + length + " bytes)");
-        }
-        synchronized (mStateLock) {
-            if (mWriteErrorOccured) {
-                if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
-                return TextToSpeech.ERROR;
-            }
-            if (mStopped) {
-                if (DBG) Log.d(TAG, "Request has been aborted.");
-                return TextToSpeech.ERROR;
-            }
-            if (mAudioTrack == null) {
-                Log.e(TAG, "audioAvailable(): Not started");
-                return TextToSpeech.ERROR;
-            }
-            final AudioTrack audioTrack = mAudioTrack;
-            // Sigh, another copy.
-            final byte[] bufferCopy = new byte[length];
-            System.arraycopy(buffer, offset, bufferCopy, 0, length);
-
-            mAudioTrackHandler.post(new Runnable() {
-                @Override
-                public void run() {
-                    int playState = audioTrack.getPlayState();
-                    if (playState == AudioTrack.PLAYSTATE_STOPPED) {
-                        if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
-                        audioTrack.play();
-                    }
-                    // TODO: loop until all data is written?
-                    if (DBG) Log.d(TAG, "AudioTrack.write()");
-                    int count = audioTrack.write(bufferCopy, 0, bufferCopy.length);
-                    // The semantics of this change very slightly. Earlier, we would
-                    // report an error immediately, Now we will return an error on
-                    // the next API call, usually done( ) or another audioAvailable( )
-                    // call.
-                    if (count < 0) {
-                        mWriteErrorOccured = true;
-                    }
-                }
-            });
-
-            return TextToSpeech.SUCCESS;
-        }
-    }
-
-    @Override
-    public int done() {
-        if (DBG) Log.d(TAG, "done()");
-        synchronized (mStateLock) {
-            if (mWriteErrorOccured) {
-                if (DBG) Log.d(TAG, "Error writing to audio track, count < 0");
-                return TextToSpeech.ERROR;
-            }
-            if (mStopped) {
-                if (DBG) Log.d(TAG, "Request has been aborted.");
-                return TextToSpeech.ERROR;
-            }
-            if (mAudioTrack == null) {
-                Log.e(TAG, "done(): Not started");
-                return TextToSpeech.ERROR;
-            }
-            mDone = true;
-            cleanUp();
-        }
-        return TextToSpeech.SUCCESS;
-    }
-
-    @Override
-    public void error() {
-        if (DBG) Log.d(TAG, "error()");
-        synchronized (mStateLock) {
-            cleanUp();
-        }
-    }
-
-    @Override
-    public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
-            byte[] buffer, int offset, int length) {
-        if (DBG) {
-            Log.d(TAG, "completeAudioAvailable(" + sampleRateInHz + "," + audioFormat
-                    + "," + channelCount + "byte[" + buffer.length + "],"
-                    + offset + "," + length + ")");
-        }
-
-        synchronized (mStateLock) {
-            if (mStopped) {
-                if (DBG) Log.d(TAG, "Request has been aborted.");
-                return TextToSpeech.ERROR;
-            }
-            if (mAudioTrack != null) {
-                Log.e(TAG, "start() called before completeAudioAvailable()");
-                cleanUp();
-                return TextToSpeech.ERROR;
-            }
-
-            int channelConfig = getChannelConfig(channelCount);
-            if (channelConfig < 0) {
-                Log.e(TAG, "Unsupported number of channels :" + channelCount);
-                cleanUp();
-                return TextToSpeech.ERROR;
-            }
-            int bytesPerFrame = getBytesPerFrame(audioFormat);
-            if (bytesPerFrame < 0) {
-                Log.e(TAG, "Unsupported audio format :" + audioFormat);
-                cleanUp();
-                return TextToSpeech.ERROR;
-            }
-
-            mAudioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
-                    audioFormat, buffer.length, AudioTrack.MODE_STATIC);
-            if (mAudioTrack == null) {
-                return TextToSpeech.ERROR;
-            }
-
-            try {
-                mAudioTrack.write(buffer, offset, length);
-                setupVolume(mAudioTrack, mVolume, mPan);
-                mAudioTrack.play();
-                blockUntilDone(mAudioTrack, bytesPerFrame, length);
-                mDone = true;
-                if (DBG) Log.d(TAG, "Wrote data to audio track succesfully : " + length);
-            } catch (IllegalStateException ex) {
-                Log.e(TAG, "Playback error", ex);
-                return TextToSpeech.ERROR;
-            } finally {
-                cleanUp();
-            }
-        }
-
-        return TextToSpeech.SUCCESS;
-    }
-
-    private void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
-        int lengthInFrames = length / bytesPerFrame;
-        int currentPosition = 0;
-        while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
-            long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
-                    audioTrack.getSampleRate();
-            if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
-                    " Playback position : " + currentPosition);
-            try {
-                Thread.sleep(estimatedTimeMs);
-            } catch (InterruptedException ie) {
-                break;
-            }
-        }
-    }
-
-    private int getBytesPerFrame(int audioFormat) {
-        if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
-            return 1;
-        } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
-            return 2;
-        }
-
-        return -1;
-    }
-
-    private int getChannelConfig(int channelCount) {
-        if (channelCount == 1) {
-            return AudioFormat.CHANNEL_OUT_MONO;
-        } else if (channelCount == 2){
-            return AudioFormat.CHANNEL_OUT_STEREO;
-        }
-
-        return -1;
-    }
-
-    private AudioTrack createStreamingAudioTrack(int sampleRateInHz, int audioFormat,
-            int channelCount) {
-        int channelConfig = getChannelConfig(channelCount);
-
-        if (channelConfig < 0) {
-            Log.e(TAG, "Unsupported number of channels : " + channelCount);
-            return null;
-        }
-
-        int minBufferSizeInBytes
-                = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
-        int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
-        AudioTrack audioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
-                audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
-        if (audioTrack == null) {
-            return null;
-        }
-
-        if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
-            audioTrack.release();
-            return null;
-        }
-        setupVolume(audioTrack, mVolume, mPan);
-        return audioTrack;
-    }
-}
diff --git a/core/java/android/speech/tts/SilenceMessageParams.java b/core/java/android/speech/tts/SilenceMessageParams.java
new file mode 100644
index 0000000..7a4ff1c
--- /dev/null
+++ b/core/java/android/speech/tts/SilenceMessageParams.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.os.ConditionVariable;
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+class SilenceMessageParams extends MessageParams {
+    private final ConditionVariable mCondVar = new ConditionVariable();
+    private final long mSilenceDurationMs;
+
+    SilenceMessageParams(UtteranceCompletedDispatcher dispatcher,
+            String callingApp, long silenceDurationMs) {
+        super(dispatcher, callingApp);
+        mSilenceDurationMs = silenceDurationMs;
+    }
+
+    long getSilenceDurationMs() {
+        return mSilenceDurationMs;
+    }
+
+    @Override
+    int getType() {
+        return TYPE_SILENCE;
+    }
+
+    ConditionVariable getConditionVariable() {
+        return mCondVar;
+    }
+
+}
diff --git a/core/java/android/speech/tts/SynthesisCallback.java b/core/java/android/speech/tts/SynthesisCallback.java
new file mode 100644
index 0000000..1b80e40
--- /dev/null
+++ b/core/java/android/speech/tts/SynthesisCallback.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+/**
+ * A callback to return speech data synthesized by a text to speech engine.
+ *
+ * The engine can provide streaming audio by calling
+ * {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally
+ * {@link #done}.
+ *
+ * Alternatively, the engine can provide all the audio at once, by using
+ * {@link #completeAudioAvailable}.
+ *
+ * {@link #error} can be called at any stage in the synthesis process to
+ * indicate that an error has occured, but if the call is made after a call
+ * to {@link #done} or {@link #completeAudioAvailable} it might be discarded.
+ */
+public interface SynthesisCallback {
+    /**
+     * @return the maximum number of bytes that the TTS engine can pass in a single call of
+     *         {@link #audioAvailable}. This does not apply to {@link #completeAudioAvailable}.
+     *         Calls to {@link #audioAvailable} with data lengths larger than this
+     *         value will not succeed.
+     */
+    public int getMaxBufferSize();
+
+    /**
+     * The service should call this when it starts to synthesize audio for this
+     * request.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     *
+     * @param sampleRateInHz Sample rate in HZ of the generated audio.
+     * @param audioFormat Audio format of the generated audio. Must be one of
+     *         the ENCODING_ constants defined in {@link android.media.AudioFormat}.
+     * @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public int start(int sampleRateInHz, int audioFormat, int channelCount);
+
+    /**
+     * The service should call this method when synthesized audio is ready for consumption.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     *
+     * @param buffer The generated audio data. This method will not hold on to {@code buffer},
+     *         so the caller is free to modify it after this method returns.
+     * @param offset The offset into {@code buffer} where the audio data starts.
+     * @param length The number of bytes of audio data in {@code buffer}. This must be
+     *         less than or equal to the return value of {@link #getMaxBufferSize}.
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public int audioAvailable(byte[] buffer, int offset, int length);
+
+    /**
+     * The service can call this method instead of using {@link #start}, {@link #audioAvailable}
+     * and {@link #done} if all the audio data is available in a single buffer.
+     *
+     * @param sampleRateInHz Sample rate in HZ of the generated audio.
+     * @param audioFormat Audio format of the generated audio. Must be one of
+     *         the ENCODING_ constants defined in {@link android.media.AudioFormat}.
+     * @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
+     * @param buffer The generated audio data. This method will not hold on to {@code buffer},
+     *         so the caller is free to modify it after this method returns.
+     * @param offset The offset into {@code buffer} where the audio data starts.
+     * @param length The number of bytes of audio data in {@code buffer}.
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public int completeAudioAvailable(int sampleRateInHz, int audioFormat,
+            int channelCount, byte[] buffer, int offset, int length);
+
+    /**
+     * The service should call this method when all the synthesized audio for a request has
+     * been passed to {@link #audioAvailable}.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     *
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public int done();
+
+    /**
+     * The service should call this method if the speech synthesis fails.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     */
+    public void error();
+
+}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java
new file mode 100644
index 0000000..51f3d2e
--- /dev/null
+++ b/core/java/android/speech/tts/SynthesisMessageParams.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package android.speech.tts;
+
+import android.media.AudioTrack;
+import android.speech.tts.TextToSpeechService.UtteranceCompletedDispatcher;
+
+import java.util.LinkedList;
+
+/**
+ * Params required to play back a synthesis request.
+ */
+final class SynthesisMessageParams extends MessageParams {
+    final int mStreamType;
+    final int mSampleRateInHz;
+    final int mAudioFormat;
+    final int mChannelCount;
+    final float mVolume;
+    final float mPan;
+
+    public volatile AudioTrack mAudioTrack;
+
+    private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>();
+
+    SynthesisMessageParams(int streamType, int sampleRate,
+            int audioFormat, int channelCount,
+            float volume, float pan, UtteranceCompletedDispatcher dispatcher,
+            String callingApp) {
+        super(dispatcher, callingApp);
+
+        mStreamType = streamType;
+        mSampleRateInHz = sampleRate;
+        mAudioFormat = audioFormat;
+        mChannelCount = channelCount;
+        mVolume = volume;
+        mPan = pan;
+
+        // initially null.
+        mAudioTrack = null;
+    }
+
+    @Override
+    int getType() {
+        return TYPE_SYNTHESIS;
+    }
+
+    synchronized void addBuffer(byte[] buffer, int offset, int length) {
+        mDataBufferList.add(new ListEntry(buffer, offset, length));
+    }
+
+    synchronized void addBuffer(byte[] buffer) {
+        mDataBufferList.add(new ListEntry(buffer, 0, buffer.length));
+    }
+
+    synchronized ListEntry getNextBuffer() {
+        return mDataBufferList.poll();
+    }
+
+
+    void setAudioTrack(AudioTrack audioTrack) {
+        mAudioTrack = audioTrack;
+    }
+
+    AudioTrack getAudioTrack() {
+        return mAudioTrack;
+    }
+
+    static final class ListEntry {
+        final byte[] mBytes;
+        final int mOffset;
+        final int mLength;
+
+        ListEntry(byte[] bytes, int offset, int length) {
+            mBytes = bytes;
+            mOffset = offset;
+            mLength = length;
+        }
+    }
+}
+
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
index 57ae10d..ef1704c 100644
--- a/core/java/android/speech/tts/SynthesisRequest.java
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -18,17 +18,22 @@
 import android.os.Bundle;
 
 /**
- * A request for speech synthesis given to a TTS engine for processing.
+ * Contains data required by engines to synthesize speech. This data is :
+ * <ul>
+ *   <li>The text to synthesize</li>
+ *   <li>The synthesis locale, represented as a language, country and a variant.
+ *   The language is an ISO 639-3 letter language code, and the country is an
+ *   ISO 3166 alpha 3 code. The variant is not specified.</li>
+ *   <li>The synthesis speech rate, with 100 being the normal, and
+ *   higher values representing higher speech rates.</li>
+ *   <li>The voice pitch, with 100 being the default pitch.</li>
+ * </ul>
  *
- * The engine can provide streaming audio by calling
- * {@link #start}, then {@link #audioAvailable} until all audio has been provided, then finally
- * {@link #done}.
- *
- * Alternatively, the engine can provide all the audio at once, by using
- * {@link #completeAudioAvailable}.
+ * Any additional parameters sent to the text to speech service are passed in
+ * uninterpreted, see the @code{params} argument in {@link TextToSpeech#speak}
+ * and {@link TextToSpeech#synthesizeToFile}.
  */
-public abstract class SynthesisRequest {
-
+public final class SynthesisRequest {
     private final String mText;
     private final Bundle mParams;
     private String mLanguage;
@@ -37,35 +42,13 @@
     private int mSpeechRate;
     private int mPitch;
 
-    public SynthesisRequest(String text, Bundle params) {
+    SynthesisRequest(String text, Bundle params) {
         mText = text;
+        // Makes a copy of params.
         mParams = new Bundle(params);
     }
 
     /**
-     * Sets the locale for the request.
-     */
-    void setLanguage(String language, String country, String variant) {
-        mLanguage = language;
-        mCountry = country;
-        mVariant = variant;
-    }
-
-    /**
-     * Sets the speech rate.
-     */
-    void setSpeechRate(int speechRate) {
-        mSpeechRate = speechRate;
-    }
-
-    /**
-     * Sets the pitch.
-     */
-    void setPitch(int pitch) {
-        mPitch = pitch;
-    }
-
-    /**
      * Gets the text which should be synthesized.
      */
     public String getText() {
@@ -115,86 +98,25 @@
     }
 
     /**
-     * Gets the maximum number of bytes that the TTS engine can pass in a single call of
-     * {@link #audioAvailable}. This does not apply to {@link #completeAudioAvailable}.
+     * Sets the locale for the request.
      */
-    public abstract int getMaxBufferSize();
+    void setLanguage(String language, String country, String variant) {
+        mLanguage = language;
+        mCountry = country;
+        mVariant = variant;
+    }
 
     /**
-     * Checks whether the synthesis request completed successfully.
+     * Sets the speech rate.
      */
-    abstract boolean isDone();
+    void setSpeechRate(int speechRate) {
+        mSpeechRate = speechRate;
+    }
 
     /**
-     * Aborts the speech request.
-     *
-     * Can be called from multiple threads.
+     * Sets the pitch.
      */
-    abstract void stop();
-
-    /**
-     * The service should call this when it starts to synthesize audio for this
-     * request.
-     *
-     * This method should only be called on the synthesis thread,
-     * while in {@link TextToSpeechService#onSynthesizeText}.
-     *
-     * @param sampleRateInHz Sample rate in HZ of the generated audio.
-     * @param audioFormat Audio format of the generated audio. Must be one of
-     *         the ENCODING_ constants defined in {@link android.media.AudioFormat}.
-     * @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
-     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
-     */
-    public abstract int start(int sampleRateInHz, int audioFormat, int channelCount);
-
-    /**
-     * The service should call this method when synthesized audio is ready for consumption.
-     *
-     * This method should only be called on the synthesis thread,
-     * while in {@link TextToSpeechService#onSynthesizeText}.
-     *
-     * @param buffer The generated audio data. This method will not hold on to {@code buffer},
-     *         so the caller is free to modify it after this method returns.
-     * @param offset The offset into {@code buffer} where the audio data starts.
-     * @param length The number of bytes of audio data in {@code buffer}. This must be
-     *         less than or equal to the return value of {@link #getMaxBufferSize}.
-     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
-     */
-    public abstract int audioAvailable(byte[] buffer, int offset, int length);
-
-    /**
-     * The service should call this method when all the synthesized audio for a request has
-     * been passed to {@link #audioAvailable}.
-     *
-     * This method should only be called on the synthesis thread,
-     * while in {@link TextToSpeechService#onSynthesizeText}.
-     *
-     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
-     */
-    public abstract int done();
-
-    /**
-     * The service should call this method if the speech synthesis fails.
-     *
-     * This method should only be called on the synthesis thread,
-     * while in {@link TextToSpeechService#onSynthesizeText}.
-     */
-    public abstract void error();
-
-    /**
-     * The service can call this method instead of using {@link #start}, {@link #audioAvailable}
-     * and {@link #done} if all the audio data is available in a single buffer.
-     *
-     * @param sampleRateInHz Sample rate in HZ of the generated audio.
-     * @param audioFormat Audio format of the generated audio. Must be one of
-     *         the ENCODING_ constants defined in {@link android.media.AudioFormat}.
-     * @param channelCount The number of channels. Must be {@code 1} or {@code 2}.
-     * @param buffer The generated audio data. This method will not hold on to {@code buffer},
-     *         so the caller is free to modify it after this method returns.
-     * @param offset The offset into {@code buffer} where the audio data starts.
-     * @param length The number of bytes of audio data in {@code buffer}.
-     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
-     */
-    public abstract int completeAudioAvailable(int sampleRateInHz, int audioFormat,
-            int channelCount, byte[] buffer, int offset, int length);
-}
\ No newline at end of file
+    void setPitch(int pitch) {
+        mPitch = pitch;
+    }
+}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index ddd3252..4898f0e 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -19,7 +19,6 @@
 import android.content.Intent;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.ConditionVariable;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.IBinder;
@@ -40,8 +39,34 @@
 
 
 /**
- * Abstract base class for TTS engine implementations.
+ * Abstract base class for TTS engine implementations. The following methods
+ * need to be implemented.
+ *
+ * <ul>
+ *   <li>{@link #onIsLanguageAvailable}</li>
+ *   <li>{@link #onLoadLanguage}</li>
+ *   <li>{@link #onGetLanguage}</li>
+ *   <li>{@link #onSynthesizeText}</li>
+ *   <li>{@link #onStop}</li>
+ * </ul>
+ *
+ * The first three deal primarily with language management, and are used to
+ * query the engine for it's support for a given language and indicate to it
+ * that requests in a given language are imminent.
+ *
+ * {@link #onSynthesizeText} is central to the engine implementation. The
+ * implementation should synthesize text as per the request parameters and
+ * return synthesized data via the supplied callback. This class and its helpers
+ * will then consume that data, which might mean queueing it for playback or writing
+ * it to a file or similar. All calls to this method will be on a single
+ * thread, which will be different from the main thread of the service. Synthesis
+ * must be synchronous which means the engine must NOT hold on the callback or call
+ * any methods on it after the method returns
+ *
+ * {@link #onStop} tells the engine that it should stop all ongoing synthesis, if
+ * any. Any pending data from the current synthesis will be discarded.
  */
+// TODO: Add a link to the sample TTS engine once it's done.
 public abstract class TextToSpeechService extends Service {
 
     private static final boolean DBG = false;
@@ -51,7 +76,10 @@
     private static final String SYNTH_THREAD_NAME = "SynthThread";
 
     private SynthHandler mSynthHandler;
-    private Handler mAudioTrackHandler;
+    // A thread and it's associated handler for playing back any audio
+    // associated with this TTS engine. Will handle all requests except synthesis
+    // to file requests, which occur on the synthesis thread.
+    private AudioPlaybackHandler mAudioPlaybackHandler;
 
     private CallbackMap mCallbacks;
 
@@ -66,9 +94,8 @@
         synthThread.start();
         mSynthHandler = new SynthHandler(synthThread.getLooper());
 
-        HandlerThread audioTrackThread = new HandlerThread("TTS.audioTrackThread");
-        audioTrackThread.start();
-        mAudioTrackHandler = new Handler(audioTrackThread.getLooper());
+        mAudioPlaybackHandler = new AudioPlaybackHandler();
+        mAudioPlaybackHandler.start();
 
         mCallbacks = new CallbackMap();
 
@@ -83,8 +110,8 @@
 
         // Tell the synthesizer to stop
         mSynthHandler.quit();
-        mAudioTrackHandler.getLooper().quit();
-
+        // Tell the audio playback thread to stop.
+        mAudioPlaybackHandler.quit();
         // Unregister all callbacks.
         mCallbacks.kill();
 
@@ -157,10 +184,12 @@
      *
      * Called on the synthesis thread.
      *
-     * @param request The synthesis request. The method should use the methods in the request
-     *         object to communicate the results of the synthesis.
+     * @param request The synthesis request.
+     * @param callback The callback the the engine must use to make data available for
+     *         playback or for writing to a file.
      */
-    protected abstract void onSynthesizeText(SynthesisRequest request);
+    protected abstract void onSynthesizeText(SynthesisRequest request,
+            SynthesisCallback callback);
 
     private boolean areDefaultsEnforced() {
         return getSecureSettingInt(Settings.Secure.TTS_USE_DEFAULTS,
@@ -236,13 +265,6 @@
             super(looper);
         }
 
-        private void dispatchUtteranceCompleted(SpeechItem item) {
-            String utteranceId = item.getUtteranceId();
-            if (!TextUtils.isEmpty(utteranceId)) {
-                mCallbacks.dispatchUtteranceCompleted(item.getCallingApp(), utteranceId);
-            }
-        }
-
         private synchronized SpeechItem getCurrentSpeechItem() {
             return mCurrentSpeechItem;
         }
@@ -276,19 +298,22 @@
             if (!speechItem.isValid()) {
                 return TextToSpeech.ERROR;
             }
-            // TODO: The old code also supported the undocumented queueMode == 2,
-            // which clears out all pending items from the calling app, as well as all
-            // non-file items from other apps.
+
             if (queueMode == TextToSpeech.QUEUE_FLUSH) {
                 stop(speechItem.getCallingApp());
+            } else if (queueMode == 2) {
+                // Stop the current speech item.
+                stop(speechItem.getCallingApp());
+                // Remove all other items from the queue.
+                removeCallbacksAndMessages(null);
+                // Remove all pending playback as well.
+                mAudioPlaybackHandler.removeAllItems();
             }
             Runnable runnable = new Runnable() {
                 @Override
                 public void run() {
                     setCurrentSpeechItem(speechItem);
-                    if (speechItem.play() == TextToSpeech.SUCCESS) {
-                        dispatchUtteranceCompleted(speechItem);
-                    }
+                    speechItem.play();
                     setCurrentSpeechItem(null);
                 }
             };
@@ -313,19 +338,28 @@
             if (TextUtils.isEmpty(callingApp)) {
                 return TextToSpeech.ERROR;
             }
+
             removeCallbacksAndMessages(callingApp);
             SpeechItem current = setCurrentSpeechItem(null);
             if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
                 current.stop();
             }
+
+            // Remove any enqueued audio too.
+            mAudioPlaybackHandler.removePlaybackItems(callingApp);
+
             return TextToSpeech.SUCCESS;
         }
     }
 
+    interface UtteranceCompletedDispatcher {
+        public void dispatchUtteranceCompleted();
+    }
+
     /**
      * An item in the synth thread queue.
      */
-    private static abstract class SpeechItem {
+    private abstract class SpeechItem implements UtteranceCompletedDispatcher {
         private final String mCallingApp;
         protected final Bundle mParams;
         private boolean mStarted = false;
@@ -380,6 +414,13 @@
             stopImpl();
         }
 
+        public void dispatchUtteranceCompleted() {
+            final String utteranceId = getUtteranceId();
+            if (!TextUtils.isEmpty(utteranceId)) {
+                mCallbacks.dispatchUtteranceCompleted(getCallingApp(), utteranceId);
+            }
+        }
+
         protected abstract int playImpl();
 
         protected abstract void stopImpl();
@@ -413,13 +454,18 @@
         }
     }
 
-    private class SynthesisSpeechItem extends SpeechItem {
+    class SynthesisSpeechItem extends SpeechItem {
         private final String mText;
-        private SynthesisRequest mSynthesisRequest;
+        private final SynthesisRequest mSynthesisRequest;
+        // Non null after synthesis has started, and all accesses
+        // guarded by 'this'.
+        private AbstractSynthesisCallback mSynthesisCallback;
 
         public SynthesisSpeechItem(String callingApp, Bundle params, String text) {
             super(callingApp, params);
             mText = text;
+            mSynthesisRequest = new SynthesisRequest(mText, mParams);
+            setRequestParams(mSynthesisRequest);
         }
 
         public String getText() {
@@ -441,19 +487,18 @@
 
         @Override
         protected int playImpl() {
-            SynthesisRequest synthesisRequest;
+            AbstractSynthesisCallback synthesisCallback;
             synchronized (this) {
-                mSynthesisRequest = createSynthesisRequest();
-                synthesisRequest = mSynthesisRequest;
+                mSynthesisCallback = createSynthesisCallback();
+                synthesisCallback = mSynthesisCallback;
             }
-            setRequestParams(synthesisRequest);
-            TextToSpeechService.this.onSynthesizeText(synthesisRequest);
-            return synthesisRequest.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
+            TextToSpeechService.this.onSynthesizeText(mSynthesisRequest, synthesisCallback);
+            return synthesisCallback.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
         }
 
-        protected SynthesisRequest createSynthesisRequest() {
-            return new PlaybackSynthesisRequest(mText, mParams,
-                    getStreamType(), getVolume(), getPan(), mAudioTrackHandler);
+        protected AbstractSynthesisCallback createSynthesisCallback() {
+            return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(),
+                    mAudioPlaybackHandler, this, getCallingApp());
         }
 
         private void setRequestParams(SynthesisRequest request) {
@@ -469,11 +514,11 @@
 
         @Override
         protected void stopImpl() {
-            SynthesisRequest synthesisRequest;
+            AbstractSynthesisCallback synthesisCallback;
             synchronized (this) {
-                synthesisRequest = mSynthesisRequest;
+                synthesisCallback = mSynthesisCallback;
             }
-            synthesisRequest.stop();
+            synthesisCallback.stop();
             TextToSpeechService.this.onStop();
         }
 
@@ -522,8 +567,17 @@
         }
 
         @Override
-        protected SynthesisRequest createSynthesisRequest() {
-            return new FileSynthesisRequest(getText(), mParams, mFile);
+        protected AbstractSynthesisCallback createSynthesisCallback() {
+            return new FileSynthesisCallback(mFile);
+        }
+
+        @Override
+        protected int playImpl() {
+            int status = super.playImpl();
+            if (status == TextToSpeech.SUCCESS) {
+                dispatchUtteranceCompleted();
+            }
+            return status;
         }
 
         /**
@@ -557,6 +611,7 @@
     private class AudioSpeechItem extends SpeechItem {
 
         private final BlockingMediaPlayer mPlayer;
+        private AudioMessageParams mToken;
 
         public AudioSpeechItem(String callingApp, Bundle params, Uri uri) {
             super(callingApp, params);
@@ -570,23 +625,26 @@
 
         @Override
         protected int playImpl() {
-            return mPlayer.startAndWait() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
+            mToken = new AudioMessageParams(this, getCallingApp(), mPlayer);
+            mAudioPlaybackHandler.enqueueAudio(mToken);
+            return TextToSpeech.SUCCESS;
         }
 
         @Override
         protected void stopImpl() {
-            mPlayer.stop();
+            if (mToken != null) {
+                mAudioPlaybackHandler.stop(mToken);
+            }
         }
     }
 
     private class SilenceSpeechItem extends SpeechItem {
         private final long mDuration;
-        private final ConditionVariable mDone;
+        private SilenceMessageParams mToken;
 
         public SilenceSpeechItem(String callingApp, Bundle params, long duration) {
             super(callingApp, params);
             mDuration = duration;
-            mDone = new ConditionVariable();
         }
 
         @Override
@@ -596,13 +654,16 @@
 
         @Override
         protected int playImpl() {
-            boolean aborted = mDone.block(mDuration);
-            return aborted ? TextToSpeech.ERROR : TextToSpeech.SUCCESS;
+            mToken = new SilenceMessageParams(this, getCallingApp(), mDuration);
+            mAudioPlaybackHandler.enqueueSilence(mToken);
+            return TextToSpeech.SUCCESS;
         }
 
         @Override
         protected void stopImpl() {
-            mDone.open();
+            if (mToken != null) {
+                mAudioPlaybackHandler.stop(mToken);
+            }
         }
     }
 
diff --git a/core/java/android/util/CalendarUtils.java b/core/java/android/util/CalendarUtils.java
deleted file mode 100644
index b2b4897..0000000
--- a/core/java/android/util/CalendarUtils.java
+++ /dev/null
@@ -1,348 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.util;
-
-import android.content.AsyncQueryHandler;
-import android.content.ContentResolver;
-import android.content.ContentValues;
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.database.Cursor;
-import android.provider.Calendar.CalendarCache;
-import android.text.TextUtils;
-import android.text.format.DateUtils;
-import android.text.format.Time;
-
-import java.util.Formatter;
-import java.util.HashSet;
-import java.util.Locale;
-
-/**
- * A class containing utility methods related to Calendar apps.
- *
- * @hide
- */
-public class CalendarUtils {
-    private static final boolean DEBUG = false;
-    private static final String TAG = "CalendarUtils";
-
-    /**
-     * This class contains methods specific to reading and writing time zone
-     * values.
-     */
-    public static class TimeZoneUtils {
-        private static final String[] TIMEZONE_TYPE_ARGS = { CalendarCache.TIMEZONE_KEY_TYPE };
-        private static final String[] TIMEZONE_INSTANCES_ARGS =
-                { CalendarCache.TIMEZONE_KEY_INSTANCES };
-
-        private static StringBuilder mSB = new StringBuilder(50);
-        private static Formatter mF = new Formatter(mSB, Locale.getDefault());
-        private volatile static boolean mFirstTZRequest = true;
-        private volatile static boolean mTZQueryInProgress = false;
-
-        private volatile static boolean mUseHomeTZ = false;
-        private volatile static String mHomeTZ = Time.getCurrentTimezone();
-
-        private static HashSet<Runnable> mTZCallbacks = new HashSet<Runnable>();
-        private static int mToken = 1;
-        private static AsyncTZHandler mHandler;
-
-        // The name of the shared preferences file. This name must be maintained for historical
-        // reasons, as it's what PreferenceManager assigned the first time the file was created.
-        private final String mPrefsName;
-
-        /**
-         * This is the key used for writing whether or not a home time zone should
-         * be used in the Calendar app to the Calendar Preferences.
-         */
-        public static final String KEY_HOME_TZ_ENABLED = "preferences_home_tz_enabled";
-        /**
-         * This is the key used for writing the time zone that should be used if
-         * home time zones are enabled for the Calendar app.
-         */
-        public static final String KEY_HOME_TZ = "preferences_home_tz";
-
-        /**
-         * This is a helper class for handling the async queries and updates for the
-         * time zone settings in Calendar.
-         */
-        private class AsyncTZHandler extends AsyncQueryHandler {
-            public AsyncTZHandler(ContentResolver cr) {
-                super(cr);
-            }
-
-            @Override
-            protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
-                synchronized (mTZCallbacks) {
-                    if (cursor == null) {
-                        mTZQueryInProgress = false;
-                        mFirstTZRequest = true;
-                        return;
-                    }
-
-                    boolean writePrefs = false;
-                    // Check the values in the db
-                    int keyColumn = cursor.getColumnIndexOrThrow(CalendarCache.KEY);
-                    int valueColumn = cursor.getColumnIndexOrThrow(CalendarCache.VALUE);
-                    while(cursor.moveToNext()) {
-                        String key = cursor.getString(keyColumn);
-                        String value = cursor.getString(valueColumn);
-                        if (TextUtils.equals(key, CalendarCache.TIMEZONE_KEY_TYPE)) {
-                            boolean useHomeTZ = !TextUtils.equals(
-                                    value, CalendarCache.TIMEZONE_TYPE_AUTO);
-                            if (useHomeTZ != mUseHomeTZ) {
-                                writePrefs = true;
-                                mUseHomeTZ = useHomeTZ;
-                            }
-                        } else if (TextUtils.equals(
-                                key, CalendarCache.TIMEZONE_KEY_INSTANCES_PREVIOUS)) {
-                            if (!TextUtils.isEmpty(value) && !TextUtils.equals(mHomeTZ, value)) {
-                                writePrefs = true;
-                                mHomeTZ = value;
-                            }
-                        }
-                    }
-                    cursor.close();
-                    if (writePrefs) {
-                        SharedPreferences prefs = getSharedPreferences((Context)cookie, mPrefsName);
-                        // Write the prefs
-                        setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ);
-                        setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ);
-                    }
-
-                    mTZQueryInProgress = false;
-                    for (Runnable callback : mTZCallbacks) {
-                        if (callback != null) {
-                            callback.run();
-                        }
-                    }
-                    mTZCallbacks.clear();
-                }
-            }
-        }
-
-        /**
-         * The name of the file where the shared prefs for Calendar are stored
-         * must be provided. All activities within an app should provide the
-         * same preferences name or behavior may become erratic.
-         *
-         * @param prefsName
-         */
-        public TimeZoneUtils(String prefsName) {
-            mPrefsName = prefsName;
-        }
-
-        /**
-         * Formats a date or a time range according to the local conventions.
-         *
-         * This formats a date/time range using Calendar's time zone and the
-         * local conventions for the region of the device.
-         *
-         * If the {@link DateUtils#FORMAT_UTC} flag is used it will pass in
-         * the UTC time zone instead.
-         *
-         * @param context the context is required only if the time is shown
-         * @param startMillis the start time in UTC milliseconds
-         * @param endMillis the end time in UTC milliseconds
-         * @param flags a bit mask of options See
-         * {@link DateUtils#formatDateRange(Context, Formatter, long, long, int, String) formatDateRange}
-         * @return a string containing the formatted date/time range.
-         */
-        public String formatDateRange(Context context, long startMillis,
-                long endMillis, int flags) {
-            String date;
-            String tz;
-            if ((flags & DateUtils.FORMAT_UTC) != 0) {
-                tz = Time.TIMEZONE_UTC;
-            } else {
-                tz = getTimeZone(context, null);
-            }
-            synchronized (mSB) {
-                mSB.setLength(0);
-                date = DateUtils.formatDateRange(context, mF, startMillis, endMillis, flags,
-                        tz).toString();
-            }
-            return date;
-        }
-
-        /**
-         * Writes a new home time zone to the db.
-         *
-         * Updates the home time zone in the db asynchronously and updates
-         * the local cache. Sending a time zone of
-         * {@link CalendarCache#TIMEZONE_TYPE_AUTO} will cause it to be set
-         * to the device's time zone. null or empty tz will be ignored.
-         *
-         * @param context The calling activity
-         * @param timeZone The time zone to set Calendar to, or
-         * {@link CalendarCache#TIMEZONE_TYPE_AUTO}
-         */
-        public void setTimeZone(Context context, String timeZone) {
-            if (TextUtils.isEmpty(timeZone)) {
-                if (DEBUG) {
-                    Log.d(TAG, "Empty time zone, nothing to be done.");
-                }
-                return;
-            }
-            boolean updatePrefs = false;
-            synchronized (mTZCallbacks) {
-                if (CalendarCache.TIMEZONE_TYPE_AUTO.equals(timeZone)) {
-                    if (mUseHomeTZ) {
-                        updatePrefs = true;
-                    }
-                    mUseHomeTZ = false;
-                } else {
-                    if (!mUseHomeTZ || !TextUtils.equals(mHomeTZ, timeZone)) {
-                        updatePrefs = true;
-                    }
-                    mUseHomeTZ = true;
-                    mHomeTZ = timeZone;
-                }
-            }
-            if (updatePrefs) {
-                // Write the prefs
-                SharedPreferences prefs = getSharedPreferences(context, mPrefsName);
-                setSharedPreference(prefs, KEY_HOME_TZ_ENABLED, mUseHomeTZ);
-                setSharedPreference(prefs, KEY_HOME_TZ, mHomeTZ);
-
-                // Update the db
-                ContentValues values = new ContentValues();
-                if (mHandler != null) {
-                    mHandler.cancelOperation(mToken);
-                }
-
-                mHandler = new AsyncTZHandler(context.getContentResolver());
-
-                // skip 0 so query can use it
-                if (++mToken == 0) {
-                    mToken = 1;
-                }
-
-                // Write the use home tz setting
-                values.put(CalendarCache.VALUE, mUseHomeTZ ? CalendarCache.TIMEZONE_TYPE_HOME
-                        : CalendarCache.TIMEZONE_TYPE_AUTO);
-                mHandler.startUpdate(mToken, null, CalendarCache.URI, values, CalendarCache.WHERE,
-                        TIMEZONE_TYPE_ARGS);
-
-                // If using a home tz write it to the db
-                if (mUseHomeTZ) {
-                    ContentValues values2 = new ContentValues();
-                    values2.put(CalendarCache.VALUE, mHomeTZ);
-                    mHandler.startUpdate(mToken, null, CalendarCache.URI, values2,
-                            CalendarCache.WHERE, TIMEZONE_INSTANCES_ARGS);
-                }
-            }
-        }
-
-        /**
-         * Gets the time zone that Calendar should be displayed in
-         *
-         * This is a helper method to get the appropriate time zone for Calendar. If this
-         * is the first time this method has been called it will initiate an asynchronous
-         * query to verify that the data in preferences is correct. The callback supplied
-         * will only be called if this query returns a value other than what is stored in
-         * preferences and should cause the calling activity to refresh anything that
-         * depends on calling this method.
-         *
-         * @param context The calling activity
-         * @param callback The runnable that should execute if a query returns new values
-         * @return The string value representing the time zone Calendar should display
-         */
-        public String getTimeZone(Context context, Runnable callback) {
-            synchronized (mTZCallbacks){
-                if (mFirstTZRequest) {
-                    mTZQueryInProgress = true;
-                    mFirstTZRequest = false;
-
-                    SharedPreferences prefs = getSharedPreferences(context, mPrefsName);
-                    mUseHomeTZ = prefs.getBoolean(KEY_HOME_TZ_ENABLED, false);
-                    mHomeTZ = prefs.getString(KEY_HOME_TZ, Time.getCurrentTimezone());
-
-                    // When the async query returns it should synchronize on
-                    // mTZCallbacks, update mUseHomeTZ, mHomeTZ, and the
-                    // preferences, set mTZQueryInProgress to false, and call all
-                    // the runnables in mTZCallbacks.
-                    if (mHandler == null) {
-                        mHandler = new AsyncTZHandler(context.getContentResolver());
-                    }
-                    mHandler.startQuery(0, context, CalendarCache.URI, CalendarCache.POJECTION,
-                            null, null, null);
-                }
-                if (mTZQueryInProgress) {
-                    mTZCallbacks.add(callback);
-                }
-            }
-            return mUseHomeTZ ? mHomeTZ : Time.getCurrentTimezone();
-        }
-
-        /**
-         * Forces a query of the database to check for changes to the time zone.
-         * This should be called if another app may have modified the db. If a
-         * query is already in progress the callback will be added to the list
-         * of callbacks to be called when it returns.
-         *
-         * @param context The calling activity
-         * @param callback The runnable that should execute if a query returns
-         *            new values
-         */
-        public void forceDBRequery(Context context, Runnable callback) {
-            synchronized (mTZCallbacks){
-                if (mTZQueryInProgress) {
-                    mTZCallbacks.add(callback);
-                    return;
-                }
-                mFirstTZRequest = true;
-                getTimeZone(context, callback);
-            }
-        }
-    }
-
-        /**
-         * A helper method for writing a String value to the preferences
-         * asynchronously.
-         *
-         * @param context A context with access to the correct preferences
-         * @param key The preference to write to
-         * @param value The value to write
-         */
-        public static void setSharedPreference(SharedPreferences prefs, String key, String value) {
-//            SharedPreferences prefs = getSharedPreferences(context);
-            SharedPreferences.Editor editor = prefs.edit();
-            editor.putString(key, value);
-            editor.apply();
-        }
-
-        /**
-         * A helper method for writing a boolean value to the preferences
-         * asynchronously.
-         *
-         * @param context A context with access to the correct preferences
-         * @param key The preference to write to
-         * @param value The value to write
-         */
-        public static void setSharedPreference(SharedPreferences prefs, String key, boolean value) {
-//            SharedPreferences prefs = getSharedPreferences(context, prefsName);
-            SharedPreferences.Editor editor = prefs.edit();
-            editor.putBoolean(key, value);
-            editor.apply();
-        }
-
-        /** Return a properly configured SharedPreferences instance */
-        public static SharedPreferences getSharedPreferences(Context context, String prefsName) {
-            return context.getSharedPreferences(prefsName, Context.MODE_PRIVATE);
-        }
-}
diff --git a/core/java/android/util/FinitePool.java b/core/java/android/util/FinitePool.java
index 3ef8293..4ae21ad 100644
--- a/core/java/android/util/FinitePool.java
+++ b/core/java/android/util/FinitePool.java
@@ -69,6 +69,7 @@
 
         if (element != null) {
             element.setNextPoolable(null);
+            element.setPooled(false);
             mManager.onAcquired(element);            
         }
 
@@ -76,9 +77,13 @@
     }
 
     public void release(T element) {
+        if (element.isPooled()) {
+            throw new IllegalArgumentException("Element already in the pool.");
+        }
         if (mInfinite || mPoolCount < mLimit) {
             mPoolCount++;
             element.setNextPoolable(mRoot);
+            element.setPooled(true);
             mRoot = element;
         }
         mManager.onReleased(element);
diff --git a/core/java/android/util/FloatProperty.java b/core/java/android/util/FloatProperty.java
new file mode 100644
index 0000000..a67b3cb
--- /dev/null
+++ b/core/java/android/util/FloatProperty.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.util.Property;
+
+/**
+ * An implementation of {@link android.util.Property} to be used specifically with fields of type
+ * <code>float</code>. This type-specific subclass enables performance benefit by allowing
+ * calls to a {@link #set(Object, Float) set()} function that takes the primitive
+ * <code>float</code> type and avoids autoboxing and other overhead associated with the
+ * <code>Float</code> class.
+ *
+ * @param <T> The class on which the Property is declared.
+ *
+ * @hide
+ */
+public abstract class FloatProperty<T> extends Property<T, Float> {
+
+    public FloatProperty(String name) {
+        super(Float.class, name);
+    }
+
+    /**
+     * A type-specific override of the {@link #set(Object, Float)} that is faster when dealing
+     * with fields of type <code>float</code>.
+     */
+    public abstract void setValue(T object, float value);
+
+    @Override
+    final public void set(T object, Float value) {
+        setValue(object, value);
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/android/util/IntProperty.java b/core/java/android/util/IntProperty.java
new file mode 100644
index 0000000..459d6b2
--- /dev/null
+++ b/core/java/android/util/IntProperty.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import android.util.Property;
+
+/**
+ * An implementation of {@link android.util.Property} to be used specifically with fields of type
+ * <code>int</code>. This type-specific subclass enables performance benefit by allowing
+ * calls to a {@link #set(Object, Integer) set()} function that takes the primitive
+ * <code>int</code> type and avoids autoboxing and other overhead associated with the
+ * <code>Integer</code> class.
+ *
+ * @param <T> The class on which the Property is declared.
+ *
+ * @hide
+ */
+public abstract class IntProperty<T> extends Property<T, Integer> {
+
+    public IntProperty(String name) {
+        super(Integer.class, name);
+    }
+
+    /**
+     * A type-specific override of the {@link #set(Object, Integer)} that is faster when dealing
+     * with fields of type <code>int</code>.
+     */
+    public abstract void setValue(T object, int value);
+
+    @Override
+    final public void set(T object, Integer value) {
+        set(object, value.intValue());
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/android/util/NoSuchPropertyException.java b/core/java/android/util/NoSuchPropertyException.java
new file mode 100644
index 0000000..b93f983
--- /dev/null
+++ b/core/java/android/util/NoSuchPropertyException.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+/**
+ * Thrown when code requests a {@link Property} on a class that does
+ * not expose the appropriate method or field.
+ *
+ * @see Property#of(java.lang.Class, java.lang.Class, java.lang.String)
+ */
+public class NoSuchPropertyException extends RuntimeException {
+
+    public NoSuchPropertyException(String s) {
+        super(s);
+    }
+
+}
diff --git a/core/java/android/util/Poolable.java b/core/java/android/util/Poolable.java
index fd9bd9b..87e0529 100644
--- a/core/java/android/util/Poolable.java
+++ b/core/java/android/util/Poolable.java
@@ -22,4 +22,6 @@
 public interface Poolable<T> {
     void setNextPoolable(T element);
     T getNextPoolable();
+    boolean isPooled();
+    void setPooled(boolean isPooled);
 }
diff --git a/core/java/android/util/Property.java b/core/java/android/util/Property.java
new file mode 100644
index 0000000..458e4c3
--- /dev/null
+++ b/core/java/android/util/Property.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+
+/**
+ * A property is an abstraction that can be used to represent a <emb>mutable</em> value that is held
+ * in a <em>host</em> object. The Property's {@link #set(Object, Object)} or {@link #get(Object)}
+ * methods can be implemented in terms of the private fields of the host object, or via "setter" and
+ * "getter" methods or by some other mechanism, as appropriate.
+ *
+ * @param <T> The class on which the property is declared.
+ * @param <V> The type that this property represents.
+ */
+public abstract class Property<T, V> {
+
+    private final String mName;
+    private final Class<V> mType;
+
+    /**
+     * This factory method creates and returns a Property given the <code>class</code> and
+     * <code>name</code> parameters, where the <code>"name"</code> parameter represents either:
+     * <ul>
+     *     <li>a public <code>getName()</code> method on the class which takes no arguments, plus an
+     *     optional public <code>setName()</code> method which takes a value of the same type
+     *     returned by <code>getName()</code>
+     *     <li>a public <code>isName()</code> method on the class which takes no arguments, plus an
+     *     optional public <code>setName()</code> method which takes a value of the same type
+     *     returned by <code>isName()</code>
+     *     <li>a public <code>name</code> field on the class
+     * </ul>
+     *
+     * <p>If either of the get/is method alternatives is found on the class, but an appropriate
+     * <code>setName()</code> method is not found, the <code>Property</code> will be
+     * {@link #isReadOnly() readOnly}. Calling the {@link #set(Object, Object)} method on such
+     * a property is allowed, but will have no effect.</p>
+     *
+     * <p>If neither the methods nor the field are found on the class a
+     * {@link NoSuchPropertyException} exception will be thrown.</p>
+     */
+    public static <T, V> Property<T, V> of(Class<T> hostType, Class<V> valueType, String name) {
+        return new ReflectiveProperty<T, V>(hostType, valueType, name);
+    }
+
+    /**
+     * A constructor that takes an identifying name and {@link #getType() type} for the property.
+     */
+    public Property(Class<V> type, String name) {
+        mName = name;
+        mType = type;
+    }
+
+    /**
+     * Returns true if the {@link #set(Object, Object)} method does not set the value on the target
+     * object (in which case the {@link #set(Object, Object) set()} method should throw a {@link
+     * NoSuchPropertyException} exception). This may happen if the Property wraps functionality that
+     * allows querying the underlying value but not setting it. For example, the {@link #of(Class,
+     * Class, String)} factory method may return a Property with name "foo" for an object that has
+     * only a <code>getFoo()</code> or <code>isFoo()</code> method, but no matching
+     * <code>setFoo()</code> method.
+     */
+    public boolean isReadOnly() {
+        return false;
+    }
+
+    /**
+     * Sets the value on <code>object</code> which this property represents. If the method is unable
+     * to set the value on the target object, it will throw a {@link NoSuchPropertyException}
+     * exception.
+     */
+    public void set(T object, V value) {
+        throw new NoSuchPropertyException("Property is read-only; set() is not implemented");
+    }
+
+    /**
+     * Returns the current value that this property represents on the given <code>object</code>.
+     */
+    public abstract V get(T object);
+
+    /**
+     * Returns the name for this property.
+     */
+    public String getName() {
+        return mName;
+    }
+
+    /**
+     * Returns the type for this property.
+     */
+    public Class<V> getType() {
+        return mType;
+    }
+}
diff --git a/core/java/android/util/ReflectiveProperty.java b/core/java/android/util/ReflectiveProperty.java
new file mode 100644
index 0000000..6832240
--- /dev/null
+++ b/core/java/android/util/ReflectiveProperty.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.util;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * Internal class to automatically generate a Property for a given class/name pair, given the
+ * specification of {@link Property#of(java.lang.Class, java.lang.Class, java.lang.String)}
+ */
+class ReflectiveProperty<T, V> extends Property<T, V> {
+
+    private static final String PREFIX_GET = "get";
+    private static final String PREFIX_IS = "is";
+    private static final String PREFIX_SET = "set";
+    private Method mSetter;
+    private Method mGetter;
+    private Field mField;
+
+    /**
+     * For given property name 'name', look for getName/isName method or 'name' field.
+     * Also look for setName method (optional - could be readonly). Failing method getters and
+     * field results in throwing NoSuchPropertyException.
+     *
+     * @param propertyHolder The class on which the methods or field are found
+     * @param name The name of the property, where this name is capitalized and appended to
+     * "get" and "is to search for the appropriate methods. If the get/is methods are not found,
+     * the constructor will search for a field with that exact name.
+     */
+    public ReflectiveProperty(Class<T> propertyHolder, Class<V> valueType, String name) {
+         // TODO: cache reflection info for each new class/name pair
+        super(valueType, name);
+        char firstLetter = Character.toUpperCase(name.charAt(0));
+        String theRest = name.substring(1);
+        String capitalizedName = firstLetter + theRest;
+        String getterName = PREFIX_GET + capitalizedName;
+        try {
+            mGetter = propertyHolder.getMethod(getterName, (Class<?>[])null);
+        } catch (NoSuchMethodException e) {
+            // getName() not available - try isName() instead
+            getterName = PREFIX_IS + capitalizedName;
+            try {
+                mGetter = propertyHolder.getMethod(getterName, (Class<?>[])null);
+            } catch (NoSuchMethodException e1) {
+                // Try public field instead
+                try {
+                    mField = propertyHolder.getField(name);
+                    Class fieldType = mField.getType();
+                    if (!typesMatch(valueType, fieldType)) {
+                        throw new NoSuchPropertyException("Underlying type (" + fieldType + ") " +
+                                "does not match Property type (" + valueType + ")");
+                    }
+                    return;
+                } catch (NoSuchFieldException e2) {
+                    // no way to access property - throw appropriate exception
+                    throw new NoSuchPropertyException("No accessor method or field found for"
+                            + " property with name " + name);
+                }
+            }
+        }
+        Class getterType = mGetter.getReturnType();
+        // Check to make sure our getter type matches our valueType
+        if (!typesMatch(valueType, getterType)) {
+            throw new NoSuchPropertyException("Underlying type (" + getterType + ") " +
+                    "does not match Property type (" + valueType + ")");
+        }
+        String setterName = PREFIX_SET + capitalizedName;
+        try {
+            mSetter = propertyHolder.getMethod(setterName, getterType);
+        } catch (NoSuchMethodException ignored) {
+            // Okay to not have a setter - just a readonly property
+        }
+    }
+
+    /**
+     * Utility method to check whether the type of the underlying field/method on the target
+     * object matches the type of the Property. The extra checks for primitive types are because
+     * generics will force the Property type to be a class, whereas the type of the underlying
+     * method/field will probably be a primitive type instead. Accept float as matching Float,
+     * etc.
+     */
+    private boolean typesMatch(Class<V> valueType, Class getterType) {
+        if (getterType != valueType) {
+            if (getterType.isPrimitive()) {
+                return (getterType == float.class && valueType == Float.class) ||
+                        (getterType == int.class && valueType == Integer.class) ||
+                        (getterType == boolean.class && valueType == Boolean.class) ||
+                        (getterType == long.class && valueType == Long.class) ||
+                        (getterType == double.class && valueType == Double.class) ||
+                        (getterType == short.class && valueType == Short.class) ||
+                        (getterType == byte.class && valueType == Byte.class) ||
+                        (getterType == char.class && valueType == Character.class);
+            }
+            return false;
+        }
+        return true;
+    }
+
+    @Override
+    public void set(T object, V value) {
+        if (mSetter != null) {
+            try {
+                mSetter.invoke(object, value);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError();
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        } else if (mField != null) {
+            try {
+                mField.set(object, value);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError();
+            }
+        } else {
+            throw new UnsupportedOperationException("Property " + getName() +" is read-only");
+        }
+    }
+
+    @Override
+    public V get(T object) {
+        if (mGetter != null) {
+            try {
+                return (V) mGetter.invoke(object, (Object[])null);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError();
+            } catch (InvocationTargetException e) {
+                throw new RuntimeException(e.getCause());
+            }
+        } else if (mField != null) {
+            try {
+                return (V) mField.get(object);
+            } catch (IllegalAccessException e) {
+                throw new AssertionError();
+            }
+        }
+        // Should not get here: there should always be a non-null getter or field
+        throw new AssertionError();
+    }
+
+    /**
+     * Returns false if there is no setter or public field underlying this Property.
+     */
+    @Override
+    public boolean isReadOnly() {
+        return (mSetter == null && mField == null);
+    }
+}
diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java
index 2603281..ec94fe7 100644
--- a/core/java/android/view/GLES20RecordingCanvas.java
+++ b/core/java/android/view/GLES20RecordingCanvas.java
@@ -25,7 +25,7 @@
 import android.graphics.RectF;
 import android.graphics.Shader;
 
-import java.util.HashSet;
+import java.util.ArrayList;
 
 /**
  * An implementation of a GL canvas that records drawing operations.
@@ -37,7 +37,7 @@
     // These lists ensure that any Bitmaps recorded by a DisplayList are kept alive as long
     // as the DisplayList is alive.
     @SuppressWarnings({"MismatchedQueryAndUpdateOfCollection"})
-    private final HashSet<Bitmap> mBitmaps = new HashSet<Bitmap>();
+    private final ArrayList<Bitmap> mBitmaps = new ArrayList<Bitmap>(5);
 
     GLES20RecordingCanvas(boolean translucent) {
         super(true, translucent);
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 1ccc66f..a496a9e 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -610,6 +610,8 @@
         mVelocityTracker = null;
         mIsDoubleTapping = false;
         mStillDown = false;
+        mAlwaysInTapRegion = false;
+        mAlwaysInBiggerTapRegion = false;
         if (mInLongPress) {
             mInLongPress = false;
         }
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index b2a35d3..69e6489 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -81,8 +81,10 @@
      *  horizontal axis. */
     public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT;
 
-    /** Raw bit controlling whether the horizontal direction is relative (before/after) or not. */
-    public static final int RELATIVE_HORIZONTAL_DIRECTION = 0x00800000;
+    /** Raw bit controlling whether the layout direction is relative or not (START/END instead of
+     * absolute LEFT/RIGHT).
+     */
+    public static final int RELATIVE_LAYOUT_DIRECTION = 0x00800000;
 
     /**
      * Binary mask to get the absolute horizontal gravity of a gravity.
@@ -109,16 +111,16 @@
      */
     public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;
     
-    /** Push object to x-axis position before its container, not changing its size. */
-    public static final int BEFORE = RELATIVE_HORIZONTAL_DIRECTION | LEFT;
+    /** Push object to x-axis position at the start of its container, not changing its size. */
+    public static final int START = RELATIVE_LAYOUT_DIRECTION | LEFT;
 
-    /** Push object to x-axis position after its container, not changing its size. */
-    public static final int AFTER = RELATIVE_HORIZONTAL_DIRECTION | RIGHT;
+    /** Push object to x-axis position at the end of its container, not changing its size. */
+    public static final int END = RELATIVE_LAYOUT_DIRECTION | RIGHT;
 
     /**
      * Binary mask for the horizontal gravity and script specific direction bit.
      */
-    public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = BEFORE | AFTER;
+    public static final int RELATIVE_HORIZONTAL_GRAVITY_MASK = START | END;
 
     /**
      * Apply a gravity constant to an object. This suppose that the layout direction is LTR.
@@ -342,8 +344,8 @@
     /**
      * <p>Convert script specific gravity to absolute horizontal value.</p>
      *
-     * if horizontal direction is LTR, then BEFORE will set LEFT and AFTER will set RIGHT.
-     * if horizontal direction is RTL, then BEFORE will set RIGHT and AFTER will set LEFT.
+     * if horizontal direction is LTR, then START will set LEFT and END will set RIGHT.
+     * if horizontal direction is RTL, then START will set RIGHT and END will set LEFT.
      *
      * @param gravity The gravity to convert to absolute (horizontal) values.
      * @param isRtl Whether the layout is right-to-left.
@@ -351,11 +353,11 @@
      */
     public static int getAbsoluteGravity(int gravity, boolean isRtl) {
         int result = gravity;
-        // If layout is script specific and gravity is horizontal relative (BEFORE or AFTER)
-        if ((result & RELATIVE_HORIZONTAL_DIRECTION) > 0) {
-            if ((result & Gravity.BEFORE) == Gravity.BEFORE) {
-                // Remove the BEFORE bit
-                result &= ~BEFORE;
+        // If layout is script specific and gravity is horizontal relative (START or END)
+        if ((result & RELATIVE_LAYOUT_DIRECTION) > 0) {
+            if ((result & Gravity.START) == Gravity.START) {
+                // Remove the START bit
+                result &= ~START;
                 if (isRtl) {
                     // Set the RIGHT bit
                     result |= RIGHT;
@@ -363,9 +365,9 @@
                     // Set the LEFT bit
                     result |= LEFT;
                 }
-            } else if ((result & Gravity.AFTER) == Gravity.AFTER) {
-                // Remove the AFTER bit
-                result &= ~AFTER;
+            } else if ((result & Gravity.END) == Gravity.END) {
+                // Remove the END bit
+                result &= ~END;
                 if (isRtl) {
                     // Set the LEFT bit
                     result |= LEFT;
@@ -376,7 +378,7 @@
             }
             // Don't need the script specific bit any more, so remove it as we are converting to
             // absolute values (LEFT or RIGHT)
-            result &= ~RELATIVE_HORIZONTAL_DIRECTION;
+            result &= ~RELATIVE_LAYOUT_DIRECTION;
         }
         return result;
     }
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 5e07e1a..5d2c1a7 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -340,6 +340,15 @@
                         // Set focus point to the remaining finger
                         final int index = event.findPointerIndex(actionId == mActiveId0 ?
                                 mActiveId1 : mActiveId0);
+                        if (index < 0) {
+                            mInvalidGesture = true;
+                            Log.e(TAG, "Invalid MotionEvent stream detected.", new Throwable());
+                            if (mGestureInProgress) {
+                                mListener.onScaleEnd(this);
+                            }
+                            return false;
+                        }
+
                         mActiveId0 = event.getPointerId(index);
 
                         mActive0MostRecent = true;
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
index fccef2b..5a91d31 100644
--- a/core/java/android/view/VelocityTracker.java
+++ b/core/java/android/view/VelocityTracker.java
@@ -50,6 +50,7 @@
 
     private int mPtr;
     private VelocityTracker mNext;
+    private boolean mIsPooled;
 
     private static native int nativeInitialize();
     private static native void nativeDispose(int ptr);
@@ -93,6 +94,20 @@
         return mNext;
     }
 
+    /**
+     * @hide
+     */
+    public boolean isPooled() {
+        return mIsPooled;
+    }
+
+    /**
+     * @hide
+     */
+    public void setPooled(boolean isPooled) {
+        mIsPooled = isPooled;
+    }
+
     private VelocityTracker() {
         mPtr = nativeInitialize();
     }
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 014f19f..74926bb 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -16,6 +16,8 @@
 
 package android.view;
 
+import android.util.FloatProperty;
+import android.util.Property;
 import com.android.internal.R;
 import com.android.internal.util.Predicate;
 import com.android.internal.view.menu.MenuBuilder;
@@ -61,6 +63,7 @@
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityEventSource;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.EditorInfo;
@@ -950,51 +953,51 @@
 
     /**
      * Horizontal direction of this view is from Left to Right.
-     * Use with {@link #setHorizontalDirection}.
+     * Use with {@link #setLayoutDirection}.
      * {@hide}
      */
-    public static final int HORIZONTAL_DIRECTION_LTR = 0x00000000;
+    public static final int LAYOUT_DIRECTION_LTR = 0x00000000;
 
     /**
      * Horizontal direction of this view is from Right to Left.
-     * Use with {@link #setHorizontalDirection}.
+     * Use with {@link #setLayoutDirection}.
      * {@hide}
      */
-    public static final int HORIZONTAL_DIRECTION_RTL = 0x40000000;
+    public static final int LAYOUT_DIRECTION_RTL = 0x40000000;
 
     /**
      * Horizontal direction of this view is inherited from its parent.
-     * Use with {@link #setHorizontalDirection}.
+     * Use with {@link #setLayoutDirection}.
      * {@hide}
      */
-    public static final int HORIZONTAL_DIRECTION_INHERIT = 0x80000000;
+    public static final int LAYOUT_DIRECTION_INHERIT = 0x80000000;
 
     /**
      * Horizontal direction of this view is from deduced from the default language
-     * script for the locale. Use with {@link #setHorizontalDirection}.
+     * script for the locale. Use with {@link #setLayoutDirection}.
      * {@hide}
      */
-    public static final int HORIZONTAL_DIRECTION_LOCALE = 0xC0000000;
+    public static final int LAYOUT_DIRECTION_LOCALE = 0xC0000000;
 
     /**
      * Mask for use with setFlags indicating bits used for horizontalDirection.
      * {@hide}
      */
-    static final int HORIZONTAL_DIRECTION_MASK = 0xC0000000;
+    static final int LAYOUT_DIRECTION_MASK = 0xC0000000;
 
     /*
      * Array of horizontal direction flags for mapping attribute "horizontalDirection" to correct
      * flag value.
      * {@hide}
      */
-    private static final int[] HORIZONTAL_DIRECTION_FLAGS = { HORIZONTAL_DIRECTION_LTR,
-            HORIZONTAL_DIRECTION_RTL, HORIZONTAL_DIRECTION_INHERIT, HORIZONTAL_DIRECTION_LOCALE};
+    private static final int[] LAYOUT_DIRECTION_FLAGS = {LAYOUT_DIRECTION_LTR,
+        LAYOUT_DIRECTION_RTL, LAYOUT_DIRECTION_INHERIT, LAYOUT_DIRECTION_LOCALE};
 
     /**
      * Default horizontalDirection.
      * {@hide}
      */
-    private static final int HORIZONTAL_DIRECTION_DEFAULT = HORIZONTAL_DIRECTION_INHERIT;
+    private static final int LAYOUT_DIRECTION_DEFAULT = LAYOUT_DIRECTION_INHERIT;
 
     /**
      * View flag indicating whether {@link #addFocusables(ArrayList, int, int)}
@@ -1492,6 +1495,11 @@
     private static final Object sTagsLock = new Object();
 
     /**
+     * The next available accessiiblity id.
+     */
+    private static int sNextAccessibilityViewId;
+
+    /**
      * The animation currently associated with this view.
      * @hide
      */
@@ -1533,6 +1541,11 @@
     int mID = NO_ID;
 
     /**
+     * The stable ID of this view for accessibility porposes.
+     */
+    int mAccessibilityViewId = NO_ID;
+
+    /**
      * The view's tag.
      * {@hide}
      *
@@ -2453,7 +2466,7 @@
     public View(Context context) {
         mContext = context;
         mResources = context != null ? context.getResources() : null;
-        mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | HORIZONTAL_DIRECTION_INHERIT;
+        mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | LAYOUT_DIRECTION_INHERIT;
         mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
         setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
     }
@@ -2651,18 +2664,18 @@
                         viewFlagMasks |= VISIBILITY_MASK;
                     }
                     break;
-                case com.android.internal.R.styleable.View_horizontalDirection:
+                case com.android.internal.R.styleable.View_layoutDirection:
                     // Clear any HORIZONTAL_DIRECTION flag already set
-                    viewFlagValues &= ~HORIZONTAL_DIRECTION_MASK;
+                    viewFlagValues &= ~LAYOUT_DIRECTION_MASK;
                     // Set the HORIZONTAL_DIRECTION flags depending on the value of the attribute
-                    final int horizontalDirection = a.getInt(attr, -1);
-                    if (horizontalDirection != -1) {
-                        viewFlagValues |= HORIZONTAL_DIRECTION_FLAGS[horizontalDirection];
+                    final int layoutDirection = a.getInt(attr, -1);
+                    if (layoutDirection != -1) {
+                        viewFlagValues |= LAYOUT_DIRECTION_FLAGS[layoutDirection];
                     } else {
-                        // Set to default (HORIZONTAL_DIRECTION_INHERIT)
-                        viewFlagValues |= HORIZONTAL_DIRECTION_DEFAULT;
+                        // Set to default (LAYOUT_DIRECTION_INHERIT)
+                        viewFlagValues |= LAYOUT_DIRECTION_DEFAULT;
                     }
-                    viewFlagMasks |= HORIZONTAL_DIRECTION_MASK;
+                    viewFlagMasks |= LAYOUT_DIRECTION_MASK;
                     break;
                 case com.android.internal.R.styleable.View_drawingCacheQuality:
                     final int cacheQuality = a.getInt(attr, 0);
@@ -3649,6 +3662,7 @@
      * @see #dispatchPopulateAccessibilityEvent(AccessibilityEvent)
      */
     public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
+        event.setSource(this);
         event.setClassName(getClass().getName());
         event.setPackageName(getContext().getPackageName());
         event.setEnabled(isEnabled());
@@ -3664,6 +3678,112 @@
     }
 
     /**
+     * Returns an {@link AccessibilityNodeInfo} representing this view from the
+     * point of view of an {@link android.accessibilityservice.AccessibilityService}.
+     * This method is responsible for obtaining an accessibility node info from a
+     * pool of reusable instances and calling
+     * {@link #onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} on this view to
+     * initialize the former.
+     * <p>
+     * Note: The client is responsible for recycling the obtained instance by calling
+     *       {@link AccessibilityNodeInfo#recycle()} to minimize object creation.
+     * </p>
+     * @return A populated {@link AccessibilityNodeInfo}.
+     *
+     * @see AccessibilityNodeInfo
+     */
+    public AccessibilityNodeInfo createAccessibilityNodeInfo() {
+        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(this);
+        onInitializeAccessibilityNodeInfo(info);
+        return info;
+    }
+
+    /**
+     * Initializes an {@link AccessibilityNodeInfo} with information about this view.
+     * The base implementation sets:
+     * <ul>
+     *   <li>{@link AccessibilityNodeInfo#setParent(View)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setBounds(Rect)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setPackageName(CharSequence)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setClassName(CharSequence)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setContentDescription(CharSequence)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setEnabled(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setClickable(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setFocusable(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setFocused(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setLongClickable(boolean)},</li>
+     *   <li>{@link AccessibilityNodeInfo#setSelected(boolean)},</li>
+     * </ul>
+     * <p>
+     * Subclasses should override this method, call the super implementation,
+     * and set additional attributes.
+     * </p>
+     * @param info The instance to initialize.
+     */
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        Rect bounds = mAttachInfo.mTmpInvalRect;
+        getDrawingRect(bounds);
+        info.setBounds(bounds);
+
+        ViewParent parent = getParent();
+        if (parent instanceof View) {
+            View parentView = (View) parent;
+            info.setParent(parentView);
+        }
+
+        info.setPackageName(mContext.getPackageName());
+        info.setClassName(getClass().getName());
+        info.setContentDescription(getContentDescription());
+
+        info.setEnabled(isEnabled());
+        info.setClickable(isClickable());
+        info.setFocusable(isFocusable());
+        info.setFocused(isFocused());
+        info.setSelected(isSelected());
+        info.setLongClickable(isLongClickable());
+
+        // TODO: These make sense only if we are in an AdapterView but all
+        // views can be selected. Maybe from accessiiblity perspective
+        // we should report as selectable view in an AdapterView.
+        info.addAction(AccessibilityNodeInfo.ACTION_SELECT);
+        info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
+
+        if (isFocusable()) {
+            if (isFocused()) {
+                info.addAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
+            } else {
+                info.addAction(AccessibilityNodeInfo.ACTION_FOCUS);
+            }
+        }
+    }
+
+    /**
+     * Gets the unique identifier of this view on the screen for accessibility purposes.
+     * If this {@link View} is not attached to any window, {@value #NO_ID} is returned.
+     *
+     * @return The view accessibility id.
+     *
+     * @hide
+     */
+    public int getAccessibilityViewId() {
+        if (mAccessibilityViewId == NO_ID) {
+            mAccessibilityViewId = sNextAccessibilityViewId++;
+        }
+        return mAccessibilityViewId;
+    }
+
+    /**
+     * Gets the unique identifier of the window in which this View reseides.
+     *
+     * @return The window accessibility id.
+     *
+     * @hide
+     */
+    public int getAccessibilityWindowId() {
+        return mAttachInfo != null ? mAttachInfo.mAccessibilityWindowId : NO_ID;
+    }
+
+    /**
      * Gets the {@link View} description. It briefly describes the view and is
      * primarily used for accessibility support. Set this property to enable
      * better accessibility support for your application. This is especially
@@ -4138,38 +4258,38 @@
     }
 
     /**
-     * Returns the horizontal direction for this view.
+     * Returns the layout direction for this view.
      *
-     * @return One of {@link #HORIZONTAL_DIRECTION_LTR},
-     *   {@link #HORIZONTAL_DIRECTION_RTL},
-     *   {@link #HORIZONTAL_DIRECTION_INHERIT} or
-     *   {@link #HORIZONTAL_DIRECTION_LOCALE}.
-     * @attr ref android.R.styleable#View_horizontalDirection
+     * @return One of {@link #LAYOUT_DIRECTION_LTR},
+     *   {@link #LAYOUT_DIRECTION_RTL},
+     *   {@link #LAYOUT_DIRECTION_INHERIT} or
+     *   {@link #LAYOUT_DIRECTION_LOCALE}.
+     * @attr ref android.R.styleable#View_layoutDirection
      * @hide
      */
     @ViewDebug.ExportedProperty(category = "layout", mapping = {
-        @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LTR,     to = "LTR"),
-        @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_RTL,     to = "RTL"),
-        @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_INHERIT, to = "INHERIT"),
-        @ViewDebug.IntToString(from = HORIZONTAL_DIRECTION_LOCALE,  to = "LOCALE")
+        @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LTR,     to = "LTR"),
+        @ViewDebug.IntToString(from = LAYOUT_DIRECTION_RTL,     to = "RTL"),
+        @ViewDebug.IntToString(from = LAYOUT_DIRECTION_INHERIT, to = "INHERIT"),
+        @ViewDebug.IntToString(from = LAYOUT_DIRECTION_LOCALE,  to = "LOCALE")
     })
-    public int getHorizontalDirection() {
-        return mViewFlags & HORIZONTAL_DIRECTION_MASK;
+    public int getLayoutDirection() {
+        return mViewFlags & LAYOUT_DIRECTION_MASK;
     }
 
     /**
-     * Set the horizontal direction for this view.
+     * Set the layout direction for this view.
      *
-     * @param horizontalDirection One of {@link #HORIZONTAL_DIRECTION_LTR},
-     *   {@link #HORIZONTAL_DIRECTION_RTL},
-     *   {@link #HORIZONTAL_DIRECTION_INHERIT} or
-     *   {@link #HORIZONTAL_DIRECTION_LOCALE}.
-     * @attr ref android.R.styleable#View_horizontalDirection
+     * @param layoutDirection One of {@link #LAYOUT_DIRECTION_LTR},
+     *   {@link #LAYOUT_DIRECTION_RTL},
+     *   {@link #LAYOUT_DIRECTION_INHERIT} or
+     *   {@link #LAYOUT_DIRECTION_LOCALE}.
+     * @attr ref android.R.styleable#View_layoutDirection
      * @hide
      */
     @RemotableViewMethod
-    public void setHorizontalDirection(int horizontalDirection) {
-        setFlags(horizontalDirection, HORIZONTAL_DIRECTION_MASK);
+    public void setLayoutDirection(int layoutDirection) {
+        setFlags(layoutDirection, LAYOUT_DIRECTION_MASK);
     }
 
     /**
@@ -4571,6 +4691,16 @@
     }
 
     /**
+     * Finds the Views that contain given text. The containment is case insensitive.
+     * As View's text is considered any text content that View renders.
+     *
+     * @param outViews The output list of matching Views.
+     * @param text The text to match against.
+     */
+    public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+    }
+
+    /**
      * Find and return all touchable views that are descendants of this view,
      * possibly including this view if it is touchable itself.
      *
@@ -4677,8 +4807,8 @@
 
         // need to be focusable in touch mode if in touch mode
         if (isInTouchMode() &&
-                (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
-            return false;
+            (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
+               return false;
         }
 
         // need to not have any parents blocking us
@@ -5975,7 +6105,7 @@
             }
         }
 
-        if ((changed & HORIZONTAL_DIRECTION_MASK) != 0) {
+        if ((changed & LAYOUT_DIRECTION_MASK) != 0) {
             requestLayout();
         }
     }
@@ -8530,24 +8660,24 @@
             mPrivateFlags &= ~AWAKEN_SCROLL_BARS_ON_ATTACH;
         }
         jumpDrawablesToCurrentState();
-        resolveHorizontalDirection();
+        resolveLayoutDirection();
     }
 
     /**
      * Resolving the layout direction. LTR is set initially.
      * We are supposing here that the parent directionality will be resolved before its children
      */
-    private void resolveHorizontalDirection() {
+    private void resolveLayoutDirection() {
         mPrivateFlags2 &= ~RESOLVED_LAYOUT_RTL;
-        switch (getHorizontalDirection()) {
-            case HORIZONTAL_DIRECTION_INHERIT:
+        switch (getLayoutDirection()) {
+            case LAYOUT_DIRECTION_INHERIT:
                 // If this is root view, no need to look at parent's layout dir.
                 if (mParent != null && mParent instanceof ViewGroup &&
                         ((ViewGroup) mParent).isLayoutRtl()) {
                     mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
                 }
                 break;
-            case HORIZONTAL_DIRECTION_RTL:
+            case LAYOUT_DIRECTION_RTL:
                 mPrivateFlags2 |= RESOLVED_LAYOUT_RTL;
                 break;
         }
@@ -12308,6 +12438,169 @@
         return getVerticalScrollFactor();
     }
 
+    //
+    // Properties
+    //
+    /**
+     * A Property wrapper around the <code>alpha</code> functionality handled by the
+     * {@link View#setAlpha(float)} and {@link View#getAlpha()} methods.
+     */
+    static Property<View, Float> ALPHA = new FloatProperty<View>("alpha") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setAlpha(value);
+        }
+
+        @Override
+        public Float get(View object) {
+            return object.getAlpha();
+        }
+    };
+
+    /**
+     * A Property wrapper around the <code>translationX</code> functionality handled by the
+     * {@link View#setTranslationX(float)} and {@link View#getTranslationX()} methods.
+     */
+    public static Property<View, Float> TRANSLATION_X = new FloatProperty<View>("translationX") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setTranslationX(value);
+        }
+
+                @Override
+        public Float get(View object) {
+            return object.getTranslationX();
+        }
+    };
+
+    /**
+     * A Property wrapper around the <code>translationY</code> functionality handled by the
+     * {@link View#setTranslationY(float)} and {@link View#getTranslationY()} methods.
+     */
+    public static Property<View, Float> TRANSLATION_Y = new FloatProperty<View>("translationY") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setTranslationY(value);
+        }
+
+        @Override
+        public Float get(View object) {
+            return object.getTranslationY();
+        }
+    };
+
+    /**
+     * A Property wrapper around the <code>x</code> functionality handled by the
+     * {@link View#setX(float)} and {@link View#getX()} methods.
+     */
+    public static Property<View, Float> X = new FloatProperty<View>("x") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setX(value);
+        }
+
+        @Override
+        public Float get(View object) {
+            return object.getX();
+        }
+    };
+
+    /**
+     * A Property wrapper around the <code>y</code> functionality handled by the
+     * {@link View#setY(float)} and {@link View#getY()} methods.
+     */
+    public static Property<View, Float> Y = new FloatProperty<View>("y") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setY(value);
+        }
+
+        @Override
+        public Float get(View object) {
+            return object.getY();
+        }
+    };
+
+    /**
+     * A Property wrapper around the <code>rotation</code> functionality handled by the
+     * {@link View#setRotation(float)} and {@link View#getRotation()} methods.
+     */
+    public static Property<View, Float> ROTATION = new FloatProperty<View>("rotation") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setRotation(value);
+        }
+
+        @Override
+        public Float get(View object) {
+            return object.getRotation();
+        }
+    };
+
+    /**
+     * A Property wrapper around the <code>rotationX</code> functionality handled by the
+     * {@link View#setRotationX(float)} and {@link View#getRotationX()} methods.
+     */
+    public static Property<View, Float> ROTATION_X = new FloatProperty<View>("rotationX") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setRotationX(value);
+        }
+
+        @Override
+        public Float get(View object) {
+            return object.getRotationX();
+        }
+    };
+
+    /**
+     * A Property wrapper around the <code>rotationY</code> functionality handled by the
+     * {@link View#setRotationY(float)} and {@link View#getRotationY()} methods.
+     */
+    public static Property<View, Float> ROTATION_Y = new FloatProperty<View>("rotationY") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setRotationY(value);
+        }
+
+        @Override
+        public Float get(View object) {
+            return object.getRotationY();
+        }
+    };
+
+    /**
+     * A Property wrapper around the <code>scaleX</code> functionality handled by the
+     * {@link View#setScaleX(float)} and {@link View#getScaleX()} methods.
+     */
+    public static Property<View, Float> SCALE_X = new FloatProperty<View>("scaleX") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setScaleX(value);
+        }
+
+        @Override
+        public Float get(View object) {
+            return object.getScaleX();
+        }
+    };
+
+    /**
+     * A Property wrapper around the <code>scaleY</code> functionality handled by the
+     * {@link View#setScaleY(float)} and {@link View#getScaleY()} methods.
+     */
+    public static Property<View, Float> SCALE_Y = new FloatProperty<View>("scaleY") {
+        @Override
+        public void setValue(View object, float value) {
+            object.setScaleY(value);
+        }
+
+        @Override
+        public Float get(View object) {
+            return object.getScaleY();
+        }
+    };
+
     /**
      * A MeasureSpec encapsulates the layout requirements passed from parent to child.
      * Each MeasureSpec represents a requirement for either the width or the height.
@@ -12719,6 +13012,7 @@
             );
 
             private InvalidateInfo mNext;
+            private boolean mIsPooled;
 
             View target;
 
@@ -12742,6 +13036,14 @@
             void release() {
                 sPool.release(this);
             }
+
+            public boolean isPooled() {
+                return mIsPooled;
+            }
+
+            public void setPooled(boolean isPooled) {
+                mIsPooled = isPooled;
+            }
         }
 
         final IWindowSession mSession;
@@ -12952,6 +13254,11 @@
         final ArrayList<View> mFocusablesTempList = new ArrayList<View>(24);
 
         /**
+         * The id of the window for accessibility purposes.
+         */
+        int mAccessibilityWindowId = View.NO_ID;
+
+        /**
          * Creates a new set of attachment information with the specified
          * events handler and thread.
          *
diff --git a/core/java/android/view/ViewAncestor.java b/core/java/android/view/ViewAncestor.java
index 1e72529..da26aba 100644
--- a/core/java/android/view/ViewAncestor.java
+++ b/core/java/android/view/ViewAncestor.java
@@ -50,18 +50,28 @@
 import android.util.DisplayMetrics;
 import android.util.EventLog;
 import android.util.Log;
+import android.util.Pool;
+import android.util.Poolable;
+import android.util.PoolableManager;
+import android.util.Pools;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.TypedValue;
 import android.view.View.MeasureSpec;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Scroller;
+
 import com.android.internal.policy.PolicyManager;
+import com.android.internal.util.Predicate;
 import com.android.internal.view.BaseSurfaceHolder;
 import com.android.internal.view.IInputMethodCallback;
 import com.android.internal.view.IInputMethodSession;
@@ -71,6 +81,7 @@
 import java.io.OutputStream;
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
+import java.util.List;
 
 /**
  * The top of a view hierarchy, implementing the needed protocol between View
@@ -84,7 +95,6 @@
         View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
     private static final String TAG = "ViewAncestor";
     private static final boolean DBG = false;
-    private static final boolean SHOW_FPS = false;
     private static final boolean LOCAL_LOGV = false;
     /** @noinspection PointlessBooleanExpression*/
     private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
@@ -119,8 +129,6 @@
     static final ArrayList<ComponentCallbacks> sConfigCallbacks
             = new ArrayList<ComponentCallbacks>();
     
-    private static int sDrawTime;
-
     long mLastTrackballTime = 0;
     final TrackballAxis mTrackballAxisX = new TrackballAxis();
     final TrackballAxis mTrackballAxisY = new TrackballAxis();
@@ -248,6 +256,12 @@
      */
     AudioManager mAudioManager;
 
+    final AccessibilityManager mAccessibilityManager;
+
+    AccessibilityInteractionController mAccessibilityInteractionContrtoller;
+
+    AccessibilityInteractionConnectionManager mAccessibilityInteractionConnectionManager;
+
     private final int mDensity;
 
     /**
@@ -285,7 +299,7 @@
         // done here instead of in the static block because Zygote does not
         // allow the spawning of threads.
         getWindowSession(context.getMainLooper());
-        
+
         mThread = Thread.currentThread();
         mLocation = new WindowLeaked(null);
         mLocation.fillInStackTrace();
@@ -302,6 +316,11 @@
         mPreviousTransparentRegion = new Region();
         mFirst = true; // true for the first time the view is added
         mAdded = false;
+        mAccessibilityManager = AccessibilityManager.getInstance(context);
+        mAccessibilityInteractionConnectionManager =
+            new AccessibilityInteractionConnectionManager();
+        mAccessibilityManager.addAccessibilityStateChangeListener(
+                mAccessibilityInteractionConnectionManager);
         mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
         mViewConfiguration = ViewConfiguration.get(context);
         mDensity = context.getResources().getDisplayMetrics().densityDpi;
@@ -490,10 +509,14 @@
                     InputQueue.registerInputChannel(mInputChannel, mInputHandler,
                             Looper.myQueue());
                 }
-                
+
                 view.assignParent(this);
                 mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
                 mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
+
+                if (mAccessibilityManager.isEnabled()) {
+                    mAccessibilityInteractionConnectionManager.ensureConnection();
+                }
             }
         }
     }
@@ -1787,14 +1810,6 @@
                         mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
                     }
 
-                    if (SHOW_FPS || ViewDebug.DEBUG_SHOW_FPS) {
-                        int now = (int)SystemClock.elapsedRealtime();
-                        if (sDrawTime != 0) {
-                            nativeShowFPS(canvas, now - sDrawTime);
-                        }
-                        sDrawTime = now;
-                    }
-
                     if (ViewDebug.DEBUG_PROFILE_DRAWING) {
                         EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
                     }
@@ -2004,6 +2019,10 @@
             mView.dispatchDetachedFromWindow();
         }
 
+        mAccessibilityInteractionConnectionManager.ensureNoConnection();
+        mAccessibilityManager.removeAccessibilityStateChangeListener(
+                mAccessibilityInteractionConnectionManager);
+
         mView = null;
         mAttachInfo.mRootView = null;
         mAttachInfo.mSurface = null;
@@ -2020,7 +2039,6 @@
                 InputQueue.unregisterInputChannel(mInputChannel);
             }
         }
-        
         try {
             sWindowSession.remove(mWindow);
         } catch (RemoteException e) {
@@ -2098,6 +2116,10 @@
     public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016;
     public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017;
     public final static int DISPATCH_GENERIC_MOTION = 1018;
+    public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1019;
+    public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1020;
+    public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1021;
+    public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT = 1022;
 
     @Override
     public void handleMessage(Message msg) {
@@ -2298,9 +2320,25 @@
         case DISPATCH_SYSTEM_UI_VISIBILITY: {
             handleDispatchSystemUiVisibilityChanged(msg.arg1);
         } break;
+        case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: {
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg);
+        } break;
+        case DO_PERFORM_ACCESSIBILITY_ACTION: {
+            getAccessibilityInteractionController()
+                .perfromAccessibilityActionUiThread(msg);
+        } break;
+        case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: {
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfoByViewIdUiThread(msg);
+        } break;
+        case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT: {
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfosByViewTextUiThread(msg);
+        } break;
         }
     }
-    
+
     private void startInputEvent(InputQueue.FinishedCallback finishedCallback) {
         if (mFinishedCallback != null) {
             Slog.w(TAG, "Received a new input event from the input queue but there is "
@@ -3220,6 +3258,17 @@
         return mAudioManager;
     }
 
+    public AccessibilityInteractionController getAccessibilityInteractionController() {
+        if (mView == null) {
+            throw new IllegalStateException("getAccessibilityInteractionController"
+                    + " called when there is no mView");
+        }
+        if (mAccessibilityInteractionContrtoller == null) {
+            mAccessibilityInteractionContrtoller = new AccessibilityInteractionController();
+        }
+        return mAccessibilityInteractionContrtoller;
+    }
+
     private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
             boolean insetsPending) throws RemoteException {
 
@@ -3517,7 +3566,7 @@
      * send an {@link AccessibilityEvent} to announce that.
      */
     private void sendAccessibilityEvents() {
-        if (!AccessibilityManager.getInstance(mView.getContext()).isEnabled()) {
+        if (!mAccessibilityManager.isEnabled()) {
             return;
         }
         mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
@@ -3545,7 +3594,7 @@
         if (mView == null) {
             return false;
         }
-        AccessibilityManager.getInstance(child.mContext).sendAccessibilityEvent(event);
+        mAccessibilityManager.sendAccessibilityEvent(event);
         return true;
     }
 
@@ -3608,18 +3657,18 @@
     static class InputMethodCallback extends IInputMethodCallback.Stub {
         private WeakReference<ViewAncestor> mViewAncestor;
 
-        public InputMethodCallback(ViewAncestor viewRoot) {
-            mViewAncestor = new WeakReference<ViewAncestor>(viewRoot);
+        public InputMethodCallback(ViewAncestor viewAncestor) {
+            mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor);
         }
 
         public void finishedEvent(int seq, boolean handled) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchFinishedEvent(seq, handled);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchFinishedEvent(seq, handled);
             }
         }
 
-        public void sessionCreated(IInputMethodSession session) throws RemoteException {
+        public void sessionCreated(IInputMethodSession session) {
             // Stub -- not for use in the client.
         }
     }
@@ -3627,36 +3676,37 @@
     static class W extends IWindow.Stub {
         private final WeakReference<ViewAncestor> mViewAncestor;
 
-        W(ViewAncestor viewRoot) {
-            mViewAncestor = new WeakReference<ViewAncestor>(viewRoot);
+        W(ViewAncestor viewAncestor) {
+            mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor);
         }
 
         public void resized(int w, int h, Rect coveredInsets, Rect visibleInsets,
                 boolean reportDraw, Configuration newConfig) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw, newConfig);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchResized(w, h, coveredInsets, visibleInsets, reportDraw,
+                        newConfig);
             }
         }
 
         public void dispatchAppVisibility(boolean visible) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchAppVisibility(visible);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchAppVisibility(visible);
             }
         }
 
         public void dispatchGetNewSurface() {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchGetNewSurface();
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchGetNewSurface();
             }
         }
 
         public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.windowFocusChanged(hasFocus, inTouchMode);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.windowFocusChanged(hasFocus, inTouchMode);
             }
         }
 
@@ -3674,9 +3724,9 @@
         }
 
         public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                final View view = viewRoot.mView;
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                final View view = viewAncestor.mView;
                 if (view != null) {
                     if (checkCallingPermission(Manifest.permission.DUMP) !=
                             PackageManager.PERMISSION_GRANTED) {
@@ -3705,9 +3755,9 @@
         }
         
         public void closeSystemDialogs(String reason) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchCloseSystemDialogs(reason);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchCloseSystemDialogs(reason);
             }
         }
         
@@ -3720,7 +3770,7 @@
                 }
             }
         }
-        
+
         public void dispatchWallpaperCommand(String action, int x, int y,
                 int z, Bundle extras, boolean sync) {
             if (sync) {
@@ -3733,17 +3783,16 @@
 
         /* Drag/drop */
         public void dispatchDragEvent(DragEvent event) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchDragEvent(event);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchDragEvent(event);
             }
         }
 
-        @Override
         public void dispatchSystemUiVisibilityChanged(int visibility) {
-            final ViewAncestor viewRoot = mViewAncestor.get();
-            if (viewRoot != null) {
-                viewRoot.dispatchSystemUiVisibilityChanged(visibility);
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor != null) {
+                viewAncestor.dispatchSystemUiVisibilityChanged(visibility);
             }
         }
     }
@@ -4053,5 +4102,393 @@
         }
     }
 
-    private static native void nativeShowFPS(Canvas canvas, int durationMillis);
+    /**
+     * Class for managing the accessibility interaction connection
+     * based on the global accessibility state.
+     */
+    final class AccessibilityInteractionConnectionManager
+            implements AccessibilityStateChangeListener {
+        public void onAccessibilityStateChanged(boolean enabled) {
+            if (enabled) {
+                ensureConnection();
+            } else {
+                ensureNoConnection();
+            }
+        }
+
+        public void ensureConnection() {
+            final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
+            if (!registered) {
+                mAttachInfo.mAccessibilityWindowId =
+                    mAccessibilityManager.addAccessibilityInteractionConnection(mWindow,
+                            new AccessibilityInteractionConnection(ViewAncestor.this));
+            }
+        }
+
+        public void ensureNoConnection() {
+            final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID;
+            if (registered) {
+                mAttachInfo.mAccessibilityWindowId = View.NO_ID;
+                mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow);
+            }
+        }
+    }
+
+    /**
+     * This class is an interface this ViewAncestor provides to the
+     * AccessibilityManagerService to the latter can interact with
+     * the view hierarchy in this ViewAncestor.
+     */
+    final class AccessibilityInteractionConnection
+            extends IAccessibilityInteractionConnection.Stub {
+        private final WeakReference<ViewAncestor> mViewAncestor;
+
+        AccessibilityInteractionConnection(ViewAncestor viewAncestor) {
+            mViewAncestor = new WeakReference<ViewAncestor>(viewAncestor);
+        }
+
+        public void findAccessibilityNodeInfoByAccessibilityId(int accessibilityId,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor == null) {
+                return;
+            }
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfoByAccessibilityIdClientThread(accessibilityId,
+                        interactionId, callback);
+        }
+
+        public void performAccessibilityAction(int accessibilityId, int action,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor == null) {
+                return;
+            }
+            getAccessibilityInteractionController()
+                .performAccessibilityActionClientThread(accessibilityId, action, interactionId,
+                        callback);
+        }
+
+        public void findAccessibilityNodeInfoByViewId(int viewId,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor == null) {
+                return;
+            }
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfoByViewIdClientThread(viewId, interactionId, callback);
+        }
+
+        public void findAccessibilityNodeInfosByViewText(String text, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback) {
+            final ViewAncestor viewAncestor = mViewAncestor.get();
+            if (viewAncestor == null) {
+                return;
+            }
+            getAccessibilityInteractionController()
+                .findAccessibilityNodeInfosByViewTextClientThread(text, interactionId, callback);
+        }
+    }
+
+    /**
+     * Class for managing accessibility interactions initiated from the system
+     * and targeting the view hierarchy. A *ClientThread method is to be
+     * called from the interaction connection this ViewAncestor gives the
+     * system to talk to it and a corresponding *UiThread method that is executed
+     * on the UI thread.
+     */
+    final class AccessibilityInteractionController {
+        private static final int POOL_SIZE = 5;
+
+        private FindByAccessibilitytIdPredicate mFindByAccessibilityIdPredicate =
+            new FindByAccessibilitytIdPredicate();
+
+        private ArrayList<AccessibilityNodeInfo> mTempAccessibilityNodeInfoList =
+            new ArrayList<AccessibilityNodeInfo>();
+
+        // Reusable poolable arguments for interacting with the view hierarchy
+        // to fit more arguments than Message and to avoid sharing objects between
+        // two messages since several threads can send messages concurrently.
+        private final Pool<SomeArgs> mPool = Pools.synchronizedPool(Pools.finitePool(
+                new PoolableManager<SomeArgs>() {
+                    public SomeArgs newInstance() {
+                        return new SomeArgs();
+                    }
+
+                    public void onAcquired(SomeArgs info) {
+                        /* do nothing */
+                    }
+
+                    public void onReleased(SomeArgs info) {
+                        info.clear();
+                    }
+                }, POOL_SIZE)
+        );
+
+        public class SomeArgs implements Poolable<SomeArgs> {
+            private SomeArgs mNext;
+            private boolean mIsPooled;
+
+            public Object arg1;
+            public Object arg2;
+            public int argi1;
+            public int argi2;
+            public int argi3;
+
+            public SomeArgs getNextPoolable() {
+                return mNext;
+            }
+
+            public boolean isPooled() {
+                return mIsPooled;
+            }
+
+            public void setNextPoolable(SomeArgs args) {
+                mNext = args;
+            }
+
+            public void setPooled(boolean isPooled) {
+                mIsPooled = isPooled;
+            }
+
+            private void clear() {
+                arg1 = null;
+                arg2 = null;
+                argi1 = 0;
+                argi2 = 0;
+                argi3 = 0;
+            }
+        }
+
+        public void findAccessibilityNodeInfoByAccessibilityIdClientThread(int accessibilityId,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            Message message = Message.obtain();
+            message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID;
+            message.arg1 = accessibilityId;
+            message.arg2 = interactionId;
+            message.obj = callback;
+            sendMessage(message);
+        }
+
+        public void findAccessibilityNodeInfoByAccessibilityIdUiThread(Message message) {
+            final int accessibilityId = message.arg1;
+            final int interactionId = message.arg2;
+            final IAccessibilityInteractionConnectionCallback callback =
+                (IAccessibilityInteractionConnectionCallback) message.obj;
+
+            View root = ViewAncestor.this.mView;
+            if (root == null) {
+                return;
+            }
+
+            FindByAccessibilitytIdPredicate predicate = mFindByAccessibilityIdPredicate;
+            predicate.init(accessibilityId);
+
+            View target = root.findViewByPredicate(predicate);
+            if (target != null) {
+                AccessibilityNodeInfo info = target.createAccessibilityNodeInfo();
+                try {
+                    callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+                } catch (RemoteException re) {
+                    /* ignore - the other side will time out */
+                }
+            }
+        }
+
+        public void findAccessibilityNodeInfoByViewIdClientThread(int viewId, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback) {
+            Message message = Message.obtain();
+            message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID;
+            message.arg1 = viewId;
+            message.arg2 = interactionId;
+            message.obj = callback;
+            sendMessage(message);
+        }
+
+        public void findAccessibilityNodeInfoByViewIdUiThread(Message message) {
+            final int viewId = message.arg1;
+            final int interactionId = message.arg2;
+            final IAccessibilityInteractionConnectionCallback callback =
+                (IAccessibilityInteractionConnectionCallback) message.obj;
+
+            View root = ViewAncestor.this.mView;
+            if (root == null) {
+                return;
+            }
+            View target = root.findViewById(viewId);
+            if (target != null) {
+                AccessibilityNodeInfo info = target.createAccessibilityNodeInfo();
+                try {
+                    callback.setFindAccessibilityNodeInfoResult(info, interactionId);
+                } catch (RemoteException re) {
+                    /* ignore - the other side will time out */
+                }
+            }
+        }
+
+        public void findAccessibilityNodeInfosByViewTextClientThread(String text, int interactionId,
+                IAccessibilityInteractionConnectionCallback callback) {
+            Message message = Message.obtain();
+            message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_TEXT;
+            SomeArgs args = mPool.acquire();
+            args.arg1 = text;
+            args.argi1 = interactionId;
+            args.arg2 = callback;
+            message.obj = args;
+            sendMessage(message);
+        }
+
+        public void findAccessibilityNodeInfosByViewTextUiThread(Message message) {
+            SomeArgs args = (SomeArgs) message.obj;
+            final String text = (String) args.arg1;
+            final int interactionId = args.argi1;
+            final IAccessibilityInteractionConnectionCallback callback =
+                (IAccessibilityInteractionConnectionCallback) args.arg2;
+            mPool.release(args);
+
+            View root = ViewAncestor.this.mView;
+            if (root == null) {
+                return;
+            }
+
+            ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList;
+            foundViews.clear();
+
+            root.findViewsWithText(foundViews, text);
+            if (foundViews.isEmpty()) {
+                return;
+            }
+
+            List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList;
+            infos.clear();
+
+            final int viewCount = foundViews.size();
+            for (int i = 0; i < viewCount; i++) {
+                View foundView = foundViews.get(i);
+                infos.add(foundView.createAccessibilityNodeInfo());
+            }
+
+            try {
+                callback.setFindAccessibilityNodeInfosResult(infos, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+
+        public void performAccessibilityActionClientThread(int accessibilityId, int action,
+                int interactionId, IAccessibilityInteractionConnectionCallback callback) {
+            Message message = Message.obtain();
+            message.what = DO_PERFORM_ACCESSIBILITY_ACTION;
+            SomeArgs args = mPool.acquire();
+            args.argi1 = accessibilityId;
+            args.argi2 = action;
+            args.argi3 = interactionId;
+            args.arg1 = callback;
+            message.obj = args;
+            sendMessage(message);
+        }
+
+        public void perfromAccessibilityActionUiThread(Message message) {
+            SomeArgs args = (SomeArgs) message.obj;
+            final int accessibilityId = args.argi1;
+            final int action = args.argi2;
+            final int interactionId = args.argi3;
+            final IAccessibilityInteractionConnectionCallback callback =
+                (IAccessibilityInteractionConnectionCallback) args.arg1;
+            mPool.release(args);
+
+            if (ViewAncestor.this.mView == null) {
+                return;
+            }
+
+            boolean succeeded = false;
+            switch (action) {
+                case AccessibilityNodeInfo.ACTION_FOCUS: {
+                    succeeded = performActionFocus(accessibilityId);
+                } break;
+                case AccessibilityNodeInfo.ACTION_CLEAR_FOCUS: {
+                    succeeded = performActionClearFocus(accessibilityId);
+                } break;
+                case AccessibilityNodeInfo.ACTION_SELECT: {
+                    succeeded = performActionSelect(accessibilityId);
+                } break;
+                case AccessibilityNodeInfo.ACTION_CLEAR_SELECTION: {
+                    succeeded = performActionClearSelection(accessibilityId);
+                } break;
+            }
+
+            try {
+                callback.setPerformAccessibilityActionResult(succeeded, interactionId);
+            } catch (RemoteException re) {
+                /* ignore - the other side will time out */
+            }
+        }
+
+        private boolean performActionFocus(int accessibilityId) {
+            View target = findViewByAccessibilityId(accessibilityId);
+            if (target == null) {
+                return false;
+            }
+            // Get out of touch mode since accessibility wants to move focus around.
+            ensureTouchMode(false);
+            return target.requestFocus();
+        }
+
+        private boolean performActionClearFocus(int accessibilityId) {
+            View target = findViewByAccessibilityId(accessibilityId);
+            if (target == null) {
+                return false;
+            }
+            if (!target.isFocused()) {
+                return false;
+            }
+            target.clearFocus();
+            return !target.isFocused();
+        }
+
+        private boolean performActionSelect(int accessibilityId) {
+            View target = findViewByAccessibilityId(accessibilityId);
+            if (target == null) {
+                return false;
+            }
+            if (target.isSelected()) {
+                return false;
+            }
+            target.setSelected(true);
+            return target.isSelected();
+        }
+
+        private boolean performActionClearSelection(int accessibilityId) {
+            View target = findViewByAccessibilityId(accessibilityId);
+            if (target == null) {
+                return false;
+            }
+            if (!target.isSelected()) {
+                return false;
+            }
+            target.setSelected(false);
+            return !target.isSelected();
+        }
+
+        private View findViewByAccessibilityId(int accessibilityId) {
+            View root = ViewAncestor.this.mView;
+            if (root == null) {
+                return null;
+            }
+            mFindByAccessibilityIdPredicate.init(accessibilityId);
+            return root.findViewByPredicate(mFindByAccessibilityIdPredicate);
+        }
+
+        private final class FindByAccessibilitytIdPredicate implements Predicate<View> {
+            public int mSerchedId;
+
+            public void init(int searchedId) {
+                mSerchedId = searchedId;
+            }
+
+            public boolean apply(View view) {
+                return (view.getAccessibilityViewId() == mSerchedId);
+            }
+        }
+    }
 }
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 36bb046..5919150 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -110,7 +110,21 @@
      * double-tap.
      */
     private static final int DOUBLE_TAP_TIMEOUT = 300;
-    
+
+    /**
+     * Defines the maximum duration in milliseconds between a touch pad
+     * touch and release for a given touch to be considered a tap (click) as
+     * opposed to a hover movement gesture.
+     */
+    private static final int HOVER_TAP_TIMEOUT = 150;
+
+    /**
+     * Defines the maximum distance in pixels that a touch pad touch can move
+     * before being released for it to be considered a tap (click) as opposed
+     * to a hover movement gesture.
+     */
+    private static final int HOVER_TAP_SLOP = 20;
+
     /**
      * Defines the duration in milliseconds we want to display zoom controls in response 
      * to a user panning within an application.
@@ -369,7 +383,7 @@
     public static int getTapTimeout() {
         return TAP_TIMEOUT;
     }
-    
+
     /**
      * @return the duration in milliseconds we will wait to see if a touch event
      * is a jump tap. If the user does not move within this interval, it is
@@ -387,7 +401,27 @@
     public static int getDoubleTapTimeout() {
         return DOUBLE_TAP_TIMEOUT;
     }
-    
+
+    /**
+     * @return the maximum duration in milliseconds between a touch pad
+     * touch and release for a given touch to be considered a tap (click) as
+     * opposed to a hover movement gesture.
+     * @hide
+     */
+    public static int getHoverTapTimeout() {
+        return HOVER_TAP_TIMEOUT;
+    }
+
+    /**
+     * @return the maximum distance in pixels that a touch pad touch can move
+     * before being released for it to be considered a tap (click) as opposed
+     * to a hover movement gesture.
+     * @hide
+     */
+    public static int getHoverTapSlop() {
+        return HOVER_TAP_SLOP;
+    }
+
     /**
      * @return Inset in pixels to look for touchable content when the user touches the edge of the
      *         screen
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index f504b90..57ee8a0 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -35,6 +35,7 @@
 import android.util.Log;
 import android.util.SparseArray;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AlphaAnimation;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
@@ -772,6 +773,18 @@
         }
     }
 
+    @Override
+    public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+        final int childrenCount = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < childrenCount; i++) {
+            View child = children[i];
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+                child.findViewsWithText(outViews, text);
+            }
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
@@ -2007,6 +2020,16 @@
         return false;
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        for (int i = 0, count = mChildrenCount; i < count; i++) {
+            View child = mChildren[i];
+            info.addChild(child);
+        }
+    }
+
     /**
      * {@inheritDoc}
      */
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index a1ddd08..b0181bb 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -77,8 +77,8 @@
             implements Parcelable {
         /**
          * X position for this window.  With the default gravity it is ignored.
-         * When using {@link Gravity#LEFT} or {@link Gravity#BEFORE} or {@link Gravity#RIGHT} or
-         * {@link Gravity#AFTER} it provides an offset from the given edge.
+         * When using {@link Gravity#LEFT} or {@link Gravity#START} or {@link Gravity#RIGHT} or
+         * {@link Gravity#END} it provides an offset from the given edge.
          */
         @ViewDebug.ExportedProperty
         public int x;
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index 7b80797..06e4827 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -16,9 +16,12 @@
 
 package android.view.accessibility;
 
+import android.accessibilityservice.IAccessibilityServiceConnection;
 import android.os.Parcel;
 import android.os.Parcelable;
+import android.os.RemoteException;
 import android.text.TextUtils;
+import android.view.View;
 
 import java.util.ArrayList;
 
@@ -159,6 +162,7 @@
  * @see android.accessibilityservice.AccessibilityService
  */
 public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
+    private static final boolean DEBUG = false;
 
     /**
      * Invalid selection/focus position.
@@ -256,21 +260,38 @@
     private static final Object sPoolLock = new Object();
     private static AccessibilityEvent sPool;
     private static int sPoolSize;
-
     private AccessibilityEvent mNext;
     private boolean mIsInPool;
 
     private int mEventType;
+    private int mSourceAccessibilityViewId = View.NO_ID;
+    private int mSourceAccessibilityWindowId = View.NO_ID;
     private CharSequence mPackageName;
     private long mEventTime;
 
     private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
 
+    private IAccessibilityServiceConnection mConnection;
+
     /*
      * Hide constructor from clients.
      */
     private AccessibilityEvent() {
+    }
 
+    /**
+     * Initialize an event from another one.
+     *
+     * @param event The event to initialize from.
+     */
+    void init(AccessibilityEvent event) {
+        super.init(event);
+        mEventType = event.mEventType;
+        mEventTime = event.mEventTime;
+        mSourceAccessibilityWindowId = event.mSourceAccessibilityWindowId;
+        mSourceAccessibilityViewId = event.mSourceAccessibilityViewId;
+        mPackageName = event.mPackageName;
+        mConnection = event.mConnection;
     }
 
     /**
@@ -286,8 +307,11 @@
      * Appends an {@link AccessibilityRecord} to the end of event records.
      *
      * @param record The record to append.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void appendRecord(AccessibilityRecord record) {
+        enforceNotSealed();
         mRecords.add(record);
     }
 
@@ -311,11 +335,89 @@
     }
 
     /**
+     * Sets the event source.
+     *
+     * @param source The source.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setSource(View source) {
+        enforceNotSealed();
+        if (source != null) {
+            mSourceAccessibilityWindowId = source.getAccessibilityWindowId();
+            mSourceAccessibilityViewId = source.getAccessibilityViewId();
+        } else {
+            mSourceAccessibilityWindowId = View.NO_ID;
+            mSourceAccessibilityViewId = View.NO_ID;
+        }
+    }
+
+    /**
+     * Gets the {@link AccessibilityNodeInfo} of the event source.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     * @return The info.
+     */
+    public AccessibilityNodeInfo getSource() {
+        enforceSealed();
+        if (mSourceAccessibilityWindowId == View.NO_ID
+                || mSourceAccessibilityViewId == View.NO_ID) {
+            return null;
+        }
+        try {
+            return mConnection.findAccessibilityNodeInfoByAccessibilityId(
+                    mSourceAccessibilityWindowId, mSourceAccessibilityViewId);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the id of the window from which the event comes from.
+     *
+     * @return The window id.
+     */
+    public int getAccessibilityWindowId() {
+        return mSourceAccessibilityWindowId;
+    }
+
+    /**
+     * Sets the client token for the accessibility service that
+     * provided this node info.
+     *
+     * @param connection The connection.
+     *
+     * @hide
+     */
+    public final void setConnection(IAccessibilityServiceConnection connection) {
+        mConnection = connection;
+    }
+
+    /**
+     * Gets the accessibility window id of the source window.
+     *
+     * @return The id.
+     *
+     * @hide
+     */
+    public int getSourceAccessibilityWindowId() {
+        return mSourceAccessibilityWindowId;
+    }
+
+    /**
      * Sets the event type.
      *
      * @param eventType The event type.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setEventType(int eventType) {
+        enforceNotSealed();
         mEventType = eventType;
     }
 
@@ -332,8 +434,11 @@
      * Sets the time in which this event was sent.
      *
      * @param eventTime The event time.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setEventTime(long eventTime) {
+        enforceNotSealed();
         mEventTime = eventTime;
     }
 
@@ -350,8 +455,11 @@
      * Sets the package name of the source.
      *
      * @param packageName The package name.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setPackageName(CharSequence packageName) {
+        enforceNotSealed();
         mPackageName = packageName;
     }
 
@@ -370,6 +478,27 @@
 
     /**
      * Returns a cached instance if such is available or a new one is
+     * instantiated with type property set.
+     *
+     * @param event The other event.
+     * @return An instance.
+     */
+    public static AccessibilityEvent obtain(AccessibilityEvent event) {
+        AccessibilityEvent eventClone = AccessibilityEvent.obtain();
+        eventClone.init(event);
+
+        final int recordCount = event.mRecords.size();
+        for (int i = 0; i < recordCount; i++) {
+            AccessibilityRecord record = event.mRecords.get(i);
+            AccessibilityRecord recordClone = AccessibilityRecord.obtain(record);
+            eventClone.mRecords.add(recordClone);
+        }
+
+        return eventClone;
+    }
+
+    /**
+     * Returns a cached instance if such is available or a new one is
      * instantiated.
      *
      * @return An instance.
@@ -413,11 +542,16 @@
 
     /**
      * Clears the state of this instance.
+     *
+     * @hide
      */
     @Override
     protected void clear() {
         super.clear();
+        mConnection = null;
         mEventType = 0;
+        mSourceAccessibilityViewId = View.NO_ID;
+        mSourceAccessibilityWindowId = View.NO_ID;
         mPackageName = null;
         mEventTime = 0;
         while (!mRecords.isEmpty()) {
@@ -432,7 +566,14 @@
      * @param parcel A parcel containing the state of a {@link AccessibilityEvent}.
      */
     public void initFromParcel(Parcel parcel) {
+        if (parcel.readInt() == 1) {
+            mConnection = IAccessibilityServiceConnection.Stub.asInterface(
+                    parcel.readStrongBinder());
+        }
+        setSealed(parcel.readInt() == 1);
         mEventType = parcel.readInt();
+        mSourceAccessibilityWindowId = parcel.readInt();
+        mSourceAccessibilityViewId = parcel.readInt();
         mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
         mEventTime = parcel.readLong();
         readAccessibilityRecordFromParcel(this, parcel);
@@ -471,7 +612,16 @@
      * {@inheritDoc}
      */
     public void writeToParcel(Parcel parcel, int flags) {
+        if (mConnection == null) {
+            parcel.writeInt(0);
+        } else {
+            parcel.writeInt(1);
+            parcel.writeStrongBinder(mConnection.asBinder());
+        }
+        parcel.writeInt(isSealed() ? 1 : 0);
         parcel.writeInt(mEventType);
+        parcel.writeInt(mSourceAccessibilityWindowId);
+        parcel.writeInt(mSourceAccessibilityViewId);
         TextUtils.writeToParcel(mPackageName, parcel, 0);
         parcel.writeLong(mEventTime);
         writeAccessibilityRecordToParcel(this, parcel, flags);
@@ -519,18 +669,36 @@
         builder.append("; EventType: ").append(eventTypeToString(mEventType));
         builder.append("; EventTime: ").append(mEventTime);
         builder.append("; PackageName: ").append(mPackageName);
-        builder.append(" \n{\n");
         builder.append(super.toString());
-        builder.append("\n");
-        for (int i = 0; i < mRecords.size(); i++) {
-            AccessibilityRecord record = mRecords.get(i);
-            builder.append("  Record ");
-            builder.append(i);
-            builder.append(":");
-            builder.append(record.toString());
+        if (DEBUG) {
             builder.append("\n");
+            builder.append("; sourceAccessibilityWindowId: ").append(mSourceAccessibilityWindowId);
+            builder.append("; sourceAccessibilityViewId: ").append(mSourceAccessibilityViewId);
+            for (int i = 0; i < mRecords.size(); i++) {
+                AccessibilityRecord record = mRecords.get(i);
+                builder.append("  Record ");
+                builder.append(i);
+                builder.append(":");
+                builder.append(" [ ClassName: " + record.mClassName);
+                builder.append("; Text: " + record.mText);
+                builder.append("; ContentDescription: " + record.mContentDescription);
+                builder.append("; ItemCount: " + record.mItemCount);
+                builder.append("; CurrentItemIndex: " + record.mCurrentItemIndex);
+                builder.append("; IsEnabled: " + record.isEnabled());
+                builder.append("; IsPassword: " + record.isPassword());
+                builder.append("; IsChecked: " + record.isChecked());
+                builder.append("; IsFullScreen: " + record.isFullScreen());
+                builder.append("; BeforeText: " + record.mBeforeText);
+                builder.append("; FromIndex: " + record.mFromIndex);
+                builder.append("; AddedCount: " + record.mAddedCount);
+                builder.append("; RemovedCount: " + record.mRemovedCount);
+                builder.append("; ParcelableData: " + record.mParcelableData);
+                builder.append(" ]");
+                builder.append("\n");
+            }
+        } else {
+            builder.append("; recordCount: ").append(getAddedCount());
         }
-        builder.append("}\n");
         return builder.toString();
     }
 
diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java
index 88f8878..eece64a 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -28,10 +28,13 @@
 import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.util.Log;
+import android.view.IWindow;
+import android.view.View;
 
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * System level service that serves as an event dispatch for {@link AccessibilityEvent}s.
@@ -62,6 +65,21 @@
 
     boolean mIsEnabled;
 
+    final CopyOnWriteArrayList<AccessibilityStateChangeListener> mAccessibilityStateChangeListeners =
+        new CopyOnWriteArrayList<AccessibilityStateChangeListener>();
+
+    /**
+     * Listener for the accessibility state.
+     */
+    public interface AccessibilityStateChangeListener {
+        /**
+         * Called back on change in the accessibility state.
+         *
+         * @param enabled
+         */
+        public void onAccessibilityStateChanged(boolean enabled);
+    }
+
     final IAccessibilityManagerClient.Stub mClient = new IAccessibilityManagerClient.Stub() {
         public void setEnabled(boolean enabled) {
             mHandler.obtainMessage(DO_SET_ENABLED, enabled ? 1 : 0, 0).sendToTarget();
@@ -78,9 +96,8 @@
         public void handleMessage(Message message) {
             switch (message.what) {
                 case DO_SET_ENABLED :
-                    synchronized (mHandler) {
-                        mIsEnabled = (message.arg1 == 1);
-                    }
+                    final boolean isEnabled = (message.arg1 == 1);
+                    setAccessibilityState(isEnabled);
                     return;
                 default :
                     Log.w(LOG_TAG, "Unknown message type: " + message.what);
@@ -117,7 +134,8 @@
         mService = service;
 
         try {
-            mIsEnabled = mService.addClient(mClient);
+            final boolean isEnabled = mService.addClient(mClient);
+            setAccessibilityState(isEnabled);
         } catch (RemoteException re) {
             Log.e(LOG_TAG, "AccessibilityManagerService is dead", re);
         }
@@ -253,4 +271,81 @@
         }
         return Collections.unmodifiableList(services);
     }
+
+    /**
+     * Registers an {@link AccessibilityStateChangeListener}.
+     *
+     * @param listener The listener.
+     * @return True if successfully registered.
+     */
+    public boolean addAccessibilityStateChangeListener(
+            AccessibilityStateChangeListener listener) {
+        return mAccessibilityStateChangeListeners.add(listener);
+    }
+
+    /**
+     * Unregisters an {@link AccessibilityStateChangeListener}.
+     *
+     * @param listener The listener.
+     * @return True if successfully unregistered.
+     */
+    public boolean removeAccessibilityStateChangeListener(
+            AccessibilityStateChangeListener listener) {
+        return mAccessibilityStateChangeListeners.remove(listener);
+    }
+
+    /**
+     * Sets the enabled state.
+     *
+     * @param isEnabled The accessibility state.
+     */
+    private void setAccessibilityState(boolean isEnabled) {
+        synchronized (mHandler) {
+            if (isEnabled != mIsEnabled) {
+                mIsEnabled = isEnabled;
+                notifyAccessibilityStateChanged();
+            }
+        }
+    }
+
+    /**
+     * Notifies the registered {@link AccessibilityStateChangeListener}s.
+     */
+    private void notifyAccessibilityStateChanged() {
+        final int listenerCount = mAccessibilityStateChangeListeners.size();
+        for (int i = 0; i < listenerCount; i++) {
+            mAccessibilityStateChangeListeners.get(i).onAccessibilityStateChanged(mIsEnabled);
+        }
+    }
+
+    /**
+     * Adds an accessibility interaction connection interface for a given window.
+     * @param windowToken The window token to which a connection is added.
+     * @param connection The connection.
+     *
+     * @hide
+     */
+    public int addAccessibilityInteractionConnection(IWindow windowToken,
+            IAccessibilityInteractionConnection connection) {
+        try {
+            return mService.addAccessibilityInteractionConnection(windowToken, connection);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while adding an accessibility interaction connection. ", re);
+        }
+        return View.NO_ID;
+    }
+
+    /**
+     * Removed an accessibility interaction connection interface for a given window.
+     * @param windowToken The window token to which a connection is removed.
+     *
+     * @hide
+     */
+    public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+        try {
+            mService.removeAccessibilityInteractionConnection(windowToken);
+        } catch (RemoteException re) {
+            Log.e(LOG_TAG, "Error while removing an accessibility interaction connection. ", re);
+        }
+    }
 }
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl b/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl
new file mode 100644
index 0000000..59175ce
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.aidl
@@ -0,0 +1,19 @@
+/**
+ * Copyright (c) 2011, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+parcelable AccessibilityNodeInfo;
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
new file mode 100644
index 0000000..a4801fd
--- /dev/null
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.graphics.Rect;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.view.View;
+
+/**
+ * This class represents a node of the screen content. From the point of
+ * view of an accessibility service the screen content is presented as tree
+ * of accessibility nodes.
+ *
+ * TODO(svertoslavganov): Update the documentation, add sample, and describe
+ *                        the security policy.
+ */
+public class AccessibilityNodeInfo implements Parcelable {
+
+    private static final boolean DEBUG = false;
+
+    // Actions.
+
+    /**
+     * Action that focuses the node.
+     */
+    public static final int ACTION_FOCUS =  0x00000001;
+
+    /**
+     * Action that unfocuses the node.
+     */
+    public static final int ACTION_CLEAR_FOCUS =  0x00000002;
+
+    /**
+     * Action that selects the node.
+     */
+    public static final int ACTION_SELECT =  0x00000004;
+
+    /**
+     * Action that unselects the node.
+     */
+    public static final int ACTION_CLEAR_SELECTION =  0x00000008;
+
+    // Boolean attributes.
+
+    private static final int PROPERTY_CHECKABLE = 0x00000001;
+
+    private static final int PROPERTY_CHECKED = 0x00000002;
+
+    private static final int PROPERTY_FOCUSABLE = 0x00000004;
+
+    private static final int PROPERTY_FOCUSED = 0x00000008;
+
+    private static final int PROPERTY_SELECTED = 0x00000010;
+
+    private static final int PROPERTY_CLICKABLE = 0x00000020;
+
+    private static final int PROPERTY_LONG_CLICKABLE = 0x00000040;
+
+    private static final int PROPERTY_ENABLED = 0x00000080;
+
+    private static final int PROPERTY_PASSWORD = 0x00000100;
+
+    // Readable representations - lazily initialized.
+    private static SparseArray<String> sActionSymbolicNames;
+
+    // Housekeeping.
+    private static final int MAX_POOL_SIZE = 50;
+    private static final Object sPoolLock = new Object();
+    private static AccessibilityNodeInfo sPool;
+    private static int sPoolSize;
+    private AccessibilityNodeInfo mNext;
+    private boolean mIsInPool;
+    private boolean mSealed;
+
+    // Data.
+    private int mAccessibilityViewId = View.NO_ID;
+    private int mAccessibilityWindowId = View.NO_ID;
+    private int mParentAccessibilityViewId = View.NO_ID;
+    private int mBooleanProperties;
+    private final Rect mBounds = new Rect();
+
+    private CharSequence mPackageName;
+    private CharSequence mClassName;
+    private CharSequence mText;
+    private CharSequence mContentDescription;
+
+    private final SparseIntArray mChildAccessibilityIds = new SparseIntArray();
+    private int mActions;
+
+    private IAccessibilityServiceConnection mConnection;
+
+    /**
+     * Hide constructor from clients.
+     */
+    private AccessibilityNodeInfo() {
+        /* do nothing */
+    }
+
+    /**
+     * Sets the source.
+     *
+     * @param source The info source.
+     */
+    public void setSource(View source) {
+        enforceNotSealed();
+        mAccessibilityViewId = source.getAccessibilityViewId();
+        mAccessibilityWindowId = source.getAccessibilityWindowId();
+    }
+
+    /**
+     * Gets the id of the window from which the info comes from.
+     *
+     * @return The window id.
+     */
+    public int getAccessibilityWindowId() {
+        return mAccessibilityWindowId;
+    }
+
+    /**
+     * Gets the number of children.
+     *
+     * @return The child count.
+     */
+    public int getChildCount() {
+        return mChildAccessibilityIds.size();
+    }
+
+    /**
+     * Get the child at given index.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     * @param index The child index.
+     * @return The child node.
+     *
+     * @throws IllegalStateException If called outside of an AccessibilityService.
+     *
+     */
+    public AccessibilityNodeInfo getChild(int index) {
+        enforceSealed();
+        final int childAccessibilityViewId = mChildAccessibilityIds.get(index);
+        try {
+            return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
+                    childAccessibilityViewId);
+        } catch (RemoteException e) {
+             return null;
+        }
+    }
+
+    /**
+     * Adds a child.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param child The child.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void addChild(View child) {
+        enforceNotSealed();
+        final int childAccessibilityViewId = child.getAccessibilityViewId();
+        final int index = mChildAccessibilityIds.size();
+        mChildAccessibilityIds.put(index, childAccessibilityViewId);
+    }
+
+    /**
+     * Gets the actions that can be performed on the node.
+     *
+     * @return The bit mask of with actions.
+     *
+     * @see AccessibilityNodeInfo#ACTION_FOCUS
+     * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
+     * @see AccessibilityNodeInfo#ACTION_SELECT
+     * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
+     */
+    public int getActions() {
+        return mActions;
+    }
+
+    /**
+     * Adds an action that can be performed on the node.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param action The action.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void addAction(int action) {
+        enforceNotSealed();
+        mActions |= action;
+    }
+
+    /**
+     * Performs an action on the node.
+     * <p>
+     *   Note: An action can be performed only if the request is made
+     *   from an {@link android.accessibilityservice.AccessibilityService}.
+     * </p>
+     * @param action The action to perform.
+     * @return True if the action was performed.
+     *
+     * @throws IllegalStateException If called outside of an AccessibilityService.
+     */
+    public boolean performAction(int action) {
+        enforceSealed();
+        try {
+            return mConnection.performAccessibilityAction(mAccessibilityWindowId,
+                    mAccessibilityViewId, action);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * Gets the unique id identifying this node's parent.
+     * <p>
+     *   <strong>
+     *     It is a client responsibility to recycle the received info by
+     *     calling {@link AccessibilityNodeInfo#recycle()} to avoid creating
+     *     of multiple instances.
+     *   </strong>
+     * </p>
+     * @return The node's patent id.
+     */
+    public AccessibilityNodeInfo getParent() {
+        enforceSealed();
+        try {
+            return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId,
+                    mParentAccessibilityViewId);
+        } catch (RemoteException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Sets the parent.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param parent The parent.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setParent(View parent) {
+        enforceNotSealed();
+        mParentAccessibilityViewId = parent.getAccessibilityViewId();
+    }
+
+    /**
+     * Gets the node bounds in parent coordinates.
+     *
+     * @param outBounds The output node bounds.
+     */
+    public void getBounds(Rect outBounds) {
+        outBounds.set(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom);
+    }
+
+    /**
+     * Sets the node bounds in parent coordinates.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param bounds The node bounds.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setBounds(Rect bounds) {
+        enforceNotSealed();
+        mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
+    }
+
+    /**
+     * Gets whether this node is checkable.
+     *
+     * @return True if the node is checkable.
+     */
+    public boolean isCheckable() {
+        return getBooleanProperty(PROPERTY_CHECKABLE);
+    }
+
+    /**
+     * Sets whether this node is checkable.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param checkable True if the node is checkable.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setCheckable(boolean checkable) {
+        setBooleanProperty(PROPERTY_CHECKABLE, checkable);
+    }
+
+    /**
+     * Gets whether this node is checked.
+     *
+     * @return True if the node is checked.
+     */
+    public boolean isChecked() {
+        return getBooleanProperty(PROPERTY_CHECKED);
+    }
+
+    /**
+     * Sets whether this node is checked.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param checked True if the node is checked.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setChecked(boolean checked) {
+        setBooleanProperty(PROPERTY_CHECKED, checked);
+    }
+
+    /**
+     * Gets whether this node is focusable.
+     *
+     * @return True if the node is focusable.
+     */
+    public boolean isFocusable() {
+        return getBooleanProperty(PROPERTY_FOCUSABLE);
+    }
+
+    /**
+     * Sets whether this node is focusable.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param focusable True if the node is focusable.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setFocusable(boolean focusable) {
+        setBooleanProperty(PROPERTY_FOCUSABLE, focusable);
+    }
+
+    /**
+     * Gets whether this node is focused.
+     *
+     * @return True if the node is focused.
+     */
+    public boolean isFocused() {
+        return getBooleanProperty(PROPERTY_FOCUSED);
+    }
+
+    /**
+     * Sets whether this node is focused.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param focused True if the node is focused.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setFocused(boolean focused) {
+        setBooleanProperty(PROPERTY_FOCUSED, focused);
+    }
+
+    /**
+     * Gets whether this node is selected.
+     *
+     * @return True if the node is selected.
+     */
+    public boolean isSelected() {
+        return getBooleanProperty(PROPERTY_SELECTED);
+    }
+
+    /**
+     * Sets whether this node is selected.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param selected True if the node is selected.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setSelected(boolean selected) {
+        setBooleanProperty(PROPERTY_SELECTED, selected);
+    }
+
+    /**
+     * Gets whether this node is clickable.
+     *
+     * @return True if the node is clickable.
+     */
+    public boolean isClickable() {
+        return getBooleanProperty(PROPERTY_CLICKABLE);
+    }
+
+    /**
+     * Sets whether this node is clickable.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param clickable True if the node is clickable.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setClickable(boolean clickable) {
+        setBooleanProperty(PROPERTY_CLICKABLE, clickable);
+    }
+
+    /**
+     * Gets whether this node is long clickable.
+     *
+     * @return True if the node is long clickable.
+     */
+    public boolean isLongClickable() {
+        return getBooleanProperty(PROPERTY_LONG_CLICKABLE);
+    }
+
+    /**
+     * Sets whether this node is long clickable.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param longClickable True if the node is long clickable.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setLongClickable(boolean longClickable) {
+        setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable);
+    }
+
+    /**
+     * Gets whether this node is enabled.
+     *
+     * @return True if the node is enabled.
+     */
+    public boolean isEnabled() {
+        return getBooleanProperty(PROPERTY_ENABLED);
+    }
+
+    /**
+     * Sets whether this node is enabled.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param enabled True if the node is enabled.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setEnabled(boolean enabled) {
+        setBooleanProperty(PROPERTY_ENABLED, enabled);
+    }
+
+    /**
+     * Gets whether this node is a password.
+     *
+     * @return True if the node is a password.
+     */
+    public boolean isPassword() {
+        return getBooleanProperty(PROPERTY_PASSWORD);
+    }
+
+    /**
+     * Sets whether this node is a password.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param password True if the node is a password.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setPassword(boolean password) {
+        setBooleanProperty(PROPERTY_PASSWORD, password);
+    }
+
+    /**
+     * Gets the package this node comes from.
+     *
+     * @return The package name.
+     */
+    public CharSequence getPackageName() {
+        return mPackageName;
+    }
+
+    /**
+     * Sets the package this node comes from.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param packageName The package name.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setPackageName(CharSequence packageName) {
+        enforceNotSealed();
+        mPackageName = packageName;
+    }
+
+    /**
+     * Gets the class this node comes from.
+     *
+     * @return The class name.
+     */
+    public CharSequence getClassName() {
+        return mClassName;
+    }
+
+    /**
+     * Sets the class this node comes from.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param className The class name.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setClassName(CharSequence className) {
+        enforceNotSealed();
+        mClassName = className;
+    }
+
+    /**
+     * Gets the text of this node.
+     *
+     * @return The text.
+     */
+    public CharSequence getText() {
+        return mText;
+    }
+
+    /**
+     * Sets the text of this node.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param text The text.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setText(CharSequence text) {
+        enforceNotSealed();
+        mText = text;
+    }
+
+    /**
+     * Gets the content description of this node.
+     *
+     * @return The content description.
+     */
+    public CharSequence getContentDescription() {
+        return mContentDescription;
+    }
+
+    /**
+     * Sets the content description of this node.
+     * <p>
+     *   Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}.
+     *   This class is made immutable before being delivered to an AccessibilityService.
+     * </p>
+     * @param contentDescription The content description.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setContentDescription(CharSequence contentDescription) {
+        enforceNotSealed();
+        mContentDescription = contentDescription;
+    }
+
+    /**
+     * Gets the value of a boolean property.
+     *
+     * @param property The property.
+     * @return The value.
+     */
+    private boolean getBooleanProperty(int property) {
+        return (mBooleanProperties & property) != 0;
+    }
+
+    /**
+     * Sets a boolean property.
+     *
+     * @param property The property.
+     * @param value The value.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    private void setBooleanProperty(int property, boolean value) {
+        enforceNotSealed();
+        if (value) {
+            mBooleanProperties |= property;
+        } else {
+            mBooleanProperties &= ~property;
+        }
+    }
+
+    /**
+     * Sets the connection for interacting with the system.
+     *
+     * @param connection The client token.
+     *
+     * @hide
+     */
+    public final void setConnection(IAccessibilityServiceConnection connection) {
+        mConnection = connection;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int describeContents() {
+        return 0;
+    }
+
+    /**
+     * Sets if this instance is sealed.
+     *
+     * @param sealed Whether is sealed.
+     *
+     * @hide
+     */
+    public void setSealed(boolean sealed) {
+        mSealed = sealed;
+    }
+
+    /**
+     * Gets if this instance is sealed.
+     *
+     * @return Whether is sealed.
+     *
+     * @hide
+     */
+    public boolean isSealed() {
+        return mSealed;
+    }
+
+    /**
+     * Enforces that this instance is sealed.
+     *
+     * @throws IllegalStateException If this instance is not sealed.
+     *
+     * @hide
+     */
+    protected void enforceSealed() {
+        if (!isSealed()) {
+            throw new IllegalStateException("Cannot perform this "
+                    + "action on a not sealed instance.");
+        }
+    }
+
+    /**
+     * Enforces that this instance is not sealed.
+     *
+     * @throws IllegalStateException If this instance is sealed.
+     *
+     * @hide
+     */
+    protected void enforceNotSealed() {
+        if (isSealed()) {
+            throw new IllegalStateException("Cannot perform this "
+                    + "action on an sealed instance.");
+        }
+    }
+
+    /**
+     * Returns a cached instance if such is available otherwise a new one
+     * and sets the source.
+     *
+     * @return An instance.
+     *
+     * @see #setSource(View)
+     */
+    public static AccessibilityNodeInfo obtain(View source) {
+        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+        info.setSource(source);
+        return info;
+    }
+
+    /**
+     * Returns a cached instance if such is available otherwise a new one.
+     *
+     * @return An instance.
+     */
+    public static AccessibilityNodeInfo obtain() {
+        synchronized (sPoolLock) {
+            if (sPool != null) {
+                AccessibilityNodeInfo info = sPool;
+                sPool = sPool.mNext;
+                sPoolSize--;
+                info.mNext = null;
+                info.mIsInPool = false;
+                return info;
+            }
+            return new AccessibilityNodeInfo();
+        }
+    }
+
+    /**
+     * Return an instance back to be reused.
+     * <p>
+     * <b>Note: You must not touch the object after calling this function.</b>
+     *
+     * @throws IllegalStateException If the info is already recycled.
+     */
+    public void recycle() {
+        if (mIsInPool) {
+            throw new IllegalStateException("Info already recycled!");
+        }
+        clear();
+        synchronized (sPoolLock) {
+            if (sPoolSize <= MAX_POOL_SIZE) {
+                mNext = sPool;
+                sPool = this;
+                mIsInPool = true;
+                sPoolSize++;
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p>
+     *   <b>Note: After the instance is written to a parcel it is recycled.
+     *      You must not touch the object after calling this function.</b>
+     * </p>
+     */
+    public void writeToParcel(Parcel parcel, int flags) {
+        if (mConnection == null) {
+            parcel.writeInt(0);
+        } else {
+            parcel.writeInt(1);
+            parcel.writeStrongBinder(mConnection.asBinder());
+        }
+        parcel.writeInt(isSealed() ? 1 : 0);
+        parcel.writeInt(mAccessibilityViewId);
+        parcel.writeInt(mAccessibilityWindowId);
+        parcel.writeInt(mParentAccessibilityViewId);
+
+        SparseIntArray childIds = mChildAccessibilityIds;
+        final int childIdsSize = childIds.size();
+        parcel.writeInt(childIdsSize);
+        for (int i = 0; i < childIdsSize; i++) {
+            parcel.writeInt(childIds.valueAt(i));
+        }
+
+        parcel.writeInt(mBounds.top);
+        parcel.writeInt(mBounds.bottom);
+        parcel.writeInt(mBounds.left);
+        parcel.writeInt(mBounds.right);
+
+        parcel.writeInt(mActions);
+
+        parcel.writeInt(mBooleanProperties);
+
+        TextUtils.writeToParcel(mPackageName, parcel, flags);
+        TextUtils.writeToParcel(mClassName, parcel, flags);
+        TextUtils.writeToParcel(mText, parcel, flags);
+        TextUtils.writeToParcel(mContentDescription, parcel, flags);
+
+        // Since instances of this class are fetched via synchronous i.e. blocking
+        // calls in IPCs and we always recycle as soon as the instance is marshaled.
+        recycle();
+    }
+
+    /**
+     * Creates a new instance from a {@link Parcel}.
+     *
+     * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
+     */
+    private void initFromParcel(Parcel parcel) {
+        if (parcel.readInt() == 1) {
+            mConnection = IAccessibilityServiceConnection.Stub.asInterface(
+                    parcel.readStrongBinder());
+        }
+        mSealed = (parcel.readInt()  == 1);
+        mAccessibilityViewId = parcel.readInt();
+        mAccessibilityWindowId = parcel.readInt();
+        mParentAccessibilityViewId = parcel.readInt();
+
+        SparseIntArray childIds = mChildAccessibilityIds;
+        final int childrenSize = parcel.readInt();
+        for (int i = 0; i < childrenSize; i++) {
+            final int childId = parcel.readInt();
+            childIds.put(i, childId);
+        }
+
+        mBounds.top = parcel.readInt();
+        mBounds.bottom = parcel.readInt();
+        mBounds.left = parcel.readInt();
+        mBounds.right = parcel.readInt();
+
+        mActions = parcel.readInt();
+
+        mBooleanProperties = parcel.readInt();
+
+        mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+        mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
+    }
+
+    /**
+     * Clears the state of this instance.
+     */
+    private void clear() {
+        mSealed = false;
+        mConnection = null;
+        mAccessibilityViewId = View.NO_ID;
+        mParentAccessibilityViewId = View.NO_ID;
+        mChildAccessibilityIds.clear();
+        mBounds.set(0, 0, 0, 0);
+        mBooleanProperties = 0;
+        mPackageName = null;
+        mClassName = null;
+        mText = null;
+        mContentDescription = null;
+        mActions = 0;
+    }
+
+    /**
+     * Gets the human readable action symbolic name.
+     *
+     * @param action The action.
+     * @return The symbolic name.
+     */
+    private static String getActionSymbolicName(int action) {
+        SparseArray<String> actionSymbolicNames = sActionSymbolicNames;
+        if (actionSymbolicNames == null) {
+            actionSymbolicNames = sActionSymbolicNames = new SparseArray<String>();
+            actionSymbolicNames.put(ACTION_FOCUS, "ACTION_FOCUS");
+            actionSymbolicNames.put(ACTION_CLEAR_FOCUS, "ACTION_UNFOCUS");
+            actionSymbolicNames.put(ACTION_SELECT, "ACTION_SELECT");
+            actionSymbolicNames.put(ACTION_CLEAR_SELECTION, "ACTION_UNSELECT");
+        }
+        return actionSymbolicNames.get(action);
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + mAccessibilityViewId;
+        result = prime * result + mAccessibilityWindowId;
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder();
+        builder.append(super.toString());
+
+        if (DEBUG) {
+            builder.append("; accessibilityId: " + mAccessibilityViewId);
+            builder.append("; parentAccessibilityId: " + mParentAccessibilityViewId);
+            SparseIntArray childIds = mChildAccessibilityIds;
+            builder.append("; childAccessibilityIds: [");
+            for (int i = 0, count = childIds.size(); i < count; i++) {
+                builder.append(childIds.valueAt(i));
+                if (i < count - 1) {
+                    builder.append(", ");
+                }
+           }
+           builder.append("]");
+        }
+
+        builder.append("; bounds: " + mBounds);
+
+        builder.append("; packageName: ").append(mPackageName);
+        builder.append("; className: ").append(mClassName);
+        builder.append("; text: ").append(mText);
+        builder.append("; contentDescription: ").append(mContentDescription);
+
+        builder.append("; checkable: ").append(isCheckable());
+        builder.append("; checked: ").append(isChecked());
+        builder.append("; focusable: ").append(isFocusable());
+        builder.append("; focused: ").append(isFocused());
+        builder.append("; selected: ").append(isSelected());
+        builder.append("; clickable: ").append(isClickable());
+        builder.append("; longClickable: ").append(isLongClickable());
+        builder.append("; enabled: ").append(isEnabled());
+        builder.append("; password: ").append(isPassword());
+
+        builder.append("; [");
+
+        for (int actionBits = mActions; actionBits != 0;) {
+            final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
+            actionBits &= ~action;
+            builder.append(getActionSymbolicName(action));
+            if (actionBits != 0) {
+                builder.append(", ");
+            }
+        }
+
+        builder.append("]");
+
+        return builder.toString();
+    }
+
+    /**
+     * @see Parcelable.Creator
+     */
+    public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
+            new Parcelable.Creator<AccessibilityNodeInfo>() {
+        public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
+            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
+            info.initFromParcel(parcel);
+            return info;
+        }
+
+        public AccessibilityNodeInfo[] newArray(int size) {
+            return new AccessibilityNodeInfo[size];
+        }
+    };
+}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 7819b17..4bf03a7 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -38,13 +38,14 @@
     private static final int PROPERTY_PASSWORD = 0x00000004;
     private static final int PROPERTY_FULL_SCREEN = 0x00000080;
 
+    // Housekeeping
     private static final int MAX_POOL_SIZE = 10;
     private static final Object sPoolLock = new Object();
     private static AccessibilityRecord sPool;
     private static int sPoolSize;
-
     private AccessibilityRecord mNext;
     private boolean mIsInPool;
+    private boolean mSealed;
 
     protected int mBooleanProperties;
     protected int mCurrentItemIndex;
@@ -68,6 +69,26 @@
     }
 
     /**
+     * Initialize this record from another one.
+     *
+     * @param record The to initialize from.
+     */
+    void init(AccessibilityRecord record) {
+        mSealed = record.isSealed();
+        mBooleanProperties = record.mBooleanProperties;
+        mCurrentItemIndex = record.mCurrentItemIndex;
+        mItemCount = record.mItemCount;
+        mFromIndex = record.mFromIndex;
+        mAddedCount = record.mAddedCount;
+        mRemovedCount = record.mRemovedCount;
+        mClassName = record.mClassName;
+        mContentDescription = record.mContentDescription;
+        mBeforeText = record.mBeforeText;
+        mParcelableData = record.mParcelableData;
+        mText.addAll(record.mText);
+    }
+
+    /**
      * Gets if the source is checked.
      *
      * @return True if the view is checked, false otherwise.
@@ -80,8 +101,11 @@
      * Sets if the source is checked.
      *
      * @param isChecked True if the view is checked, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setChecked(boolean isChecked) {
+        enforceNotSealed();
         setBooleanProperty(PROPERTY_CHECKED, isChecked);
     }
 
@@ -98,8 +122,11 @@
      * Sets if the source is enabled.
      *
      * @param isEnabled True if the view is enabled, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setEnabled(boolean isEnabled) {
+        enforceNotSealed();
         setBooleanProperty(PROPERTY_ENABLED, isEnabled);
     }
 
@@ -116,21 +143,15 @@
      * Sets if the source is a password field.
      *
      * @param isPassword True if the view is a password field, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setPassword(boolean isPassword) {
+        enforceNotSealed();
         setBooleanProperty(PROPERTY_PASSWORD, isPassword);
     }
 
     /**
-     * Sets if the source is taking the entire screen.
-     *
-     * @param isFullScreen True if the source is full screen, false otherwise.
-     */
-    public void setFullScreen(boolean isFullScreen) {
-        setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
-    }
-
-    /**
      * Gets if the source is taking the entire screen.
      *
      * @return True if the source is full screen, false otherwise.
@@ -140,6 +161,18 @@
     }
 
     /**
+     * Sets if the source is taking the entire screen.
+     *
+     * @param isFullScreen True if the source is full screen, false otherwise.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
+     */
+    public void setFullScreen(boolean isFullScreen) {
+        enforceNotSealed();
+        setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
+    }
+
+    /**
      * Gets the number of items that can be visited.
      *
      * @return The number of items.
@@ -152,8 +185,11 @@
      * Sets the number of items that can be visited.
      *
      * @param itemCount The number of items.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setItemCount(int itemCount) {
+        enforceNotSealed();
         mItemCount = itemCount;
     }
 
@@ -170,8 +206,11 @@
      * Sets the index of the source in the list of items that can be visited.
      *
      * @param currentItemIndex The current item index.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setCurrentItemIndex(int currentItemIndex) {
+        enforceNotSealed();
         mCurrentItemIndex = currentItemIndex;
     }
 
@@ -188,8 +227,11 @@
      * Sets the index of the first character of the changed sequence.
      *
      * @param fromIndex The index of the first character.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setFromIndex(int fromIndex) {
+        enforceNotSealed();
         mFromIndex = fromIndex;
     }
 
@@ -206,8 +248,11 @@
      * Sets the number of added characters.
      *
      * @param addedCount The number of added characters.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setAddedCount(int addedCount) {
+        enforceNotSealed();
         mAddedCount = addedCount;
     }
 
@@ -224,8 +269,11 @@
      * Sets the number of removed characters.
      *
      * @param removedCount The number of removed characters.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setRemovedCount(int removedCount) {
+        enforceNotSealed();
         mRemovedCount = removedCount;
     }
 
@@ -242,8 +290,11 @@
      * Sets the class name of the source.
      *
      * @param className The lass name.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setClassName(CharSequence className) {
+        enforceNotSealed();
         mClassName = className;
     }
 
@@ -270,8 +321,11 @@
      * Sets the text before a change.
      *
      * @param beforeText The text before the change.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setBeforeText(CharSequence beforeText) {
+        enforceNotSealed();
         mBeforeText = beforeText;
     }
 
@@ -288,8 +342,11 @@
      * Sets the description of the source.
      *
      * @param contentDescription The description.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setContentDescription(CharSequence contentDescription) {
+        enforceNotSealed();
         mContentDescription = contentDescription;
     }
 
@@ -306,18 +363,71 @@
      * Sets the {@link Parcelable} data of the event.
      *
      * @param parcelableData The parcelable data.
+     *
+     * @throws IllegalStateException If called from an AccessibilityService.
      */
     public void setParcelableData(Parcelable parcelableData) {
+        enforceNotSealed();
         mParcelableData = parcelableData;
     }
 
     /**
+     * Sets if this instance is sealed.
+     *
+     * @param sealed Whether is sealed.
+     *
+     * @hide
+     */
+    public void setSealed(boolean sealed) {
+        mSealed = sealed;
+    }
+
+    /**
+     * Gets if this instance is sealed.
+     *
+     * @return Whether is sealed.
+     *
+     * @hide
+     */
+    public boolean isSealed() {
+        return mSealed;
+    }
+
+    /**
+     * Enforces that this instance is sealed.
+     *
+     * @throws IllegalStateException If this instance is not sealed.
+     *
+     * @hide
+     */
+    protected void enforceSealed() {
+        if (!isSealed()) {
+            throw new IllegalStateException("Cannot perform this "
+                    + "action on a not sealed instance.");
+        }
+    }
+
+    /**
+     * Enforces that this instance is not sealed.
+     *
+     * @throws IllegalStateException If this instance is sealed.
+     *
+     * @hide
+     */
+    protected void enforceNotSealed() {
+        if (isSealed()) {
+            throw new IllegalStateException("Cannot perform this "
+                    + "action on an sealed instance.");
+        }
+    }
+
+    /**
      * Gets the value of a boolean property.
      *
      * @param property The property.
      * @return The value.
      */
-    public boolean getBooleanProperty(int property) {
+    private boolean getBooleanProperty(int property) {
         return (mBooleanProperties & property) == property;
     }
 
@@ -337,6 +447,19 @@
 
     /**
      * Returns a cached instance if such is available or a new one is
+     * instantiated. The instance is initialized with data from the
+     * given record.
+     *
+     * @return An instance.
+     */
+    public static AccessibilityRecord obtain(AccessibilityRecord record) {
+       AccessibilityRecord clone = AccessibilityRecord.obtain();
+       clone.init(record);
+       return clone;
+    }
+
+    /**
+     * Returns a cached instance if such is available or a new one is
      * instantiated.
      *
      * @return An instance.
@@ -379,8 +502,11 @@
 
     /**
      * Clears the state of this instance.
+     *
+     * @hide
      */
     protected void clear() {
+        mSealed = false;
         mBooleanProperties = 0;
         mCurrentItemIndex = INVALID_POSITION;
         mItemCount = 0;
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
new file mode 100644
index 0000000..77dcd07
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnection.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
+
+/**
+ * Interface for interaction between the AccessibilityManagerService
+ * and the ViewRoot in a given window.
+ *
+ * @hide
+ */
+oneway interface IAccessibilityInteractionConnection {
+
+    void findAccessibilityNodeInfoByAccessibilityId(int accessibilityViewId, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback);
+
+    void findAccessibilityNodeInfoByViewId(int id, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback);
+
+    void findAccessibilityNodeInfosByViewText(String text, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback);
+
+    void performAccessibilityAction(int accessibilityId, int action, int interactionId,
+        IAccessibilityInteractionConnectionCallback callback);
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
new file mode 100644
index 0000000..9c5e8dc
--- /dev/null
+++ b/core/java/android/view/accessibility/IAccessibilityInteractionConnectionCallback.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.accessibility;
+
+import android.view.accessibility.AccessibilityNodeInfo;
+import java.util.List;
+
+/**
+ * Callback for specifying the result for an asynchronous request made
+ * via calling a method on IAccessibilityInteractionConnectionCallback.
+ *
+ * @hide
+ */
+oneway interface IAccessibilityInteractionConnectionCallback {
+
+    void setFindAccessibilityNodeInfoResult(in AccessibilityNodeInfo info, int interactionId);
+
+    void setFindAccessibilityNodeInfosResult(in List<AccessibilityNodeInfo> infos,
+        int interactionId);
+
+    void setPerformAccessibilityActionResult(boolean succeeded, int interactionId);
+}
diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl
index 6b2aae2..b14f02a 100644
--- a/core/java/android/view/accessibility/IAccessibilityManager.aidl
+++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl
@@ -18,8 +18,13 @@
 package android.view.accessibility;
 
 import android.accessibilityservice.AccessibilityServiceInfo;
+import android.accessibilityservice.IAccessibilityServiceConnection;
+import android.accessibilityservice.IEventListener;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
 import android.view.accessibility.IAccessibilityManagerClient;
+import android.view.IWindow;
 
 /**
  * Interface implemented by the AccessibilityManagerService called by
@@ -38,4 +43,11 @@
     List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(int feedbackType);
 
     void interrupt();
+
+    int addAccessibilityInteractionConnection(IWindow windowToken,
+        in IAccessibilityInteractionConnection connection);
+
+    void removeAccessibilityInteractionConnection(IWindow windowToken);
+
+    IAccessibilityServiceConnection registerEventListener(IEventListener client);
 }
diff --git a/core/java/android/view/inputmethod/InputMethodSubtype.java b/core/java/android/view/inputmethod/InputMethodSubtype.java
index 20cf93d..9d84c3e 100644
--- a/core/java/android/view/inputmethod/InputMethodSubtype.java
+++ b/core/java/android/view/inputmethod/InputMethodSubtype.java
@@ -28,6 +28,7 @@
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
 
 /**
  * This class is used to specify meta information of a subtype contained in an input method.
@@ -54,10 +55,11 @@
      * @param nameId The name of the subtype
      * @param iconId The icon of the subtype
      * @param locale The locale supported by the subtype
-     * @param modeId The mode supported by the subtype
+     * @param mode The mode supported by the subtype
      * @param extraValue The extra value of the subtype
      */
-    InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue) {
+    public InputMethodSubtype(
+            int nameId, int iconId, String locale, String mode, String extraValue) {
         this(nameId, iconId, locale, mode, extraValue, false);
     }
 
@@ -66,19 +68,20 @@
      * @param nameId The name of the subtype
      * @param iconId The icon of the subtype
      * @param locale The locale supported by the subtype
-     * @param modeId The mode supported by the subtype
+     * @param mode The mode supported by the subtype
      * @param extraValue The extra value of the subtype
      * @param isAuxiliary true when this subtype is one shot subtype.
      */
-    InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
+    public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
             boolean isAuxiliary) {
         mSubtypeNameResId = nameId;
         mSubtypeIconResId = iconId;
         mSubtypeLocale = locale != null ? locale : "";
         mSubtypeMode = mode != null ? mode : "";
         mSubtypeExtraValue = extraValue != null ? extraValue : "";
-        mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue);
         mIsAuxiliary = isAuxiliary;
+        mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
+                mIsAuxiliary);
     }
 
     InputMethodSubtype(Parcel source) {
@@ -91,8 +94,9 @@
         mSubtypeMode = s != null ? s : "";
         s = source.readString();
         mSubtypeExtraValue = s != null ? s : "";
-        mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue);
         mIsAuxiliary = (source.readInt() == 1);
+        mSubtypeHashCode = hashCodeInternal(mSubtypeLocale, mSubtypeMode, mSubtypeExtraValue,
+                mIsAuxiliary);
     }
 
     /**
@@ -151,16 +155,17 @@
      */
     public CharSequence getDisplayName(
             Context context, String packageName, ApplicationInfo appInfo) {
-        final String locale = context.getResources().getConfiguration().locale.getDisplayName();
+        final Locale locale = constructLocaleFromString(mSubtypeLocale);
+        final String localeStr = locale != null ? locale.getDisplayName() : mSubtypeLocale;
         if (mSubtypeNameResId == 0) {
-            return locale;
+            return localeStr;
         }
         final String subtypeName = context.getPackageManager().getText(
                 packageName, mSubtypeNameResId, appInfo).toString();
         if (!TextUtils.isEmpty(subtypeName)) {
-            return String.format(subtypeName, locale);
+            return String.format(subtypeName, localeStr);
         } else {
-            return locale;
+            return localeStr;
         }
     }
 
@@ -218,7 +223,8 @@
                 && (subtype.getMode().equals(getMode()))
                 && (subtype.getIconResId() == getIconResId())
                 && (subtype.getLocale().equals(getLocale()))
-                && (subtype.getExtraValue().equals(getExtraValue()));
+                && (subtype.getExtraValue().equals(getExtraValue()))
+                && (subtype.isAuxiliary() == isAuxiliary());
         }
         return false;
     }
@@ -251,8 +257,25 @@
         }
     };
 
-    private static int hashCodeInternal(String locale, String mode, String extraValue) {
-        return Arrays.hashCode(new Object[] {locale, mode, extraValue});
+    private static Locale constructLocaleFromString(String localeStr) {
+        if (TextUtils.isEmpty(localeStr))
+            return null;
+        String[] localeParams = localeStr.split("_", 3);
+        // The length of localeStr is guaranteed to always return a 1 <= value <= 3
+        // because localeStr is not empty.
+        if (localeParams.length == 1) {
+            return new Locale(localeParams[0]);
+        } else if (localeParams.length == 2) {
+            return new Locale(localeParams[0], localeParams[1]);
+        } else if (localeParams.length == 3) {
+            return new Locale(localeParams[0], localeParams[1], localeParams[2]);
+        }
+        return null;
+    }
+
+    private static int hashCodeInternal(String locale, String mode, String extraValue,
+            boolean isAuxiliary) {
+        return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary});
     }
 
     /**
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 9f2fd12..2f4774f 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -44,6 +44,9 @@
 import java.io.InputStream;
 import java.lang.ref.WeakReference;
 import java.net.URLEncoder;
+import java.nio.charset.Charsets;
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.HashMap;
@@ -1141,7 +1144,7 @@
     }
 
     /**
-     * Called by JNI when the native HTTP(S) stack gets an invalid cert chain.
+     * Called by JNI when the native HTTPS stack gets an invalid cert chain.
      *
      * We delegate the request to CallbackProxy, and route its response to
      * {@link #nativeSslCertErrorProceed(int)} or
@@ -1182,6 +1185,32 @@
     }
 
     /**
+     * Called by JNI when the native HTTPS stack gets a client
+     * certificate request.
+     *
+     * We delegate the request to CallbackProxy, and route its response to
+     * {@link #nativeSslClientCert(int, X509Certificate)}.
+     */
+    private void requestClientCert(int handle, byte[] host_and_port_bytes) {
+        String host_and_port = new String(host_and_port_bytes, Charsets.UTF_8);
+        SslClientCertLookupTable table = SslClientCertLookupTable.getInstance();
+        if (table.IsAllowed(host_and_port)) {
+            // previously allowed
+            nativeSslClientCert(handle,
+                                table.PrivateKey(host_and_port),
+                                table.CertificateChain(host_and_port));
+        } else if (table.IsDenied(host_and_port)) {
+            // previously denied
+            nativeSslClientCert(handle, null, null);
+        } else {
+            // previously ignored or new
+            mCallbackProxy.onReceivedClientCertRequest(
+                    new ClientCertRequestHandler(this, handle, host_and_port, table),
+                    host_and_port);
+        }
+    }
+
+    /**
      * Called by JNI when the native HTTP stack needs to download a file.
      *
      * We delegate the request to CallbackProxy, which owns the current app's
@@ -1366,4 +1395,8 @@
 
     private native void nativeSslCertErrorProceed(int handle);
     private native void nativeSslCertErrorCancel(int handle, int cert_error);
+
+    native void nativeSslClientCert(int handle,
+                                    byte[] pkcs8EncodedPrivateKey,
+                                    byte[][] asn1DerEncodedCertificateChain);
 }
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 23fd12d..f7d55f6 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -118,6 +118,7 @@
     private static final int SET_INSTALLABLE_WEBAPP              = 138;
     private static final int NOTIFY_SEARCHBOX_LISTENERS          = 139;
     private static final int AUTO_LOGIN                          = 140;
+    private static final int CLIENT_CERT_REQUEST                 = 141;
 
     // Message triggered by the client to resume execution
     private static final int NOTIFY                              = 200;
@@ -273,7 +274,7 @@
                     mWebViewClient.onPageFinished(mWebView, finishedUrl);
                 }
                 break;
-                
+
             case RECEIVED_ICON:
                 if (mWebChromeClient != null) {
                     mWebChromeClient.onReceivedIcon(mWebView, (Bitmap) msg.obj);
@@ -340,7 +341,7 @@
 
             case SSL_ERROR:
                 if (mWebViewClient != null) {
-                    HashMap<String, Object> map = 
+                    HashMap<String, Object> map =
                         (HashMap<String, Object>) msg.obj;
                     mWebViewClient.onReceivedSslError(mWebView,
                             (SslErrorHandler) map.get("handler"),
@@ -348,6 +349,16 @@
                 }
                 break;
 
+            case CLIENT_CERT_REQUEST:
+                if (mWebViewClient != null) {
+                    HashMap<String, Object> map =
+                        (HashMap<String, Object>) msg.obj;
+                    mWebViewClient.onReceivedClientCertRequest(mWebView,
+                            (ClientCertRequestHandler) map.get("handler"),
+                            (String) map.get("host_and_port"));
+                }
+                break;
+
             case PROGRESS:
                 // Synchronize to ensure mLatestProgress is not modified after
                 // setProgress is called and before mProgressUpdatePending is
@@ -543,14 +554,14 @@
                         new AlertDialog.Builder(mContext)
                                 .setTitle(getJsDialogTitle(url))
                                 .setMessage(message)
-                                .setPositiveButton(R.string.ok, 
+                                .setPositiveButton(R.string.ok,
                                         new DialogInterface.OnClickListener() {
                                             public void onClick(
                                                     DialogInterface dialog,
                                                     int which) {
                                                 res.confirm();
                                             }})
-                                .setNegativeButton(R.string.cancel, 
+                                .setNegativeButton(R.string.cancel,
                                         new DialogInterface.OnClickListener() {
                                             public void onClick(
                                                     DialogInterface dialog,
@@ -904,7 +915,7 @@
         if (PERF_PROBE) {
             // un-comment this if PERF_PROBE is true
 //            Looper.myQueue().setWaitCallback(null);
-            Log.d("WebCore", "WebCore thread used " + 
+            Log.d("WebCore", "WebCore thread used " +
                     (SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime)
                     + " ms and idled " + mWebCoreIdleTime + " ms");
             Network.getInstance(mContext).stopTiming();
@@ -934,7 +945,7 @@
         sendMessage(msg);
     }
 
-    public void onFormResubmission(Message dontResend, 
+    public void onFormResubmission(Message dontResend,
             Message resend) {
         // Do an unsynchronized quick check to avoid posting if no callback has
         // been set.
@@ -998,7 +1009,6 @@
             return;
         }
         Message msg = obtainMessage(SSL_ERROR);
-        //, handler);
         HashMap<String, Object> map = new HashMap();
         map.put("handler", handler);
         map.put("error", error);
@@ -1006,6 +1016,23 @@
         sendMessage(msg);
     }
     /**
+     * @hide
+     */
+    public void onReceivedClientCertRequest(ClientCertRequestHandler handler, String host_and_port) {
+        // Do an unsynchronized quick check to avoid posting if no callback has
+        // been set.
+        if (mWebViewClient == null) {
+            handler.cancel();
+            return;
+        }
+        Message msg = obtainMessage(CLIENT_CERT_REQUEST);
+        HashMap<String, Object> map = new HashMap();
+        map.put("handler", handler);
+        map.put("host_and_port", host_and_port);
+        msg.obj = map;
+        sendMessage(msg);
+    }
+    /**
      * @hide - hide this because it contains a parameter of type SslCertificate,
      * which is located in a hidden package.
      */
diff --git a/core/java/android/webkit/ClientCertRequestHandler.java b/core/java/android/webkit/ClientCertRequestHandler.java
new file mode 100644
index 0000000..3a71e7e
--- /dev/null
+++ b/core/java/android/webkit/ClientCertRequestHandler.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import java.security.PrivateKey;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+import org.apache.harmony.xnet.provider.jsse.NativeCrypto;
+
+/**
+ * ClientCertRequestHandler: class responsible for handling client
+ * certificate requests.  This class is passed as a parameter to
+ * BrowserCallback.displayClientCertRequestDialog and is meant to
+ * receive the user's response.
+ *
+ * @hide
+ */
+public final class ClientCertRequestHandler {
+
+    private final BrowserFrame mBrowserFrame;
+    private final int mHandle;
+    private final String mHostAndPort;
+    private final SslClientCertLookupTable mTable;
+    ClientCertRequestHandler(BrowserFrame browserFrame,
+                             int handle,
+                             String host_and_port,
+                             SslClientCertLookupTable table) {
+        mBrowserFrame = browserFrame;
+        mHandle = handle;
+        mHostAndPort = host_and_port;
+        mTable = table;
+    }
+
+    /**
+     * Proceed with the specified private key and client certificate chain.
+     */
+    public void proceed(PrivateKey privateKey, X509Certificate[] chain) {
+        byte[] privateKeyBytes = privateKey.getEncoded();
+        byte[][] chainBytes;
+        try {
+            chainBytes = NativeCrypto.encodeCertificates(chain);
+        } catch (CertificateEncodingException e) {
+            mBrowserFrame.nativeSslClientCert(mHandle, null, null);
+            return;
+        }
+        mTable.Allow(mHostAndPort, privateKeyBytes, chainBytes);
+        mBrowserFrame.nativeSslClientCert(mHandle, privateKeyBytes, chainBytes);
+    }
+
+    /**
+     * Igore the request for now, the user may be prompted again.
+     */
+    public void ignore() {
+        mBrowserFrame.nativeSslClientCert(mHandle, null, null);
+    }
+
+    /**
+     * Cancel this request, remember the users negative choice.
+     */
+    public void cancel() {
+        mTable.Deny(mHostAndPort);
+        mBrowserFrame.nativeSslClientCert(mHandle, null, null);
+    }
+}
diff --git a/core/java/android/webkit/SslCertLookupTable.java b/core/java/android/webkit/SslCertLookupTable.java
index abf612e..faff110 100644
--- a/core/java/android/webkit/SslCertLookupTable.java
+++ b/core/java/android/webkit/SslCertLookupTable.java
@@ -19,11 +19,11 @@
 import android.os.Bundle;
 import android.net.http.SslError;
 
-/*
+/**
  * A simple class to store the wrong certificates that user is aware but
  * chose to proceed.
  */
-class SslCertLookupTable {
+final class SslCertLookupTable {
     private static SslCertLookupTable sTable;
     private final Bundle table;
 
diff --git a/core/java/android/webkit/SslClientCertLookupTable.java b/core/java/android/webkit/SslClientCertLookupTable.java
new file mode 100644
index 0000000..630debd
--- /dev/null
+++ b/core/java/android/webkit/SslClientCertLookupTable.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * A simple class to store client certificates that user has chosen.
+ */
+final class SslClientCertLookupTable {
+    private static SslClientCertLookupTable sTable;
+    private final Map<String, byte[]> privateKeys;
+    private final Map<String, byte[][]> certificateChains;
+    private final Set<String> denied;
+
+    public static synchronized SslClientCertLookupTable getInstance() {
+        if (sTable == null) {
+            sTable = new SslClientCertLookupTable();
+        }
+        return sTable;
+    }
+
+    private SslClientCertLookupTable() {
+        privateKeys = new HashMap<String, byte[]>();
+        certificateChains = new HashMap<String, byte[][]>();
+        denied = new HashSet<String>();
+    }
+
+    public void Allow(String host_and_port, byte[] privateKey, byte[][] chain) {
+        privateKeys.put(host_and_port, privateKey);
+        certificateChains.put(host_and_port, chain);
+        denied.remove(host_and_port);
+    }
+
+    public void Deny(String host_and_port) {
+        privateKeys.remove(host_and_port);
+        certificateChains.remove(host_and_port);
+        denied.add(host_and_port);
+    }
+
+    public boolean IsAllowed(String host_and_port) {
+        return privateKeys.containsKey(host_and_port);
+    }
+
+    public boolean IsDenied(String host_and_port) {
+        return denied.contains(host_and_port);
+    }
+
+    public byte[] PrivateKey(String host_and_port) {
+        return privateKeys.get(host_and_port);
+    }
+
+    public byte[][] CertificateChain(String host_and_port) {
+        return certificateChains.get(host_and_port);
+    }
+}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 66fcc27..c361a4a 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -1234,7 +1234,7 @@
      */
     @Deprecated
     public synchronized void setPluginsEnabled(boolean flag) {
-        setPluginState(PluginState.ON);
+        setPluginState(flag ? PluginState.ON : PluginState.OFF);
     }
 
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 9e61ecf..99570fa 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -44,7 +44,6 @@
 import android.graphics.Rect;
 import android.graphics.RectF;
 import android.graphics.Region;
-import android.graphics.SurfaceTexture;
 import android.graphics.Shader;
 import android.graphics.drawable.Drawable;
 import android.net.Proxy;
@@ -99,6 +98,8 @@
 import android.widget.OverScroller;
 import android.widget.Toast;
 
+import junit.framework.Assert;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -117,8 +118,6 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import junit.framework.Assert;
-
 /**
  * <p>A View that displays web pages. This class is the basis upon which you
  * can roll your own web browser or simply display some online content within your Activity.
@@ -173,7 +172,7 @@
  *
  * // OR, you can also load from an HTML string:
  * String summary = "&lt;html>&lt;body>You scored &lt;b>192&lt;/b> points.&lt;/body>&lt;/html>";
- * webview.loadData(summary, "text/html", "utf-8");
+ * webview.loadData(summary, "text/html", null);
  * // ... although note that there are restrictions on what this HTML can do.
  * // See the JavaDocs for {@link #loadData(String,String,String) loadData()} and {@link
  * #loadDataWithBaseURL(String,String,String,String,String) loadDataWithBaseURL()} for more info.
@@ -1818,9 +1817,9 @@
      */
     public boolean loadViewState(InputStream stream) {
         try {
-            DrawData draw = ViewStateSerializer.deserializeViewState(stream, this);
+            mLoadedPicture = ViewStateSerializer.deserializeViewState(stream, this);
             mBlockWebkitViewMessages = true;
-            setNewPicture(draw);
+            setNewPicture(mLoadedPicture, true);
             return true;
         } catch (IOException e) {
             Log.w(LOGTAG, "Failed to loadViewState", e);
@@ -1835,6 +1834,7 @@
      */
     public void clearViewState() {
         mBlockWebkitViewMessages = false;
+        mLoadedPicture = null;
         invalidate();
     }
 
@@ -1965,14 +1965,17 @@
     }
 
     /**
-     * Load the given data into the WebView. This will load the data into
-     * WebView using the data: scheme. Content loaded through this mechanism
-     * does not have the ability to load content from the network.
-     * @param data A String of data in the given encoding. The date must
-     * be URI-escaped -- '#', '%', '\', '?' should be replaced by %23, %25,
-     * %27, %3f respectively.
-     * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
-     * @param encoding The encoding of the data. i.e. utf-8, base64
+     * Load the given data into the WebView using a 'data' scheme URL. Content
+     * loaded in this way does not have the ability to load content from the
+     * network.
+     * <p>
+     * If the value of the encoding parameter is 'base64', then the data must
+     * be encoded as base64. Otherwise, the data must use ASCII encoding for
+     * octets inside the range of safe URL characters and use the standard %xx
+     * hex encoding of URLs for octets outside that range.
+     * @param data A String of data in the given encoding.
+     * @param mimeType The MIMEType of the data, e.g. 'text/html'.
+     * @param encoding The encoding of the data.
      */
     public void loadData(String data, String mimeType, String encoding) {
         checkThread();
@@ -1980,7 +1983,14 @@
     }
 
     private void loadDataImpl(String data, String mimeType, String encoding) {
-        loadUrlImpl("data:" + mimeType + ";" + encoding + "," + data);
+        StringBuilder dataUrl = new StringBuilder("data:");
+        dataUrl.append(mimeType);
+        if ("base64".equals(encoding)) {
+            dataUrl.append(";base64");
+        }
+        dataUrl.append(",");
+        dataUrl.append(data);
+        loadUrlImpl(dataUrl.toString());
     }
 
     /**
@@ -1989,13 +1999,9 @@
      * that is loaded through this interface. As such, it is used to resolve any
      * relative URLs. The historyUrl is used for the history entry.
      * <p>
-     * Note for post 1.0. Due to the change in the WebKit, the access to asset
-     * files through "file:///android_asset/" for the sub resources is more
-     * restricted. If you provide null or empty string as baseUrl, you won't be
-     * able to access asset files. If the baseUrl is anything other than
-     * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
-     * sub resources.
-     *
+     * Note that content specified in this way can access local device files
+     * (via 'file' scheme URLs) only if baseUrl specifies a scheme other than
+     * 'http', 'https', 'ftp', 'ftps', 'about' or 'javascript'.
      * @param baseUrl Url to resolve relative paths with, if null defaults to
      *            "about:blank"
      * @param data A String of data in the given encoding.
@@ -5531,6 +5537,13 @@
         }
 
         mZoomManager.onSizeChanged(w, h, ow, oh);
+
+        if (mLoadedPicture != null && mDelaySetPicture == null) {
+            // Size changes normally result in a new picture
+            // Re-set the loaded picture to simulate that
+            // However, do not update the base layer as that hasn't changed
+            setNewPicture(mLoadedPicture, false);
+        }
     }
 
     @Override
@@ -6461,7 +6474,7 @@
                     if (hscroll != 0 || vscroll != 0) {
                         final int vdelta = (int) (vscroll * getVerticalScrollFactor());
                         final int hdelta = (int) (hscroll * getHorizontalScrollFactor());
-                        if (pinScrollBy(hdelta, vdelta, true, 0)) {
+                        if (pinScrollBy(hdelta, vdelta, fa