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, false, 0)) {
                             return true;
                         }
                     }
@@ -6503,6 +6516,9 @@
     // arrow key events, not trackball events, from one child to the next
     private boolean mMapTrackballToArrowKeys = true;
 
+    private DrawData mDelaySetPicture;
+    private DrawData mLoadedPicture;
+
     public void setMapTrackballToArrowKeys(boolean setMap) {
         checkThread();
         mMapTrackballToArrowKeys = setMap;
@@ -6836,11 +6852,6 @@
                 vx = 0;
             }
         }
-        if (true /* EMG release: make our fling more like Maps' */) {
-            // maps cuts their velocity in half
-            vx = vx * 3 / 4;
-            vy = vy * 3 / 4;
-        }
         if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
             WebViewCore.resumePriority();
             if (!mSelectingText) {
@@ -7912,7 +7923,8 @@
                 // after WebView's destroy() is called, skip handling messages.
                 return;
             }
-            if (mBlockWebkitViewMessages) {
+            if (mBlockWebkitViewMessages
+                    && msg.what != WEBCORE_INITIALIZED_MSG_ID) {
                 // Blocking messages from webkit
                 return;
             }
@@ -8051,7 +8063,7 @@
                 case NEW_PICTURE_MSG_ID: {
                     // called for new content
                     final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
-                    setNewPicture(draw);
+                    setNewPicture(draw, true);
                     break;
                 }
                 case WEBCORE_INITIALIZED_MSG_ID:
@@ -8060,6 +8072,10 @@
                             BrowserFrame.DRAWABLEDIR, mContext);
                     AssetManager am = mContext.getAssets();
                     nativeCreate(msg.arg1, drawableDir, am);
+                    if (mDelaySetPicture != null) {
+                        setNewPicture(mDelaySetPicture, true);
+                        mDelaySetPicture = null;
+                    }
                     break;
                 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
                     // Make sure that the textfield is currently focused
@@ -8370,12 +8386,23 @@
         }
     }
 
-    void setNewPicture(final WebViewCore.DrawData draw) {
+    void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) {
+        if (mNativeClass == 0) {
+            if (mDelaySetPicture != null) {
+                throw new IllegalStateException("Tried to setNewPicture with"
+                        + " a delay picture already set! (memory leak)");
+            }
+            // Not initialized yet, delay set
+            mDelaySetPicture = draw;
+            return;
+        }
         WebViewCore.ViewState viewState = draw.mViewState;
         boolean isPictureAfterFirstLayout = viewState != null;
-        setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
-                getSettings().getShowVisualIndicator(),
-                isPictureAfterFirstLayout);
+        if (updateBaseLayer) {
+            setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
+                    getSettings().getShowVisualIndicator(),
+                    isPictureAfterFirstLayout);
+        }
         final Point viewSize = draw.mViewSize;
         if (isPictureAfterFirstLayout) {
             // Reset the last sent data here since dealing with new page.
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 65026a5..d3be2bf 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -30,7 +30,7 @@
      * proper handler for the url. If WebViewClient is provided, return true
      * means the host application handles the url, while return false means the
      * current WebView handles the url.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param url The url to be loaded.
      * @return True if the host application wants to leave the current WebView
@@ -46,7 +46,7 @@
      * framesets will call onPageStarted one time for the main frame. This also
      * means that onPageStarted will not be called when the contents of an
      * embedded frame changes, i.e. clicking a link whose target is an iframe.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param url The url to be loaded.
      * @param favicon The favicon for this page if it already exists in the
@@ -60,7 +60,7 @@
      * is called only for main frame. When onPageFinished() is called, the
      * rendering picture may not be updated yet. To get the notification for the
      * new Picture, use {@link WebView.PictureListener#onNewPicture}.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param url The url of the page.
      */
@@ -70,7 +70,7 @@
     /**
      * Notify the host application that the WebView will load the resource
      * specified by the given url.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param url The url of the resource the WebView will load.
      */
@@ -102,7 +102,7 @@
      * HTTP redirects. As the host application if it would like to continue
      * trying to load the resource. The default behavior is to send the cancel
      * message.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param cancelMsg The message to send if the host wants to cancel
      * @param continueMsg The message to send if the host wants to continue
@@ -164,7 +164,7 @@
      * As the host application if the browser should resend data as the
      * requested page was a result of a POST. The default is to not resend the
      * data.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param dontResend The message to send if the browser should not resend
      * @param resend The message to send if the browser should resend data
@@ -176,7 +176,7 @@
 
     /**
      * Notify the host application to update its visited links database.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param url The url being visited.
      * @param isReload True if this url is being reloaded.
@@ -186,12 +186,12 @@
     }
 
     /**
-     * Notify the host application to handle a ssl certificate error request
+     * Notify the host application to handle a SSL certificate error request
      * (display the error to the user and ask whether to proceed or not). The
      * host application has to call either handler.cancel() or handler.proceed()
      * as the connection is suspended and waiting for the response. The default
      * behavior is to cancel the load.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param handler An SslErrorHandler object that will handle the user's
      *            response.
@@ -203,9 +203,29 @@
     }
 
     /**
+     * Notify the host application to handle a SSL client certificate
+     * request (display the request to the user and ask whether to
+     * proceed with a client certificate or not). The host application
+     * has to call either handler.cancel() or handler.proceed() as the
+     * connection is suspended and waiting for the response. The
+     * default behavior is to cancel, returning no client certificate.
+     *
+     * @param view The WebView that is initiating the callback.
+     * @param handler An ClientCertRequestHandler object that will
+     *            handle the user's response.
+     * @param host_and_port The host and port of the requesting server.
+     *
+     * @hide
+     */
+    public void onReceivedClientCertRequest(WebView view,
+            ClientCertRequestHandler handler, String host_and_port) {
+        handler.cancel();
+    }
+
+    /**
      * Notify the host application to handle an authentication request. The
      * default behavior is to cancel the request.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param handler The HttpAuthHandler that will handle the user's response.
      * @param host The host requiring authentication.
@@ -223,7 +243,7 @@
      * true, WebView will not handle the key event. If return false, WebView
      * will always handle the key event, so none of the super in the view chain
      * will see the key event. The default behavior returns false.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param event The key event.
      * @return True if the host application wants to handle the key event
@@ -239,7 +259,7 @@
      * or if shouldOverrideKeyEvent returns true. This is called asynchronously
      * from where the key is dispatched. It gives the host application an chance
      * to handle the unhandled key events.
-     * 
+     *
      * @param view The WebView that is initiating the callback.
      * @param event The key event.
      */
@@ -249,7 +269,7 @@
     /**
      * Notify the host application that the scale applied to the WebView has
      * changed.
-     * 
+     *
      * @param view he WebView that is initiating the callback.
      * @param oldScale The old scale factor
      * @param newScale The new scale factor
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 16e4571..309e6ff 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1141,16 +1141,13 @@
                             if (baseUrl != null) {
                                 int i = baseUrl.indexOf(':');
                                 if (i > 0) {
-                                    /*
-                                     * In 1.0, {@link
-                                     * WebView#loadDataWithBaseURL} can access
-                                     * local asset files as long as the data is
-                                     * valid. In the new WebKit, the restriction
-                                     * is tightened. To be compatible with 1.0,
-                                     * we automatically add the scheme of the
-                                     * baseUrl for local access as long as it is
-                                     * not http(s)/ftp(s)/about/javascript
-                                     */
+                                    // In 1.0, WebView.loadDataWithBaseURL() could access local
+                                    // asset files using 'file' scheme URLs as long as the data is
+                                    // valid. Later versions of WebKit have tightened the
+                                    // restriction around when pages can access such local URLs.
+                                    // To maintain compatibility with 1.0, we register the scheme of
+                                    // the baseUrl to be considered local, as long as it is not
+                                    // http(s)/ftp(s)/about/javascript.
                                     String scheme = baseUrl.substring(0, i);
                                     if (!scheme.startsWith("http") &&
                                             !scheme.startsWith("ftp") &&
@@ -1903,7 +1900,7 @@
                 width = mViewportWidth;
             } else {
                 // For mobile web site.
-                width = viewWidth;
+                width = mWebView.getViewWidth();
             }
         }
         return width;
diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java
index 7803391..fe6fb2f 100644
--- a/core/java/android/webkit/ZoomManager.java
+++ b/core/java/android/webkit/ZoomManager.java
@@ -634,8 +634,17 @@
         } else {
             newTextWrapScale = mActualScale;
         }
+        final boolean firstTimeReflow = !exceedsMinScaleIncrement(mActualScale, mTextWrapScale);
+        if (firstTimeReflow || mInZoomOverview) {
+            // In case first time reflow or in zoom overview mode, let reflow and zoom
+            // happen at the same time.
+            mTextWrapScale = newTextWrapScale;
+        }
         if (settings.isNarrowColumnLayout()
-                && exceedsMinScaleIncrement(mTextWrapScale, newTextWrapScale)) {
+                && exceedsMinScaleIncrement(mTextWrapScale, newTextWrapScale)
+                && !firstTimeReflow
+                && !mInZoomOverview) {
+            // Reflow only.
             mTextWrapScale = newTextWrapScale;
             refreshZoomScale(true);
         } else if (!mInZoomOverview && willScaleTriggerZoom(getZoomOverviewScale())) {
diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java
index 4ee16e7..6b498fe 100644
--- a/core/java/android/widget/FrameLayout.java
+++ b/core/java/android/widget/FrameLayout.java
@@ -115,7 +115,7 @@
     }
 
     /**
-     * Describes how the foreground is positioned. Defaults to BEFORE and TOP.
+     * Describes how the foreground is positioned. Defaults to START and TOP.
      *
      * @param foregroundGravity See {@link android.view.Gravity}
      *
@@ -125,7 +125,7 @@
     public void setForegroundGravity(int foregroundGravity) {
         if (mForegroundGravity != foregroundGravity) {
             if ((foregroundGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-                foregroundGravity |= Gravity.BEFORE;
+                foregroundGravity |= Gravity.START;
             }
 
             if ((foregroundGravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java
index b99cd7f..9834485 100644
--- a/core/java/android/widget/GridLayout.java
+++ b/core/java/android/widget/GridLayout.java
@@ -48,11 +48,11 @@
  * <p>
  * The grid is composed of a set of infinitely thin lines that separate the
  * viewing area into <em>cells</em>. Throughout the API, grid lines are referenced
- * by grid <em>indices</em>. A grid with <code>N</code> columns
- * has <code>N + 1</code> grid indices that run from <code>0</code>
- * through <code>N</code> inclusive. Regardless of how GridLayout is
- * configured, grid index <code>0</code> is fixed to the leading edge of the
- * container and grid index <code>N</code> is fixed to its trailing edge
+ * by grid <em>indices</em>. A grid with {@code N} columns
+ * has {@code N + 1} grid indices that run from {@code 0}
+ * through {@code N} inclusive. Regardless of how GridLayout is
+ * configured, grid index {@code 0} is fixed to the leading edge of the
+ * container and grid index {@code N} is fixed to its trailing edge
  * (after padding is taken into account).
  *
  * <h4>Row and Column Groups</h4>
@@ -231,8 +231,7 @@
     /**
      * Returns the current orientation.
      *
-     * @return either {@link #HORIZONTAL} or {@link #VERTICAL}. The default
-     * is {@link #HORIZONTAL}.
+     * @return either {@link #HORIZONTAL} or {@link #VERTICAL}
      *
      * @see #setOrientation(int)
      *
@@ -246,8 +245,10 @@
      * The orientation property does not affect layout. Orientation is used
      * only to generate default row/column indices when they are not specified
      * by a component's layout parameters.
+     * <p>
+     * The default value of this property is {@link #HORIZONTAL}.
      *
-     * @param orientation the orientation, either {@link #HORIZONTAL} or {@link #VERTICAL}.
+     * @param orientation either {@link #HORIZONTAL} or {@link #VERTICAL}
      *
      * @see #getOrientation()
      *
@@ -281,7 +282,7 @@
      * only to generate default row/column indices when they are not specified
      * by a component's layout parameters.
      *
-     * @param rowCount the number of rows.
+     * @param rowCount the number of rows
      *
      * @see #getRowCount()
      * @see LayoutParams#rowGroup
@@ -328,7 +329,7 @@
      * Returns whether or not this GridLayout will allocate default margins when no
      * corresponding layout parameters are defined.
      *
-     * @return true if default margins should be allocated.
+     * @return {@code true} if default margins should be allocated
      *
      * @see #setUseDefaultMargins(boolean)
      *
@@ -339,18 +340,20 @@
     }
 
     /**
-     * When true, GridLayout allocates default margins around children
+     * When {@code true}, GridLayout allocates default margins around children
      * based on the child's visual characteristics. Each of the
      * margins so defined may be independently overridden by an assignment
      * to the appropriate layout parameter.
      * <p>
-     * When false, the default value of all margins is zero.
+     * When {@code false}, the default value of all margins is zero.
      * <p>
-     * When setting to true, consider setting the value of the
+     * When setting to {@code true}, consider setting the value of the
      * {@link #setMarginsIncludedInAlignment(boolean) marginsIncludedInAlignment}
-     * property to false.
+     * property to {@code false}.
+     * <p>
+     * The default value of this property is {@code false}.
      *
-     * @param useDefaultMargins use true to make GridLayout allocate default margins
+     * @param useDefaultMargins use {@code true} to make GridLayout allocate default margins
      *
      * @see #getUseDefaultMargins()
      * @see #setMarginsIncludedInAlignment(boolean)
@@ -374,7 +377,7 @@
      *
      * @see #setMarginsIncludedInAlignment(boolean)
      *
-     * @return true if alignment is between edges including margins.
+     * @return {@code true} if alignment is between edges including margins
      *
      * @attr ref android.R.styleable#GridLayout_marginsIncludedInAlignment
      */
@@ -383,12 +386,14 @@
     }
 
     /**
-     * When true, the bounds of a view are extended outwards according to its
+     * When {@code true}, the bounds of a view are extended outwards according to its
      * margins before the edges of the resulting rectangle are aligned.
-     * When false, alignment occurs between the bounds of the view - i.e.
+     * When {@code false}, alignment occurs between the bounds of the view - i.e.
      * {@link #LEFT} alignment means align the left edges of the view.
+     * <p>
+     * The default value of this property is {@code true}.
      *
-     * @param marginsIncludedInAlignment true if alignment is between edges including margins.
+     * @param marginsIncludedInAlignment {@code true} if alignment between edges includes margins
      *
      * @see #getMarginsIncludedInAlignment()
      *
@@ -402,8 +407,8 @@
     /**
      * Returns whether or not row boundaries are ordered by their grid indices.
      *
-     * @return true if row boundaries must appear in the order of their indices, false otherwise.
-     *         The default is false.
+     * @return {@code true} if row boundaries must appear in the order of their indices,
+     *         {@code false} otherwise
      *
      * @see #setRowOrderPreserved(boolean)
      *
@@ -414,23 +419,25 @@
     }
 
     /**
-     * When this property is <code>false</code>, the default state, GridLayout
+     * When this property is {@code false}, the default state, GridLayout
      * is at liberty to choose an order that better suits the heights of its children.
      <p>
-     * When this property is <code>true</code>, GridLayout is forced to place the row boundaries
+     * When this property is {@code true}, GridLayout is forced to place the row boundaries
      * so that their associated grid indices are in ascending order in the view.
      * <p>
      * GridLayout implements this specification by creating ordering constraints between
      * the variables that represent the locations of the row boundaries.
      *
-     * When this property is <code>true</code>, constraints are added for each pair of consecutive
-     * indices: i.e. between row boundaries: <code>[0..1], [1..2], [3..4],...</code> etc.
+     * When this property is {@code true}, constraints are added for each pair of consecutive
+     * indices: i.e. between row boundaries: {@code [0..1], [1..2], [2..3],...} etc.
      *
-     * When the property is <code>false</code>, the ordering constraints are placed
+     * When the property is {@code false}, the ordering constraints are placed
      * only between boundaries that separate opposing edges of the layout's children.
-     *
-     * @param rowOrderPreserved use true to force GridLayout to respect the order
-     *        of row boundaries.
+     * <p>
+     * The default value of this property is {@code false}.
+
+     * @param rowOrderPreserved {@code true} to force GridLayout to respect the order
+     *        of row boundaries
      *
      * @see #isRowOrderPreserved()
      *
@@ -445,8 +452,8 @@
     /**
      * Returns whether or not column boundaries are ordered by their grid indices.
      *
-     * @return true if column boundaries must appear in the order of their indices, false otherwise.
-     *         The default is false.
+     * @return {@code true} if column boundaries must appear in the order of their indices,
+     *         {@code false} otherwise
      *
      * @see #setColumnOrderPreserved(boolean)
      *
@@ -457,22 +464,24 @@
     }
 
     /**
-     * When this property is <code>false</code>, the default state, GridLayout
+     * When this property is {@code false}, the default state, GridLayout
      * is at liberty to choose an order that better suits the widths of its children.
      <p>
-     * When this property is <code>true</code>, GridLayout is forced to place the column boundaries
+     * When this property is {@code true}, GridLayout is forced to place the column boundaries
      * so that their associated grid indices are in ascending order in the view.
      * <p>
      * GridLayout implements this specification by creating ordering constraints between
      * the variables that represent the locations of the column boundaries.
      *
-     * When this property is <code>true</code>, constraints are added for each pair of consecutive
-     * indices: i.e. between column boundaries: <code>[0..1], [1..2], [3..4],...</code> etc.
+     * When this property is {@code true}, constraints are added for each pair of consecutive
+     * indices: i.e. between column boundaries: {@code [0..1], [1..2], [2..3],...} etc.
      *
-     * When the property is <code>false</code>, the ordering constraints are placed
+     * When the property is {@code false}, the ordering constraints are placed
      * only between boundaries that separate opposing edges of the layout's children.
+     * <p>
+     * The default value of this property is {@code false}.
      *
-     * @param columnOrderPreserved use true to force GridLayout to respect the order
+     * @param columnOrderPreserved use {@code true} to force GridLayout to respect the order
      *        of column boundaries.
      *
      * @see #isColumnOrderPreserved()
@@ -767,15 +776,6 @@
         return result;
     }
 
-    private int getAlignmentValue(Alignment alignment, View c, int dim, boolean horizontal, View c1) {
-        int result = alignment.getAlignmentValue(c, dim);
-        if (mMarginsIncludedInAlignment) {
-            int leadingMargin = getMargin(c1, true, horizontal);
-            return result + leadingMargin;
-        }
-        return result;
-    }
-
     @Override
     public void requestLayout() {
         super.requestLayout();
@@ -835,18 +835,39 @@
 
             int dx, dy;
 
+            Bounds colBounds = mHorizontalAxis.getGroupBounds().getValue(i);
+            Bounds rowBounds = mVerticalAxis.getGroupBounds().getValue(i);
+
+            // Gravity offsets: the location of the alignment group relative to its cell group.
+            int c2ax = protect(hAlignment.getAlignmentValue(null, cellWidth - colBounds.size()));
+            int c2ay = protect(vAlignment.getAlignmentValue(null, cellHeight - rowBounds.size()));
+
             if (mMarginsIncludedInAlignment) {
-                dx = protect(hAlignment.getAlignmentValue(view, cellWidth - pWidth));
-                dy = protect(vAlignment.getAlignmentValue(view, cellHeight - pHeight));
+                int leftMargin = getMargin(view, true, true);
+                int topMargin = getMargin(view, true, false);
+                int rightMargin = getMargin(view, false, true);
+                int bottomMargin = getMargin(view, false, false);
+
+                // Same calculation as getMeasurementIncludingMargin()
+                int measuredWidth = leftMargin + pWidth + rightMargin;
+                int measuredHeight = topMargin + pHeight + bottomMargin;
+
+                // Alignment offsets: the location of the view relative to its alignment group.
+                int a2vx = colBounds.before - hAlignment.getAlignmentValue(view, measuredWidth);
+                int a2vy = rowBounds.before - vAlignment.getAlignmentValue(view, measuredHeight);
+
+                dx = c2ax + a2vx + leftMargin;
+                dy = c2ay + a2vy + topMargin;
+
+                cellWidth -= leftMargin + rightMargin;
+                cellHeight -= topMargin + bottomMargin;
             } else {
-                Bounds colBounds = mHorizontalAxis.getGroupBounds().getValue(i);
-                Bounds rowBounds = mVerticalAxis.getGroupBounds().getValue(i);
+                // Alignment offsets: the location of the view relative to its alignment group.
+                int a2vx = colBounds.before - hAlignment.getAlignmentValue(view, pWidth);
+                int a2vy = rowBounds.before - vAlignment.getAlignmentValue(view, pHeight);
 
-                int mx = protect(hAlignment.getAlignmentValue(null, cellWidth - colBounds.size()));
-                int my = protect(vAlignment.getAlignmentValue(null, cellHeight - rowBounds.size()));
-
-                dx = mx + -colBounds.below - hAlignment.getAlignmentValue(view, pWidth);
-                dy = my + -rowBounds.below - vAlignment.getAlignmentValue(view, pHeight);
+                dx = c2ax + a2vx;
+                dy = c2ay + a2vy;
             }
 
             int width = hAlignment.getSizeInCell(view, pWidth, cellWidth);
@@ -974,8 +995,8 @@
 
                 int size = getMeasurementIncludingMargin(c, horizontal, PRF);
                 // todo test this works correctly when the returned value is UNDEFINED
-                int below = getAlignmentValue(g.alignment, c, size, horizontal, c);
-                bounds.include(-below, size - below);
+                int before = g.alignment.getAlignmentValue(c, size);
+                bounds.include(before, size - before);
             }
         }
 
@@ -1332,7 +1353,7 @@
             if (!mMarginsIncludedInAlignment) {
                 margin = (leading ? leadingMargins : trailingMargins)[index];
             } else {
-                margin = getMargin(view, leading, horizontal);
+                margin = 0;
             }
             return leading ? (location + margin) : (location - margin);
         }
@@ -1474,26 +1495,26 @@
      *     <li>{@link #height} = {@link #WRAP_CONTENT}</li>
      *     <li>{@link #topMargin} = 0 when
      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
-     *          <code>false</code>; otherwise {@link #UNDEFINED}, to
+     *          {@code false}; otherwise {@link #UNDEFINED}, to
      *          indicate that a default value should be computed on demand. </li>
      *     <li>{@link #leftMargin} = 0 when
      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
-     *          <code>false</code>; otherwise {@link #UNDEFINED}, to
+     *          {@code false}; otherwise {@link #UNDEFINED}, to
      *          indicate that a default value should be computed on demand. </li>
      *     <li>{@link #bottomMargin} = 0 when
      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
-     *          <code>false</code>; otherwise {@link #UNDEFINED}, to
+     *          {@code false}; otherwise {@link #UNDEFINED}, to
      *          indicate that a default value should be computed on demand. </li>
      *     <li>{@link #rightMargin} = 0 when
      *          {@link GridLayout#setUseDefaultMargins(boolean) useDefaultMargins} is
-     *          <code>false</code>; otherwise {@link #UNDEFINED}, to
+     *          {@code false}; otherwise {@link #UNDEFINED}, to
      *          indicate that a default value should be computed on demand. </li>
-     *     <li>{@link #rowGroup}<code>.span</code> = <code>[0, 1]</code> </li>
-     *     <li>{@link #rowGroup}<code>.alignment</code> = {@link #BASELINE} </li>
-     *     <li>{@link #columnGroup}<code>.span</code> = <code>[0, 1]</code> </li>
-     *     <li>{@link #columnGroup}<code>.alignment</code> = {@link #LEFT} </li>
-     *     <li>{@link #rowWeight} = <code>0f</code> </li>
-     *     <li>{@link #columnWeight} = <code>0f</code> </li>
+     *     <li>{@link #rowGroup}{@code .span} = {@code [0, 1]} </li>
+     *     <li>{@link #rowGroup}{@code .alignment} = {@link #BASELINE} </li>
+     *     <li>{@link #columnGroup}{@code .span} = {@code [0, 1]} </li>
+     *     <li>{@link #columnGroup}{@code .alignment} = {@link #LEFT} </li>
+     *     <li>{@link #rowWeight} = {@code 0f} </li>
+     *     <li>{@link #columnWeight} = {@code 0f} </li>
      * </ul>
      *
      * @attr ref android.R.styleable#GridLayout_Layout_layout_row
@@ -1739,9 +1760,10 @@
         }
 
         /**
-         * Describes how the child views are positioned. Default is <code>LEFT | BASELINE</code>.
+         * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}.
+         * See {@link android.view.Gravity}.
          *
-         * @param gravity the new gravity. See {@link android.view.Gravity}.
+         * @param gravity the new gravity value
          *
          * @attr ref android.R.styleable#GridLayout_Layout_layout_gravity
          */
@@ -1881,7 +1903,7 @@
 
     /*
     For each Group (with a given alignment) we need to store the amount of space required
-    above the alignment point and the amount of space required below it. One side of this
+    before the alignment point and the amount of space required after it. One side of this
     calculation is always 0 for LEADING and TRAILING alignments but we don't make use of this.
     For CENTER and BASELINE alignments both sides are needed and in the BASELINE case no
     simple optimisations are possible.
@@ -1891,37 +1913,32 @@
     of the values for each View.
     */
     private static class Bounds {
-        public int below;
-        public int above;
-
-        private Bounds(int below, int above) {
-            this.below = below;
-            this.above = above;
-        }
+        public int before;
+        public int after;
 
         private Bounds() {
             reset();
         }
 
         private void reset() {
-            below = Integer.MAX_VALUE;
-            above = Integer.MIN_VALUE;
+            before = Integer.MIN_VALUE;
+            after = Integer.MIN_VALUE;
         }
 
-        private void include(int below, int above) {
-            this.below = min(this.below, below);
-            this.above = max(this.above, above);
+        private void include(int before, int after) {
+            this.before = max(this.before, before);
+            this.after = max(this.after, after);
         }
 
         private int size() {
-            return above - below;
+            return before + after;
         }
 
         @Override
         public String toString() {
             return "Bounds{" +
-                    "below=" + below +
-                    ", above=" + above +
+                    "before=" + before +
+                    ", after=" + after +
                     '}';
         }
     }
@@ -1934,8 +1951,8 @@
      * It is not necessary to have multiple instances of Intervals which have the same
      * {@link #min} and {@link #max} values.
      * <p>
-     * Intervals are often written as <code>[min, max]</code> and represent the set of values
-     * <em>x</em> such that <em>min <= x < max</em>.
+     * Intervals are often written as {@code [min, max]} and represent the set of values
+     * {@code x} such that {@code min <= x < max}.
      */
     /* package */ static class Interval {
         /**
@@ -1949,10 +1966,10 @@
         public final int max;
 
         /**
-         * Construct a new Interval, <code>interval</code>, where:
+         * Construct a new Interval, {@code interval}, where:
          * <ul>
-         *     <li> <code>interval.min = min</code> </li>
-         *     <li> <code>interval.max = max</code> </li>
+         *     <li> {@code interval.min = min} </li>
+         *     <li> {@code interval.max = max} </li>
          * </ul>
          *
          * @param min the minimum value.
@@ -1972,13 +1989,14 @@
         }
 
         /**
-         * Returns true if the {@link #getClass class}, {@link #min} and {@link #max} properties
-         * of this Interval and the supplied parameter are pairwise equal; false otherwise.
+         * Returns {@code true} if the {@link #getClass class},
+         * {@link #min} and {@link #max} properties of this Interval and the
+         * supplied parameter are pairwise equal; {@code false} otherwise.
          *
-         * @param that the object to compare this interval with.
+         * @param that the object to compare this interval with
          *
          * @return {@code true} if the specified object is equal to this
-         *         {@code Interval}; {@code false} otherwise.
+         *         {@code Interval}, {@code false} otherwise.
          */
         @Override
         public boolean equals(Object that) {
@@ -2019,7 +2037,7 @@
      * cells.
      * <p>
      * Groups are immutable and so may be shared between views with the same
-     * <code>span</code> and <code>alignment</code>.
+     * {@code span} and {@code alignment}.
      */
     public static class Group {
         /**
@@ -2038,14 +2056,14 @@
         public final Alignment alignment;
 
         /**
-         * Construct a new Group, <code>group</code>, where:
+         * Construct a new Group, {@code group}, where:
          * <ul>
-         *     <li> <code>group.span = span</code> </li>
-         *     <li> <code>group.alignment = alignment</code> </li>
+         *     <li> {@code group.span = span} </li>
+         *     <li> {@code group.alignment = alignment} </li>
          * </ul>
          *
-         * @param span      the span.
-         * @param alignment the alignment.
+         * @param span      the span
+         * @param alignment the alignment
          */
         /* package */ Group(Interval span, Alignment alignment) {
             this.span = span;
@@ -2053,29 +2071,29 @@
         }
 
         /**
-         * Construct a new Group, <code>group</code>, where:
+         * Construct a new Group, {@code group}, where:
          * <ul>
-         *     <li> <code>group.span = [min, max]</code> </li>
-         *     <li> <code>group.alignment = alignment</code> </li>
+         *     <li> {@code group.span = [min, max]} </li>
+         *     <li> {@code group.alignment = alignment} </li>
          * </ul>
          *
-         * @param min       the minimum.
-         * @param max       the maximum.
-         * @param alignment the alignment.
+         * @param min       the minimum
+         * @param max       the maximum
+         * @param alignment the alignment
          */
         public Group(int min, int max, Alignment alignment) {
             this(new Interval(min, max), alignment);
         }
 
         /**
-         * Construct a new Group, <code>group</code>, where:
+         * Construct a new Group, {@code group}, where:
          * <ul>
-         *     <li> <code>group.span = [min, min + 1]</code> </li>
-         *     <li> <code>group.alignment = alignment</code> </li>
+         *     <li> {@code group.span = [min, min + 1]} </li>
+         *     <li> {@code group.alignment = alignment} </li>
          * </ul>
          *
-         * @param min       the minimum.
-         * @param alignment the alignment.
+         * @param min       the minimum
+         * @param alignment the alignment
          */
         public Group(int min, Alignment alignment) {
             this(min, min + 1, alignment);
@@ -2090,13 +2108,14 @@
         }
 
         /**
-         * Returns true if the {@link #getClass class}, {@link #alignment} and <code>span</code>
-         * properties of this Group and the supplied parameter are pairwise equal; false otherwise.
+         * Returns {@code true} if the {@link #getClass class}, {@link #alignment} and {@code span}
+         * properties of this Group and the supplied parameter are pairwise equal,
+         * {@code false} otherwise.
          *
-         * @param that the object to compare this group with.
+         * @param that the object to compare this group with
          *
          * @return {@code true} if the specified object is equal to this
-         *         {@code Group}; {@code false} otherwise.
+         *         {@code Group}; {@code false} otherwise
          */
         @Override
         public boolean equals(Object that) {
@@ -2153,9 +2172,9 @@
          * alignment location.
          * For horizontal alignments measurement is made from the left edge of the component.
          *
-         * @param view     the view to which this alignment should be applied.
-         * @param viewSize the measured size of the view.
-         * @return the alignment value.
+         * @param view     the view to which this alignment should be applied
+         * @param viewSize the measured size of the view
+         * @return         the alignment value
          */
         public int getAlignmentValue(View view, int viewSize);
 
@@ -2164,10 +2183,10 @@
          * In the case of vertical alignments this method should return a height; for
          * horizontal alignments this method should return the width.
          *
-         * @param view     the view to which this alignment should be applied.
-         * @param viewSize the measured size of the view.
-         * @param cellSize the size of the cell into which this view will be placed.
-         * @return the aligned size.
+         * @param view     the view to which this alignment should be applied
+         * @param viewSize the measured size of the view
+         * @param cellSize the size of the cell into which this view will be placed
+         * @return         the aligned size
          */
         public int getSizeInCell(View view, int viewSize, int cellSize);
     }
@@ -2234,13 +2253,16 @@
      * @see View#getBaseline()
      */
     public static final Alignment BASELINE = new AbstractAlignment() {
-        public int getAlignmentValue(View view, int viewSize) {
+        public int getAlignmentValue(View view, int height) {
             if (view == null) {
                 return UNDEFINED;
             }
-            // todo do we need to call measure first?
             int baseline = view.getBaseline();
-            return baseline == -1 ? UNDEFINED : baseline;
+            if (baseline == -1) {
+                return UNDEFINED;
+            } else {
+                return baseline;
+            }
         }
 
     };
diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java
index f843574..0cdbc5b 100644
--- a/core/java/android/widget/LinearLayout.java
+++ b/core/java/android/widget/LinearLayout.java
@@ -116,10 +116,10 @@
                 equals = Gravity.LEFT, name = "LEFT"),
             @ViewDebug.FlagToString(mask = Gravity.RIGHT,
                 equals = Gravity.RIGHT, name = "RIGHT"),
-            @ViewDebug.FlagToString(mask = Gravity.BEFORE,
-                equals = Gravity.BEFORE, name = "BEFORE"),
-            @ViewDebug.FlagToString(mask = Gravity.AFTER,
-                equals = Gravity.AFTER, name = "AFTER"),
+            @ViewDebug.FlagToString(mask = Gravity.START,
+                equals = Gravity.START, name = "START"),
+            @ViewDebug.FlagToString(mask = Gravity.END,
+                equals = Gravity.END, name = "END"),
             @ViewDebug.FlagToString(mask = Gravity.CENTER_VERTICAL,
                 equals = Gravity.CENTER_VERTICAL, name = "CENTER_VERTICAL"),
             @ViewDebug.FlagToString(mask = Gravity.FILL_VERTICAL,
@@ -132,10 +132,10 @@
                 equals = Gravity.CENTER, name = "CENTER"),
             @ViewDebug.FlagToString(mask = Gravity.FILL,
                 equals = Gravity.FILL, name = "FILL"),
-            @ViewDebug.FlagToString(mask = Gravity.RELATIVE_HORIZONTAL_DIRECTION,
-                equals = Gravity.RELATIVE_HORIZONTAL_DIRECTION, name = "RELATIVE")
+            @ViewDebug.FlagToString(mask = Gravity.RELATIVE_LAYOUT_DIRECTION,
+                equals = Gravity.RELATIVE_LAYOUT_DIRECTION, name = "RELATIVE")
         })
-    private int mGravity = Gravity.BEFORE | Gravity.TOP;
+    private int mGravity = Gravity.START | Gravity.TOP;
 
     @ViewDebug.ExportedProperty(category = "measurement")
     private int mTotalLength;
@@ -1649,7 +1649,7 @@
     public void setGravity(int gravity) {
         if (mGravity != gravity) {
             if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-                gravity |= Gravity.BEFORE;
+                gravity |= Gravity.START;
             }
 
             if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
@@ -1742,8 +1742,8 @@
             @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
             @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
             @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
-            @ViewDebug.IntToString(from = Gravity.BEFORE,            to = "BEFORE"),
-            @ViewDebug.IntToString(from = Gravity.AFTER,             to = "AFTER"),
+            @ViewDebug.IntToString(from = Gravity.START,            to = "START"),
+            @ViewDebug.IntToString(from = Gravity.END,             to = "END"),
             @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
             @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
             @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index acd8539..a4771d5 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -16,6 +16,15 @@
 
 package android.widget;
 
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
 import android.content.Context;
 import android.content.res.Resources;
 import android.content.res.TypedArray;
@@ -32,14 +41,6 @@
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.widget.RemoteViews.RemoteView;
-import com.android.internal.R;
-
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.LinkedList;
-import java.util.SortedSet;
-import java.util.TreeSet;
 
 import static android.util.Log.d;
 
@@ -222,7 +223,7 @@
     public void setGravity(int gravity) {
         if (mGravity != gravity) {
             if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-                gravity |= Gravity.BEFORE;
+                gravity |= Gravity.START;
             }
 
             if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
@@ -1440,6 +1441,7 @@
             );
 
             private Node mNext;
+            private boolean mIsPooled;
 
             public void setNextPoolable(Node element) {
                 mNext = element;
@@ -1449,6 +1451,14 @@
                 return mNext;
             }
 
+            public boolean isPooled() {
+                return mIsPooled;
+            }
+
+            public void setPooled(boolean isPooled) {
+                mIsPooled = isPooled;
+            }
+
             static Node acquire(View view) {
                 final Node node = sPool.acquire();
                 node.view = view;
diff --git a/core/java/android/widget/Switch.java b/core/java/android/widget/Switch.java
index cd4b732..5c6a26f 100644
--- a/core/java/android/widget/Switch.java
+++ b/core/java/android/widget/Switch.java
@@ -16,8 +16,6 @@
 
 package android.widget;
 
-import com.android.internal.R;
-
 import android.content.Context;
 import android.content.res.ColorStateList;
 import android.content.res.Resources;
@@ -37,6 +35,8 @@
 import android.view.VelocityTracker;
 import android.view.ViewConfiguration;
 
+import com.android.internal.R;
+
 /**
  * A Switch is a two-state toggle switch widget that can select between two
  * options. The user may drag the "thumb" back and forth to choose the selected option,
@@ -84,6 +84,7 @@
     private Layout mOnLayout;
     private Layout mOffLayout;
 
+    @SuppressWarnings("hiding")
     private final Rect mTempRect = new Rect();
 
     private static final int[] CHECKED_STATE_SET = {
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 35e78fb..eba9d37 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -113,6 +113,7 @@
 import android.view.WindowManager;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
 import android.view.animation.AnimationUtils;
 import android.view.inputmethod.BaseInputConnection;
 import android.view.inputmethod.CompletionInfo;
@@ -2094,7 +2095,7 @@
      */
     public void setGravity(int gravity) {
         if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
-            gravity |= Gravity.BEFORE;
+            gravity |= Gravity.START;
         }
         if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
             gravity |= Gravity.TOP;
@@ -7647,7 +7648,18 @@
     protected int computeVerticalScrollExtent() {
         return getHeight() - getCompoundPaddingTop() - getCompoundPaddingBottom();
     }
-    
+
+    @Override
+    public void findViewsWithText(ArrayList<View> outViews, CharSequence text) {
+        CharSequence thisText = getText();
+        if (TextUtils.isEmpty(thisText)) {
+            return;
+        }
+        if (thisText.toString().toLowerCase().contains(text)) {
+            outViews.add(this);
+        }
+    }
+
     public enum BufferType {
         NORMAL, SPANNABLE, EDITABLE,
     }
@@ -7899,6 +7911,17 @@
         event.setPassword(isPassword);
     }
 
+    @Override
+    public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
+        super.onInitializeAccessibilityNodeInfo(info);
+
+        final boolean isPassword = hasPasswordTransformationMethod();
+        if (!isPassword) {
+            info.setText(getText());
+        }
+        info.setPassword(isPassword);
+    }
+
     void sendAccessibilityEventTypeViewTextChanged(CharSequence beforeText,
             int fromIndex, int removedCount, int addedCount) {
         AccessibilityEvent event =
@@ -10005,7 +10028,7 @@
 
     private boolean                 mSelectAllOnFocus = false;
 
-    private int                     mGravity = Gravity.TOP | Gravity.BEFORE;
+    private int                     mGravity = Gravity.TOP | Gravity.START;
     private boolean                 mHorizontallyScrolling;
 
     private int                     mAutoLinkMask;
diff --git a/core/java/com/android/internal/util/Objects.java b/core/java/com/android/internal/util/Objects.java
index 598a079..2664182 100644
--- a/core/java/com/android/internal/util/Objects.java
+++ b/core/java/com/android/internal/util/Objects.java
@@ -16,34 +16,47 @@
 
 package com.android.internal.util;
 
+import java.util.Arrays;
+
 /**
  * Object utility methods.
  */
 public class Objects {
 
     /**
-     * Ensures the given object isn't {@code null}.
+     * Determines whether two possibly-null objects are equal. Returns:
      *
-     * @return the given object
-     * @throws NullPointerException if the object is null
+     * <ul>
+     * <li>{@code true} if {@code a} and {@code b} are both null.
+     * <li>{@code true} if {@code a} and {@code b} are both non-null and they are
+     *     equal according to {@link Object#equals(Object)}.
+     * <li>{@code false} in all other situations.
+     * </ul>
+     *
+     * <p>This assumes that any non-null objects passed to this function conform
+     * to the {@code equals()} contract.
      */
-    public static <T> T nonNull(T t) {
-        if (t == null) {
-            throw new NullPointerException();
-        }
-        return t;
+    public static boolean equal(Object a, Object b) {
+        return a == b || (a != null && a.equals(b));
     }
 
     /**
-     * Ensures the given object isn't {@code null}.
+     * Generates a hash code for multiple values. The hash code is generated by
+     * calling {@link Arrays#hashCode(Object[])}.
      *
-     * @return the given object
-     * @throws NullPointerException if the object is null
+     * <p>This is useful for implementing {@link Object#hashCode()}. For example,
+     * in an object that has three properties, {@code x}, {@code y}, and
+     * {@code z}, one could write:
+     * <pre>
+     * public int hashCode() {
+     *   return Objects.hashCode(getX(), getY(), getZ());
+     * }</pre>
+     *
+     * <b>Warning</b>: When a single object is supplied, the returned hash code
+     * does not equal the hash code of that object.
      */
-    public static <T> T nonNull(T t, String message) {
-        if (t == null) {
-            throw new NullPointerException(message);
-        }
-        return t;
+    public static int hashCode(Object... objects) {
+        return Arrays.hashCode(objects);
     }
+
 }
diff --git a/core/java/com/android/internal/util/Preconditions.java b/core/java/com/android/internal/util/Preconditions.java
new file mode 100644
index 0000000..a53a9c0
--- /dev/null
+++ b/core/java/com/android/internal/util/Preconditions.java
@@ -0,0 +1,57 @@
+/*
+ * 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 com.android.internal.util;
+
+/**
+ * Simple static methods to be called at the start of your own methods to verify
+ * correct arguments and state.
+ */
+public class Preconditions {
+
+    /**
+     * Ensures that an object reference passed as a parameter to the calling
+     * method is not null.
+     *
+     * @param reference an object reference
+     * @return the non-null reference that was validated
+     * @throws NullPointerException if {@code reference} is null
+     */
+    public static <T> T checkNotNull(T reference) {
+        if (reference == null) {
+            throw new NullPointerException();
+        }
+        return reference;
+    }
+
+    /**
+     * Ensures that an object reference passed as a parameter to the calling
+     * method is not null.
+     *
+     * @param reference an object reference
+     * @param errorMessage the exception message to use if the check fails; will
+     *     be converted to a string using {@link String#valueOf(Object)}
+     * @return the non-null reference that was validated
+     * @throws NullPointerException if {@code reference} is null
+     */
+    public static <T> T checkNotNull(T reference, Object errorMessage) {
+        if (reference == null) {
+            throw new NullPointerException(String.valueOf(errorMessage));
+        }
+        return reference;
+    }
+
+}
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index c41b2cb..b9948fe 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -16,8 +16,6 @@
 
 package com.android.internal.view;
 
-import android.content.ClipData;
-import android.content.ClipDescription;
 import android.content.res.Configuration;
 import android.graphics.Rect;
 import android.os.Bundle;
@@ -26,8 +24,6 @@
 import android.view.DragEvent;
 import android.view.IWindow;
 import android.view.IWindowSession;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
 
 public class BaseIWindow extends IWindow.Stub {
     private IWindowSession mSession;
diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java
index 1726390..d710cfa 100644
--- a/core/java/com/android/internal/widget/ActionBarContainer.java
+++ b/core/java/com/android/internal/widget/ActionBarContainer.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.widget;
 
+import android.app.ActionBar;
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.util.AttributeSet;
@@ -32,6 +33,7 @@
 public class ActionBarContainer extends FrameLayout {
     private boolean mIsTransitioning;
     private View mTabContainer;
+    private ActionBarView mActionBarView;
 
     public ActionBarContainer(Context context) {
         this(context, null);
@@ -46,6 +48,12 @@
         a.recycle();
     }
 
+    @Override
+    public void onFinishInflate() {
+        super.onFinishInflate();
+        mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar);
+    }
+
     /**
      * Set the action bar into a "transitioning" state. While transitioning
      * the bar will block focus and touch from all of its descendants. This
@@ -101,9 +109,8 @@
         final int count = getChildCount();
         for (int i = 0; i < count; i++) {
             final View child = getChildAt(i);
-            if (child == mTabContainer) {
-                continue;
-            }
+
+            if (child == mTabContainer) continue;
 
             final LayoutParams lp = (LayoutParams) child.getLayoutParams();
             nonTabHeight = Math.max(nonTabHeight,
@@ -125,8 +132,22 @@
         super.onLayout(changed, l, t, r, b);
         if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
             final int containerHeight = getMeasuredHeight();
-            mTabContainer.layout(l, containerHeight - mTabContainer.getMeasuredHeight(),
-                    r, containerHeight);
+            final int tabHeight = mTabContainer.getMeasuredHeight();
+
+            if ((mActionBarView.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == 0) {
+                // Not showing home, put tabs on top.
+                final int count = getChildCount();
+                for (int i = 0; i < count; i++){
+                    final View child = getChildAt(i);
+
+                    if (child == mTabContainer) continue;
+
+                    child.offsetTopAndBottom(tabHeight);
+                }
+                mTabContainer.layout(l, 0, r, tabHeight);
+            } else {
+                mTabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
+            }
         }
     }
 }
diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java
index dfba486..4337e90 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -34,6 +34,7 @@
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
+import android.view.View;
 import android.widget.Button;
 
 import java.io.File;
@@ -790,6 +791,16 @@
         setBoolean(LOCKOUT_PERMANENT_KEY, locked);
     }
 
+    public boolean isEmergencyCallCapable() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_voice_capable);
+    }
+
+    public boolean isPukUnlockScreenEnable() {
+        return mContext.getResources().getBoolean(
+                com.android.internal.R.bool.config_enable_puk_unlock_screen);
+    }
+
     /**
      * @return A formatted string of the next alarm (for showing on the lock screen),
      *   or null if there is no next alarm.
@@ -842,11 +853,22 @@
     }
 
     /**
-     * Sets the text on the emergency button to indicate what action will be taken.
+     * Sets the emergency button visibility based on isEmergencyCallCapable().
+     *
+     * If the emergency button is visible, sets the text on the emergency button
+     * to indicate what action will be taken.
+     *
      * If there's currently a call in progress, the button will take them to the call
      * @param button the button to update
      */
     public void updateEmergencyCallButtonState(Button button) {
+        if (isEmergencyCallCapable()) {
+            button.setVisibility(View.VISIBLE);
+        } else {
+            button.setVisibility(View.GONE);
+            return;
+        }
+
         int newState = TelephonyManager.getDefault().getCallState();
         int textId;
         if (newState == TelephonyManager.CALL_STATE_OFFHOOK) {
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 223008c..cfff0ca 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -48,7 +48,6 @@
 	android_view_Display.cpp \
 	android_view_Surface.cpp \
 	android_view_TextureView.cpp \
-	android_view_ViewAncestor.cpp \
 	android_view_InputChannel.cpp \
 	android_view_InputQueue.cpp \
 	android_view_KeyEvent.cpp \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index e610640..4552095 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -119,7 +119,6 @@
 extern int register_android_view_GLES20Canvas(JNIEnv* env);
 extern int register_android_view_Surface(JNIEnv* env);
 extern int register_android_view_TextureView(JNIEnv* env);
-extern int register_android_view_ViewAncestor(JNIEnv* env);
 extern int register_android_database_CursorWindow(JNIEnv* env);
 extern int register_android_database_SQLiteCompiledSql(JNIEnv* env);
 extern int register_android_database_SQLiteDatabase(JNIEnv* env);
@@ -1121,7 +1120,6 @@
     REG_JNI(register_android_view_GLES20Canvas),
     REG_JNI(register_android_view_Surface),
     REG_JNI(register_android_view_TextureView),
-    REG_JNI(register_android_view_ViewAncestor),
     REG_JNI(register_com_google_android_gles_jni_EGLImpl),
     REG_JNI(register_com_google_android_gles_jni_GLImpl),
     REG_JNI(register_android_opengl_jni_GLES10),
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 4a09232..942aa8a1 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -755,32 +755,46 @@
             jfloat x, jfloat y, int flags, SkPaint* paint) {
 
         jint count = end - start;
-        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-                paint, textArray, start, count, end, flags);
+        sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+        value = gTextLayoutCache.getValue(paint, textArray, start, count, end, flags);
         if (value == NULL) {
             LOGE("Cannot get TextLayoutCache value");
             return ;
         }
+#else
+        value = new TextLayoutCacheValue();
+        value->computeValues(paint, textArray, start, count, end, flags);
+#endif
+
 #if DEBUG_GLYPHS
         logGlyphs(value);
 #endif
-        doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
-                x, y, flags, paint);
+
+    doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
+            x, y, flags, paint);
     }
 
     static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
             int start, int count, int contextCount,
             jfloat x, jfloat y, int flags, SkPaint* paint) {
 
-        sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-                paint, textArray, start, count, contextCount, flags);
+        sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+        value = gTextLayoutCache.getValue(paint, textArray, start, count, contextCount, flags);
         if (value == NULL) {
             LOGE("Cannot get TextLayoutCache value");
             return ;
         }
+#else
+        value = new TextLayoutCacheValue();
+        value->computeValues(paint, textArray, start, count, contextCount, flags);
+#endif
+
 #if DEBUG_GLYPHS
         logGlyphs(value);
 #endif
+
         doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
                 x, y, flags, paint);
     }
diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp
index 46e6c2b..7e89a37 100644
--- a/core/jni/android/graphics/TextLayout.cpp
+++ b/core/jni/android/graphics/TextLayout.cpp
@@ -254,21 +254,21 @@
 void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
                                     jint count, jint contextCount, jint dirFlags,
                                     jfloat* resultAdvances, jfloat& resultTotalAdvance) {
+    sp<TextLayoutCacheValue> value;
 #if USE_TEXT_LAYOUT_CACHE
     // Return advances from the cache. Compute them if needed
-    sp<TextLayoutCacheValue> layout = gTextLayoutCache.getValue(
+    value = gTextLayoutCache.getValue(
             paint, chars, start, count, contextCount, dirFlags);
-    if (layout != NULL) {
-        if (resultAdvances != NULL) {
-            memcpy(resultAdvances, layout->getAdvances(), layout->getAdvancesCount() * sizeof(jfloat));
-        }
-        resultTotalAdvance = layout->getTotalAdvance();
-    }
 #else
-    // Compute advances and return them
-    TextLayoutCacheValue::computeValuesWithHarfbuzz(paint, chars, start, count, contextCount,
-            dirFlags, resultAdvances, &resultTotalAdvance, NULL, NULL );
+    value = new TextLayoutCacheValue();
+    value->computeValues(paint, chars, start, count, contextCount, dirFlags);
 #endif
+    if (value != NULL) {
+        if (resultAdvances != NULL) {
+            memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat));
+        }
+        resultTotalAdvance = value->getTotalAdvance();
+    }
 }
 
 void TextLayout::getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start,
diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp
index ecfe5ff..b36fa3e 100644
--- a/core/jni/android_app_backup_FullBackup.cpp
+++ b/core/jni/android_app_backup_FullBackup.cpp
@@ -73,6 +73,8 @@
 static int backupToTar(JNIEnv* env, jobject clazz, jstring packageNameObj,
         jstring domainObj, jstring linkdomain,
         jstring rootpathObj, jstring pathObj, jobject dataOutputObj) {
+    int ret;
+
     // Extract the various strings, allowing for null object pointers
     const char* packagenamechars = env->GetStringUTFChars(packageNameObj, NULL);
     const char* rootchars = env->GetStringUTFChars(rootpathObj, NULL);
diff --git a/core/jni/android_nfc_NdefMessage.cpp b/core/jni/android_nfc_NdefMessage.cpp
index d9b64aa..41099cb 100644
--- a/core/jni/android_nfc_NdefMessage.cpp
+++ b/core/jni/android_nfc_NdefMessage.cpp
@@ -102,6 +102,19 @@
         }
         TRACE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status);
 
+        // We don't exactly know what *is* a valid length, but a simple
+        // sanity check is to make sure that the length of the header
+        // plus all fields does not exceed raw_msg_size. The min length
+        // of the header is 3 bytes: TNF, Type Length, Payload Length
+        // (ID length field is optional!)
+        uint64_t indicatedMsgLength = 3 + record.TypeLength + record.IdLength +
+                (uint64_t)record.PayloadLength;
+        if (indicatedMsgLength >
+                (uint64_t)raw_msg_size) {
+            LOGE("phFri_NdefRecord_Parse: invalid length field");
+            goto end;
+        }
+
         type = e->NewByteArray(record.TypeLength);
         if (type == NULL) {
             LOGD("NFC_Set Record Type Error\n");
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index e5c28489..0960b25 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -38,18 +38,7 @@
 #include <grp.h>
 #include <pwd.h>
 #include <signal.h>
-
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
+#include <unistd.h>
 
 #define POLICY_DEBUG 0
 #define GUARD_THREAD_PRIORITY 0
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 31988f7..57a97bd 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -438,12 +438,17 @@
 static void renderText(OpenGLRenderer* renderer, const jchar* text, int count,
         jfloat x, jfloat y, int flags, SkPaint* paint) {
 #if RTL_USE_HARFBUZZ
-    sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-            paint, text, 0, count, count, flags);
+    sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+    value = gTextLayoutCache.getValue(paint, text, 0, count, count, flags);
     if (value == NULL) {
         LOGE("Cannot get TextLayoutCache value");
         return ;
     }
+#else
+    value = new TextLayoutCacheValue();
+    value->computeValues(paint, text, 0, count, count, flags);
+#endif
 #if DEBUG_GLYPHS
     logGlyphs(value);
 #endif
@@ -466,12 +471,17 @@
         jint start, jint count, jint contextCount, jfloat x, jfloat y,
         int flags, SkPaint* paint) {
 #if RTL_USE_HARFBUZZ
-    sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
-            paint, text, start, count, contextCount, flags);
+    sp<TextLayoutCacheValue> value;
+#if USE_TEXT_LAYOUT_CACHE
+    value = gTextLayoutCache.getValue(paint, text, start, count, contextCount, flags);
     if (value == NULL) {
         LOGE("Cannot get TextLayoutCache value");
         return ;
     }
+#else
+    value = new TextLayoutCacheValue();
+    value->computeValues(paint, text, start, count, contextCount, flags);
+#endif
 #if DEBUG_GLYPHS
     logGlyphs(value);
 #endif
diff --git a/core/jni/android_view_ViewAncestor.cpp b/core/jni/android_view_ViewAncestor.cpp
deleted file mode 100644
index d8e1124..0000000
--- a/core/jni/android_view_ViewAncestor.cpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <stdio.h>
-#include <assert.h>
-#include <sys/socket.h>
-
-#include <core/SkCanvas.h>
-#include <core/SkDevice.h>
-#include <core/SkPaint.h>
-#include <utils/SkGLCanvas.h>
-#include "GraphicsJNI.h"
-
-#include "jni.h"
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <utils/misc.h>
-
-// ----------------------------------------------------------------------------
-
-namespace android {
-
-static int gPrevDur;
-
-static void android_view_ViewAncestor_showFPS(JNIEnv* env, jobject, jobject jcanvas,
-                                          jint dur) {
-    NPE_CHECK_RETURN_VOID(env, jcanvas);
-    SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
-    const SkBitmap& bm = canvas->getDevice()->accessBitmap(false);
-    int height = bm.height();
-    SkScalar bot = SkIntToScalar(height);
-
-    if (height < 200) {
-        return;
-    }
-
-    SkMatrix m;
-    SkRect   r;
-    SkPaint  p;
-    char    str[4];
-
-    dur = (gPrevDur + dur) >> 1;
-    gPrevDur = dur;
-
-    dur = 1000 / dur;
-    str[3] = (char)('0' + dur % 10); dur /= 10;
-    str[2] = (char)('0' + dur % 10); dur /= 10;
-    str[1] = (char)('0' + dur % 10); dur /= 10;
-    str[0] = (char)('0' + dur % 10);
-
-    m.reset();
-    r.set(0, bot-SkIntToScalar(10), SkIntToScalar(26), bot);
-    p.setAntiAlias(true);
-    p.setTextSize(SkIntToScalar(10));
-
-    canvas->save();
-    canvas->setMatrix(m);
-    canvas->clipRect(r, SkRegion::kReplace_Op);
-    p.setColor(SK_ColorWHITE);
-    canvas->drawPaint(p);
-    p.setColor(SK_ColorBLACK);
-    canvas->drawText(str, 4, SkIntToScalar(1), bot - SK_Scalar1, p);
-    canvas->restore();
-}
-
-
-// ----------------------------------------------------------------------------
-
-const char* const kClassPathName = "android/view/ViewAncestor";
-
-static JNINativeMethod gMethods[] = {
-    {   "nativeShowFPS", "(Landroid/graphics/Canvas;I)V",
-                                        (void*)android_view_ViewAncestor_showFPS }
-};
-
-int register_android_view_ViewAncestor(JNIEnv* env) {
-    return AndroidRuntime::registerNativeMethods(env,
-            kClassPathName, gMethods, NELEM(gMethods));
-}
-
-};
-
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 110268e..b2606c1 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -159,6 +159,15 @@
         android:label="@string/permlab_receiveMms"
         android:description="@string/permdesc_receiveMms" />
 
+    <!-- Allows an application to receive emergency cell broadcast messages,
+         to record or display them to the user. Reserved for system apps.
+         @hide Pending API council approval -->
+    <permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST"
+        android:permissionGroup="android.permission-group.MESSAGES"
+        android:protectionLevel="signatureOrSystem"
+        android:label="@string/permlab_receiveEmergencyBroadcast"
+        android:description="@string/permdesc_receiveEmergencyBroadcast" />
+
     <!-- Allows an application to read SMS messages. -->
     <permission android:name="android.permission.READ_SMS"
         android:permissionGroup="android.permission-group.MESSAGES"
@@ -208,6 +217,22 @@
         android:label="@string/permlab_writeContacts"
         android:description="@string/permdesc_writeContacts" />
 
+
+    <!-- Allows an application to read the user's personal profile data. -->
+    <permission android:name="android.permission.READ_PROFILE"
+        android:permissionGroup="android.permission-group.PERSONAL_INFO"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_readProfile"
+        android:description="@string/permdesc_readProfile" />
+
+    <!-- Allows an application to write (but not read) the user's
+         personal profile data. -->
+    <permission android:name="android.permission.WRITE_PROFILE"
+        android:permissionGroup="android.permission-group.PERSONAL_INFO"
+        android:protectionLevel="dangerous"
+        android:label="@string/permlab_writeProfile"
+        android:description="@string/permdesc_writeProfile" />
+
     <!-- Allows an application to read the user's calendar data. -->
     <permission android:name="android.permission.READ_CALENDAR"
         android:permissionGroup="android.permission-group.PERSONAL_INFO"
@@ -677,6 +702,14 @@
         android:label="@string/permlab_dump"
         android:description="@string/permdesc_dump" />
 
+    <!-- Allows an application to retrieve the content of the active window
+         An active window is the window that has fired an accessibility event. -->
+    <permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT"
+        android:permissionGroup="android.permission-group.PERSONAL_INFO"
+        android:protectionLevel="signatureOrSystem"
+        android:label="@string/permlab_retrieve_window_content"
+        android:description="@string/permdesc_retrieve_window_content" />
+
     <!-- Allows an application to open windows using the type
          {@link android.view.WindowManager.LayoutParams#TYPE_SYSTEM_ALERT},
          shown on top of all other applications.  Very few applications
diff --git a/core/res/res/drawable-hdpi/pointer_arrow.png b/core/res/res/drawable-hdpi/pointer_arrow.png
new file mode 100644
index 0000000..fbd187c
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_arrow.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_arrow_icon.xml b/core/res/res/drawable-hdpi/pointer_arrow_icon.xml
new file mode 100644
index 0000000..2f5676f
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_arrow_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_arrow"
+    android:hotSpotX="6"
+    android:hotSpotY="6" />
diff --git a/core/res/res/drawable-hdpi/pointer_spot_anchor.png b/core/res/res/drawable-hdpi/pointer_spot_anchor.png
new file mode 100644
index 0000000..d7aca36
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_anchor.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml
new file mode 100644
index 0000000..2222b8e
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_anchor_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_spot_anchor"
+    android:hotSpotX="33"
+    android:hotSpotY="33" />
diff --git a/core/res/res/drawable-hdpi/pointer_spot_hover.png b/core/res/res/drawable-hdpi/pointer_spot_hover.png
new file mode 100644
index 0000000..5041aa3
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_hover.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml
new file mode 100644
index 0000000..dc62a69
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_hover_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_spot_hover"
+    android:hotSpotX="33"
+    android:hotSpotY="33" />
diff --git a/core/res/res/drawable-hdpi/pointer_spot_touch.png b/core/res/res/drawable-hdpi/pointer_spot_touch.png
new file mode 100644
index 0000000..64a42a1
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_touch.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml b/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml
new file mode 100644
index 0000000..4bffee6
--- /dev/null
+++ b/core/res/res/drawable-hdpi/pointer_spot_touch_icon.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<pointer-icon xmlns:android="http://schemas.android.com/apk/res/android"
+    android:bitmap="@drawable/pointer_spot_touch"
+    android:hotSpotX="24"
+    android:hotSpotY="24" />
diff --git a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
index fbb9983..3bb7821 100644
--- a/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
+++ b/core/res/res/layout-sw600dp/keyguard_screen_tab_unlock_land.xml
@@ -44,31 +44,21 @@
     </RelativeLayout>
 
     <!-- right side -->
-    <LinearLayout
-            android:layout_height="match_parent"
-            android:layout_weight="1"
-            android:layout_width="0dip"
-            android:orientation="horizontal"
-            android:gravity="center_horizontal"
-                >
-        <TextView
-                android:id="@+id/screenLocked"
-                android:layout_width="wrap_content"
-                android:layout_height="wrap_content"
-                android:layout_below="@id/status2"
-                android:textAppearance="?android:attr/textAppearanceMedium"
-                android:gravity="center"
-                android:layout_marginTop="12dip"
-                android:drawablePadding="4dip"
-                />
+    <RelativeLayout
+        android:layout_height="match_parent"
+        android:layout_weight="1"
+        android:layout_width="0dip"
+        android:gravity="center_horizontal|center_vertical">
 
-        <com.android.internal.widget.WaveView
-            android:id="@+id/wave_view"
+        <TextView android:id="@+id/screenLocked"
             android:layout_width="wrap_content"
-            android:layout_height="match_parent"
-            android:layout_marginRight="0dip"
-            android:layout_weight="1.0"
-            />
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:gravity="center"
+            android:layout_marginTop="12dip"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentTop="true"
+            android:drawablePadding="4dip"/>
 
         <!-- "emergency calls only" shown when sim is missing or PUKd -->
         <TextView
@@ -76,24 +66,35 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_alignParentTop="true"
-            android:layout_marginTop="20dip"
+            android:layout_alignParentRight="true"
+            android:layout_marginTop="12dip"
             android:text="@string/emergency_calls_only"
-            android:textAppearance="?android:attr/textAppearanceSmall"
-            android:textColor="@color/white"
-               />
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:textColor="@color/white"/>
 
-        <!-- emergency call button shown when sim is PUKd and tab_selector is
-             hidden -->
+
+        <com.android.internal.widget.WaveView
+            android:id="@+id/wave_view"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_gravity="center_vertical|center_horizontal"
+            android:layout_marginRight="0dip"
+            android:layout_weight="1.0"/>
+
+
+        <!-- emergency call button shown when sim is PUKd and tab_selector is hidden -->
         <Button
             android:id="@+id/emergencyCallButton"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
+            android:layout_marginRight="80dip"
+            android:layout_marginBottom="80dip"
+            android:layout_alignParentRight="true"
+            android:layout_alignParentBottom="true"
             android:drawableLeft="@drawable/ic_emergency"
             style="@style/Widget.Button.Transparent"
-            android:drawablePadding="8dip"
-            android:layout_marginRight="80dip"
-            android:visibility="gone"
-            />
+            android:drawablePadding="8dip"/>
 
-    </LinearLayout>
+    </RelativeLayout>>
+
 </LinearLayout>
diff --git a/core/res/res/layout/global_actions_item.xml b/core/res/res/layout/global_actions_item.xml
index 68fe960..67b1644 100644
--- a/core/res/res/layout/global_actions_item.xml
+++ b/core/res/res/layout/global_actions_item.xml
@@ -40,7 +40,7 @@
         android:layout_alignParentBottom="true"
         android:layout_alignParentRight="true"
 
-        android:textAppearance="?android:attr/textAppearanceSmallInverse"
+        android:textAppearance="?android:attr/textAppearanceSmall"
         />
 
 
@@ -54,7 +54,7 @@
         android:layout_above="@id/status"
         android:layout_alignWithParentIfMissing="true"
         android:gravity="center_vertical"
-        android:textAppearance="?android:attr/textAppearanceLargeInverse"
+        android:textAppearance="?android:attr/textAppearanceLarge"
         />
 
 
diff --git a/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
new file mode 100644
index 0000000..d58fb23
--- /dev/null
+++ b/core/res/res/layout/keyguard_screen_sim_puk_landscape.xml
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@android:color/background_dark"
+        >
+
+    <LinearLayout android:id="@+id/topDisplayGroup"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <!-- header text ('Enter Puk Code') -->
+        <TextView android:id="@+id/headerText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+        <!-- Carrier info -->
+        <TextView android:id="@+id/carrier"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="9dip"
+            android:gravity="center"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <LinearLayout
+                android:orientation="vertical"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginRight="10dip"
+                android:layout_marginLeft="10dip">
+                <TextView android:id="@+id/enter_puk"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center_vertical"
+                    android:text="@android:string/keyguard_password_enter_puk_prompt"
+                    android:textSize="30sp"
+                    android:layout_marginBottom="10dip"/>
+                <TextView android:id="@+id/enter_pin"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center_vertical"
+                    android:text="@android:string/keyguard_password_enter_pin_prompt"
+                    android:textSize="30sp"
+                    android:layout_marginTop="10dip"/>
+            </LinearLayout>
+
+            <LinearLayout
+                  android:orientation="vertical"
+                  android:layout_width="wrap_content"
+                  android:layout_weight="1"
+                  android:layout_height="match_parent"
+                  android:paddingRight="0dip"
+                  android:layout_marginRight="10dip"
+                  android:layout_marginLeft="10dip">
+
+                  <LinearLayout
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal"
+                      android:layout_marginRight="6dip"
+                      android:layout_marginLeft="6dip"
+                      android:gravity="center_vertical"
+                      android:background="@android:drawable/edit_text">
+
+                      <!-- displays dots as user enters puk -->
+                      <TextView android:id="@+id/pukDisplay"
+                          android:layout_width="0dip"
+                          android:layout_height="wrap_content"
+                          android:layout_weight="1"
+                          android:maxLines="1"
+                          android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                          android:textStyle="bold"
+                          android:inputType="textPassword"
+                      />
+
+                      <ImageButton android:id="@+id/pukDel"
+                          android:src="@android:drawable/ic_input_delete"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_marginRight="-3dip"
+                          android:layout_marginBottom="-3dip"
+                      />
+                  </LinearLayout>
+
+
+                  <LinearLayout
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal"
+                      android:layout_marginRight="6dip"
+                      android:layout_marginLeft="6dip"
+                      android:gravity="center_vertical"
+                      android:background="@android:drawable/edit_text">
+
+                      <!-- displays dots as user enters new pin -->
+                      <TextView android:id="@+id/pinDisplay"
+                          android:layout_width="0dip"
+                          android:layout_height="wrap_content"
+                          android:layout_weight="1"
+                          android:maxLines="1"
+                          android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                          android:textStyle="bold"
+                          android:inputType="textPassword"
+                      />
+
+                      <ImageButton android:id="@+id/pinDel"
+                          android:src="@android:drawable/ic_input_delete"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_marginRight="-3dip"
+                          android:layout_marginBottom="-3dip"
+                      />
+                  </LinearLayout>
+              </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_alignParentBottom="true"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_marginBottom="8dip"
+        android:layout_marginLeft="8dip"
+        android:layout_marginRight="8dip">
+
+        <Button android:id="@+id/ok"
+            android:text="@android:string/ok"
+            android:layout_alignParentBottom="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginRight="8dip"
+            android:textSize="18sp"
+            />
+
+        <Button android:id="@+id/emergencyCall"
+            android:text="@android:string/lockscreen_emergency_call"
+            android:layout_alignParentBottom="true"
+            android:layout_centerHorizontal="true"
+            android:layout_width="0dip"
+            android:layout_height="wrap_content"
+            android:layout_weight="1.0"
+            android:layout_marginBottom="8dip"
+            android:layout_marginLeft="8dip"
+            android:textSize="18sp"
+            android:drawableLeft="@drawable/ic_emergency"
+            android:drawablePadding="8dip"
+        />
+    </LinearLayout>
+
+</RelativeLayout>
diff --git a/core/res/res/layout/keyguard_screen_sim_puk_portrait.xml b/core/res/res/layout/keyguard_screen_sim_puk_portrait.xml
new file mode 100644
index 0000000..5e392ef
--- /dev/null
+++ b/core/res/res/layout/keyguard_screen_sim_puk_portrait.xml
@@ -0,0 +1,185 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical"
+    android:background="@android:color/background_dark"
+    android:gravity="center_horizontal">
+
+    <LinearLayout android:id="@+id/topDisplayGroup"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical">
+
+        <!-- header text ('Enter Puk Code') -->
+        <TextView android:id="@+id/headerText"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:singleLine="true"
+            android:textAppearance="?android:attr/textAppearanceLarge"/>
+
+        <!-- Carrier info -->
+        <TextView android:id="@+id/carrier"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="9dip"
+            android:gravity="center"
+            android:singleLine="true"
+            android:ellipsize="marquee"
+            android:textAppearance="?android:attr/textAppearanceMedium"/>
+
+        <LinearLayout
+            android:orientation="horizontal"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content">
+
+            <LinearLayout
+                android:orientation="vertical"
+                android:layout_width="wrap_content"
+                android:layout_height="match_parent"
+                android:layout_marginRight="10dip"
+                android:layout_marginLeft="10dip">
+                <TextView android:id="@+id/enter_puk"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center_vertical"
+                    android:text="@android:string/keyguard_password_enter_puk_prompt"
+                    android:textSize="30sp"
+                    android:layout_marginBottom="10dip"/>
+                <TextView android:id="@+id/enter_pin"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:gravity="center_vertical"
+                    android:text="@android:string/keyguard_password_enter_pin_prompt"
+                    android:textSize="30sp"
+                    android:layout_marginTop="10dip"/>
+            </LinearLayout>
+
+            <LinearLayout
+                  android:orientation="vertical"
+                  android:layout_width="wrap_content"
+                  android:layout_weight="1"
+                  android:layout_height="match_parent"
+                  android:paddingRight="0dip"
+                  android:layout_marginRight="10dip"
+                  android:layout_marginLeft="10dip">
+
+                  <LinearLayout
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal"
+                      android:layout_marginRight="6dip"
+                      android:layout_marginLeft="6dip"
+                      android:gravity="center_vertical"
+                      android:background="@android:drawable/edit_text">
+
+                      <!-- displays dots as user enters puk -->
+                      <TextView android:id="@+id/pukDisplay"
+                          android:layout_width="0dip"
+                          android:layout_height="wrap_content"
+                          android:layout_weight="1"
+                          android:maxLines="1"
+                          android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                          android:textStyle="bold"
+                          android:inputType="textPassword"
+                      />
+
+                      <ImageButton android:id="@+id/pukDel"
+                          android:src="@android:drawable/ic_input_delete"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_marginRight="-3dip"
+                          android:layout_marginBottom="-3dip"
+                      />
+                  </LinearLayout>
+
+
+                  <LinearLayout
+                      android:layout_width="match_parent"
+                      android:layout_height="wrap_content"
+                      android:orientation="horizontal"
+                      android:layout_marginRight="6dip"
+                      android:layout_marginLeft="6dip"
+                      android:gravity="center_vertical"
+                      android:background="@android:drawable/edit_text">
+
+                      <!-- displays dots as user enters new pin -->
+                      <TextView android:id="@+id/pinDisplay"
+                          android:layout_width="0dip"
+                          android:layout_height="wrap_content"
+                          android:layout_weight="1"
+                          android:maxLines="1"
+                          android:textAppearance="?android:attr/textAppearanceLargeInverse"
+                          android:textStyle="bold"
+                          android:inputType="textPassword"
+                      />
+
+                      <ImageButton android:id="@+id/pinDel"
+                          android:src="@android:drawable/ic_input_delete"
+                          android:layout_width="wrap_content"
+                          android:layout_height="wrap_content"
+                          android:layout_marginRight="-3dip"
+                          android:layout_marginBottom="-3dip"
+                      />
+                  </LinearLayout>
+              </LinearLayout>
+        </LinearLayout>
+    </LinearLayout>
+
+    <include
+        android:id="@+id/keyPad"
+        layout="@android:layout/twelve_key_entry"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/topDisplayGroup"
+        android:layout_marginTop="10dip"
+    />
+
+    <!-- spacer below keypad -->
+    <View
+        android:id="@+id/spacerBottom"
+        android:layout_width="match_parent"
+        android:layout_height="1dip"
+        android:layout_marginTop="6dip"
+        android:layout_above="@id/emergencyCall"
+        android:background="@android:drawable/divider_horizontal_dark"
+    />
+
+    <!-- The emergency button should take the rest of the space and be centered vertically -->
+    <LinearLayout
+        android:layout_width="match_parent"
+        android:layout_height="0dip"
+        android:layout_weight="1"
+        android:gravity="center"
+        android:orientation="vertical">
+
+        <!-- emergency call button -->
+        <Button
+            android:id="@+id/emergencyCall"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:drawableLeft="@android:drawable/ic_emergency"
+            android:drawablePadding="8dip"
+            android:text="@android:string/lockscreen_emergency_call"
+        />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/preference.xml b/core/res/res/layout/preference.xml
index 1f92252..448f89a 100644
--- a/core/res/res/layout/preference.xml
+++ b/core/res/res/layout/preference.xml
@@ -22,7 +22,8 @@
     android:layout_height="wrap_content"
     android:minHeight="?android:attr/listPreferredItemHeight"
     android:gravity="center_vertical"
-    android:paddingRight="?android:attr/scrollbarSize">
+    android:paddingRight="?android:attr/scrollbarSize"
+    android:background="?android:attr/selectableItemBackground" >
 
     <ImageView
         android:id="@+android:id/icon"
diff --git a/core/res/res/layout/preference_widget_switch.xml b/core/res/res/layout/preference_widget_switch.xml
new file mode 100644
index 0000000..ed4ed57
--- /dev/null
+++ b/core/res/res/layout/preference_widget_switch.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<!-- Layout used by SwitchPreference for the switch widget style. This is inflated
+     inside android.R.layout.preference. -->
+<Switch xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+android:id/switchWidget"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    android:layout_gravity="center"
+    android:padding="16dip"
+    android:focusable="false"
+    android:clickable="false" />
diff --git a/core/res/res/layout/volume_adjust.xml b/core/res/res/layout/volume_adjust.xml
index b0ca3e8..7303003 100644
--- a/core/res/res/layout/volume_adjust.xml
+++ b/core/res/res/layout/volume_adjust.xml
@@ -15,22 +15,21 @@
 -->
 
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:gravity="left">
-
+    android:layout_width="480dp"
+    android:layout_height="wrap_content">
     <LinearLayout
-        android:layout_width="wrap_content"
+        android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:layout_marginTop="80dip"
+        android:layout_marginTop="80dp"
         android:background="@android:drawable/dialog_full_holo_dark"
         android:orientation="horizontal"
         >
 
         <LinearLayout
             android:id="@+id/slider_group"
-            android:layout_width="wrap_content"
+            android:layout_width="0dp"
             android:layout_height="wrap_content"
+            android:layout_weight="1"
             android:orientation="vertical"
             >
             <!-- Sliders go here -->
@@ -56,9 +55,6 @@
             android:background="?attr/selectableItemBackground"
             android:src="@drawable/ic_sysbar_quicksettings"
             />
-        
+
     </LinearLayout>
-
-</FrameLayout>
-
-
+</FrameLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/volume_adjust_item.xml b/core/res/res/layout/volume_adjust_item.xml
index e841d87..beb511d 100644
--- a/core/res/res/layout/volume_adjust_item.xml
+++ b/core/res/res/layout/volume_adjust_item.xml
@@ -15,7 +15,7 @@
 -->
 
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
+    android:layout_width="match_parent"
     android:layout_height="80dip"
     android:orientation="horizontal"
     android:layout_marginTop="8dip"
@@ -34,8 +34,9 @@
     <SeekBar
         style="?android:attr/seekBarStyle"
         android:id="@+id/seekbar"
-        android:layout_width="300dip"
+        android:layout_width="300dp"
         android:layout_height="wrap_content"
+        android:layout_weight="1"
         android:padding="16dip"
         android:layout_marginLeft="8dip"
         android:layout_marginRight="8dip" />
diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml
index 38b62d4..cab43ac 100644
--- a/core/res/res/values-af/strings.xml
+++ b/core/res/res/values-af/strings.xml
@@ -412,6 +412,10 @@
     <!-- no translation found for permlab_setOrientation (3365947717163866844) -->
     <skip />
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Laat \'n program toe om die skerm te enige tyd te draai. Behoort vir gewone programme nooit nodig te wees nie."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
     <skip />
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Laat program toe om te versoek dat die voorsiende materiaal aan alle aanhoudende prosesse gestuur word."</string>
diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml
index 9c22f0e..b97bc7a 100644
--- a/core/res/res/values-am/strings.xml
+++ b/core/res/res/values-am/strings.xml
@@ -396,6 +396,10 @@
     <!-- no translation found for permlab_setOrientation (3365947717163866844) -->
     <skip />
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"ትግበራው በማንኛውም ሰዓት የማያንማሽከርከሪያለመለወጥ ይፈቅዳል።ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
     <skip />
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"ትግበራዎች ለሁሉም ተከታታይ ሂደቶች ልከው የሚያቀርቧቸው ሲግናሎችን ለመጠየቅ ይፈቅዳል።"</string>
diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml
index c9454d3..8d5ecb8 100644
--- a/core/res/res/values-ar/strings.xml
+++ b/core/res/res/values-ar/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"للسماح للمالك بإرسال الأهداف إلى أحد مشرفي الجهاز. لا يجب استخدامه على الإطلاق للتطبيقات العادية."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"تغيير اتجاه الشاشة"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"للسماح لتطبيق ما بتغيير تدوير الشاشة في أي وقت. لا يجب استخدامه على الإطلاق للتطبيقات العادية."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"إرسال إشارات Linux للتطبيقات"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"للسماح للتطبيق بطلب إرسال الإشارة المزوّدة لجميع العمليات المستمرة."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"تشغيل التطبيق دائمًا"</string>
diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml
index 70eb79a..b43b50f 100644
--- a/core/res/res/values-bg/strings.xml
+++ b/core/res/res/values-bg/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Разрешава на притежателя да изпраща намерения до администратор на устройството. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"промяна на ориентацията на екрана"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Разрешава на приложението да променя ориентацията на екрана по всяко време. Нормалните приложения би трябвало никога да не се нуждаят от това."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"изпращане на сигнали от Linux до приложенията"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Разрешава на приложението да подаде заявка предоставеният сигнал да се изпрати до всички постоянни процеси."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"задаване на постоянно изпълнение на приложението"</string>
diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml
index 45201c1..2947b93 100644
--- a/core/res/res/values-ca/strings.xml
+++ b/core/res/res/values-ca/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permet al titular enviar intencions a un administrador del sistema. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"canviar l\'orientació de la pantalla"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permet a una aplicació canviar el gir de la pantalla en qualsevol moment. No s\'hauria de necessitar mai per a les aplicacions normals."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar senyals Linux a les aplicacions"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permet a l\'aplicació sol·licitar que el senyal subministrat s\'enviï a tots els processos persistents."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"fer que l\'aplicació s\'executi sempre"</string>
diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml
index f88e381..92deaca 100644
--- a/core/res/res/values-cs/strings.xml
+++ b/core/res/res/values-cs/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Umožňuje držiteli oprávnění odesílat informace správci zařízení. Běžné aplikace by toto oprávnění nikdy neměly požadovat."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"změna orientace obrazovky"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Umožňuje aplikaci kdykoli změnit orientaci obrazovky. Běžné aplikace by toto nastavení nikdy neměly využívat."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"odeslání signálů Linux aplikacím"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Umožňuje aplikaci vyžádat zaslání poskytnutého signálu všem trvalým procesům."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"trvalé spuštění aplikace"</string>
diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml
index c6946d4..a3149a2 100644
--- a/core/res/res/values-da/strings.xml
+++ b/core/res/res/values-da/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillader brugeren at sende hensigter til en enhedsadministrator. Bør aldrig være nødvendigt for almindelige programmer."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"skift skærmretning"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillader, at et program ændrer rotationen af skærmen når som helst. Bør aldrig være nødvendigt til normale programmer."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"send Linux-signaler til programmer"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Tillader, at programmet kan anmode om, at det leverede signal sendes til alle vedholdende processer."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"lad altid programmet køre"</string>
diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml
index ffef226..b6b6be5 100644
--- a/core/res/res/values-de/strings.xml
+++ b/core/res/res/values-de/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Ermöglicht dem Halter, Intents an einen Geräteadministrator zu senden. Sollte nie für normale Anwendungen benötigt werden."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"Bildschirmausrichtung ändern"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Ermöglicht der Anwendung, die Bildschirmdrehung jederzeit zu ändern. Sollte nicht für normale Anwendungen benötigt werden."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-Signale an Anwendungen senden"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ermöglicht der Anwendung, das Senden des gelieferten Signals an alle anhaltenden Prozesse zu fordern"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"Anwendungen permanent ausführen"</string>
diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml
index bf1d432f..a5e13f1 100644
--- a/core/res/res/values-el/strings.xml
+++ b/core/res/res/values-el/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Επιτρέπει στον κάτοχο την αποστολή στόχων σε έναν διαχειριστή συσκευής. Δεν θα χρειαστεί ποτέ για κανονικές εφαρμογές."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"αλλαγή προσανατολισμού οθόνης"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Επιτρέπει σε μια εφαρμογή την αλλαγή της περιστροφής της οθόνης οποιαδήποτε στιγμή. Δεν είναι απαραίτητο για κανονικές εφαρμογές."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"αποστολή σημάτων Linux σε εφαρμογές"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Επιτρέπει σε μια εφαρμογή την αποστολή αιτήματος για την αποστολή του παρεχόμενου σήματος σε όλες τις υπάρχουσες διαδικασίες."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"η εφαρμογή να εκτελείται συνεχώς"</string>
diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml
index b0e77ba..d5ef98b 100644
--- a/core/res/res/values-en-rGB/strings.xml
+++ b/core/res/res/values-en-rGB/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Allows the holder to send intents to a device administrator. Should never be needed for normal applications."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"change screen orientation"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Allows an application to change the rotation of the screen at any time. Should never be needed for normal applications."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"send Linux signals to applications"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Allows application to request that the supplied signal be sent to all persistent processes."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"make application always run"</string>
diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml
index fb97c4a..3cb61e4 100644
--- a/core/res/res/values-es-rUS/strings.xml
+++ b/core/res/res/values-es-rUS/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite que el propietario envíe sus intentos a un administrador de dispositivos. No se necesita para las aplicaciones normales."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar la orientación de la pantalla"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Admite una aplicación que cambia la rotación de la pantalla en cualquier momento. Se debe evitar utilizarlo en aplicaciones normales."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales de Linux a las aplicaciones"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Admite que la aplicación solicite el envío de la señal suministrada a todos los procesos continuos."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"hacer que siempre se ejecute la aplicación"</string>
diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml
index a1e3a54..9db80ca 100644
--- a/core/res/res/values-es/strings.xml
+++ b/core/res/res/values-es/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite enviar intentos a un administrador de dispositivos. Este permiso nunca debería ser necesario para las aplicaciones normales."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"cambiar orientación de la pantalla"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que una aplicación cambie la rotación de la pantalla en cualquier momento. No debería ser necesario nunca para las aplicaciones normales."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar señales Linux a aplicaciones"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite que la aplicación solicite que la señal suministrada se envíe a todos los procesos persistentes."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"hacer que la aplicación se ejecute siempre"</string>
diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml
index 00d173d..3fcb21d 100644
--- a/core/res/res/values-fa/strings.xml
+++ b/core/res/res/values-fa/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"به نگهدارنده اجازه می دهد مفاد را به یک سرپرست دستگاه ارسال کند. هرگز برای برنامه های معمولی مورد نیاز نیست."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"تغییر جهت صفحه"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"به یک برنامه کاربردی اجازه می دهد چرخش صفحه را در هر زمانی تغییر دهد. هرگز برای برنامه های معمولی مورد نیاز نیست."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"ارسال سیگنال های Linux برای برنامه ها"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"به برنامه کاربردی اجازه می دهد درخواست کند که سیگنال ارائه شده را به تمام فرایندهای ثابت ارسال کند."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"برنامه همیشه اجرا شود"</string>
diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml
index 2d3bf48..70e5ff7 100644
--- a/core/res/res/values-fi/strings.xml
+++ b/core/res/res/values-fi/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Antaa sovelluksen lähettää kyselyitä laitteen järjestelmänvalvojalle. Ei tavallisten sovelluksien käyttöön."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"muuta näytön suuntaa"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Antaa sovelluksen vaihtaa näytön suuntaa milloin tahansa. Ei tavallisten sovelluksien käyttöön."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"lähettää sovelluksiin Linux-signaaleja"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Antaa sovelluksen pyytää, että signaali lähetetään kaikille kiinteille prosesseille."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"aseta sovellus olemaan jatkuvasti käynnissä"</string>
diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml
index 3f3eb93..c618edf 100644
--- a/core/res/res/values-fr/strings.xml
+++ b/core/res/res/values-fr/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permet à l\'application d\'envoyer des intentions à l\'administrateur de l\'appareil. Les applications standard ne devraient jamais avoir recours à cette fonctionnalité."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"Changement d\'orientation de l\'écran"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permet à une application de modifier la rotation de l\'écran à tout moment. Les applications normales ne devraient jamais avoir recours à cette fonctionnalité."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Envoi de signaux Linux aux applications"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permet à une application de demander que le signal fourni soit envoyé à tous les processus persistants."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"Exécution de l\'application en continu"</string>
@@ -449,7 +453,7 @@
     <string name="permlab_nfc" msgid="4423351274757876953">"contrôler la communication en champ proche"</string>
     <string name="permdesc_nfc" msgid="9171401851954407226">"Permet à une application de communiquer avec des tags, cartes et lecteurs prenant en charge la communication en champ proche (NFC)."</string>
     <string name="permlab_vpn" msgid="8345800584532175312">"intercepter et modifier l\'ensemble du trafic réseau"</string>
-    <string name="permdesc_vpn" msgid="5617893078989944219">"Permet à une application d\'intercepter et d\'inspecter l\'ensemble du trafic réseau, par exemple pour établir une connexion VPN. Des applications malveillantes sont susceptibles de surveiller, rediriger ou modifier les paquets réseau à votre insu."</string>
+    <string name="permdesc_vpn" msgid="5617893078989944219">"Permet à une application d\'intercepter et de contrôler l\'ensemble du trafic réseau, par exemple pour établir une connexion VPN. Des applications malveillantes sont susceptibles de surveiller, rediriger ou modifier les paquets réseau à votre insu."</string>
     <string name="permlab_disableKeyguard" msgid="4977406164311535092">"Désactivation du verrouillage des touches"</string>
     <string name="permdesc_disableKeyguard" msgid="3189763479326302017">"Permet à une application de désactiver le verrouillage des touches et toute sécurité par mot de passe. Exemple : Votre téléphone désactive le verrouillage du clavier lorsque vous recevez un appel, puis le réactive lorsque vous raccrochez."</string>
     <string name="permlab_readSyncSettings" msgid="6201810008230503052">"Lecture des paramètres de synchronisation"</string>
diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml
index 773a8f3..f17a272 100644
--- a/core/res/res/values-hr/strings.xml
+++ b/core/res/res/values-hr/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Nositelju omogućuje slanje namjera administratoru uređaja. Nikad ne bi trebalo koristiti za uobičajene aplikacije."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"promjena orijentacije zaslona"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Aplikaciji omogućuje promjenu rotacije zaslona u svakom trenutku. Nikad ne bi trebalo koristiti za uobičajene aplikacije."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"slanje Linux signala u aplikaciju"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Aplikacijama omogućuje traženje zahtjeva da se dobiveni signal pošalje na sve trajne postupke."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"uvijek pokrenuta aplikacija"</string>
diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml
index 09dd648..0c2aef3 100644
--- a/core/res/res/values-hu/strings.xml
+++ b/core/res/res/values-hu/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Lehetővé teszi a használó számára, hogy célokat küldjön egy eszközkezelőnek. A normál alkalmazásoknak erre soha nincs szüksége."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"képernyő irányának módosítása"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Lehetővé teszi egy alkalmazás számára a képernyő elforgatásának módosítását. A normál alkalmazásoknak erre soha nincs szüksége."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux jelek küldése alkalmazásoknak"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Lehetővé teszi az alkalmazás számára, hogy a megadott jelet elküldje az összes állandó folyamatnak."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"alkalmazások folyamatos futtatása"</string>
diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml
index b2f3af8..00e2dd4 100644
--- a/core/res/res/values-in/strings.xml
+++ b/core/res/res/values-in/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Mengizinkan pemegang mengirimkan tujuan kepada administrator perangkat. Tidak diperlukan untuk aplikasi normal."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ubah orientasi layar"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Mengizinkan aplikasi mengubah rotasi layar kapan saja. Tidak diperlukan untuk aplikasi normal."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"kirim sinyal Linux ke aplikasi"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Mengizinkan aplikasi meminta sinyal yang diberikan untuk dikirimkan ke semua proses yang ada."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"jadikan aplikasi selalu berjalan"</string>
diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml
index 19d317e..0d78861 100644
--- a/core/res/res/values-it/strings.xml
+++ b/core/res/res/values-it/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Consente l\'invio di intent a un amministratore del dispositivo. L\'autorizzazione non deve mai essere necessaria per le normali applicazioni."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"modifica orientamento dello schermo"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Consente a un\'applicazione di cambiare la rotazione dello schermo in qualsiasi momento. Non dovrebbe essere mai necessario per le normali applicazioni."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"invio segnali Linuz alle applicazioni"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Consente all\'applicazione di richiedere l\'invio del segnale fornito a tutti i processi persistenti."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"esecuzione permanente delle applicazioni"</string>
diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml
index 4aba678..6b50982 100644
--- a/core/res/res/values-iw/strings.xml
+++ b/core/res/res/values-iw/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"מאפשר למשתמש לשלוח כוונות למנהל התקן. לא אמור להידרש לעולם ביישומים רגילים."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"שנה את כיוון המסך"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"מאפשר ליישום לשנות את סיבוב המסך בכל עת. לא אמור להידרש לעולם ביישומים רגילים."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"שלח אותות Linux ליישומים"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"מאפשר ליישום לבקש שהאות המסופק יישלח לכל התהליכים המתמשכים."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"הגדר את היישום לפעול תמיד"</string>
diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml
index e078274..bdad7b6 100644
--- a/core/res/res/values-ja/strings.xml
+++ b/core/res/res/values-ja/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"デバイス管理者へのintentの送信を所有者に許可します。通常のアプリケーションでは不要です。"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"画面の向きの変更"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"いつでも画面の回転を変更することをアプリケーションに許可します。通常のアプリケーションにはまったく必要ありません。"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linuxのシグナルをアプリケーションに送信"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"受信した電波を継続プロセスに送信することをアプリケーションに許可します。"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"アプリケーションを常に実行する"</string>
diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml
index 4d1778e..5a3584a 100644
--- a/core/res/res/values-ko/strings.xml
+++ b/core/res/res/values-ko/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"보유자가 기기 관리자에게 인텐트를 보낼 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"화면 방향 변경"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"애플리케이션이 언제든지 화면 회전을 변경할 수 있도록 합니다. 일반 애플리케이션에는 절대로 필요하지 않습니다."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"애플리케이션에 Linux 시그널 보내기"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"애플리케이션이 제공된 시그널을 모든 영구 프로세스로 보내도록 요청할 수 있도록 합니다."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"애플리케이션이 항상 실행되도록 설정"</string>
diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml
index 5b7ef8c..eab92c6 100644
--- a/core/res/res/values-lt/strings.xml
+++ b/core/res/res/values-lt/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Leidžia savininkui siųsti tikslus įrenginio administratoriui. Neturėtų reikėti įprastose programose."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"keisti ekrano padėtį"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Leidžia programai bet kuriuo metu keisti ekrano sukimą. Neturėtų reikėti įprastoms programoms."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"programoms siųsti „Linux“ signalus"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Leidžia programai pateikti užklausą, kad teikiamas signalas būtų siunčiamas visiems nuolatiniams procesams."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"nustatyti, kad programa būtų visada paleista"</string>
diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml
index 2aa49ca..5c5525c 100644
--- a/core/res/res/values-lv/strings.xml
+++ b/core/res/res/values-lv/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Ļauj īpašniekam sūtīt nolūkus ierīces administratoram. Nekad nav nepieciešams parastajām lietojumprogrammām."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"mainīt ekrāna orientāciju"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Ļauj lietojumprogrammai jebkurā brīdī mainīt ekrāna pagriešanas iestatījumu. Parastajām lietojumprogrammām nekad nav nepieciešama."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"sūtīt Linux signālus uz lietojumprogrammām"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ļauj lietojumprogrammai pieprasīt, lai piegādātais signāls tiktu sūtīts uz visiem pastāvīgajiem procesiem."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"likt lietojumprogrammai vienmēr darboties"</string>
diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml
index e82fc10..6b0e18e 100644
--- a/core/res/res/values-ms/strings.xml
+++ b/core/res/res/values-ms/strings.xml
@@ -297,6 +297,10 @@
     <skip />
     <string name="permlab_setOrientation" msgid="3365947717163866844">"tukar orientasi skrin"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Membolehkan aplikasi untuk menukar putaran skrin pada bila-bila masa. Tidak seharusnya diperlukan untuk aplikasi biasa."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"hantar isyarat Linux kepada aplikasi"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Membenarkan aplikasi meminta isyarat yang dibekalkan dihantar kepada semua proses gigih."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"buatkan aplikasi sentiasa berjalan"</string>
diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml
index 02a414c..2413685 100644
--- a/core/res/res/values-nb/strings.xml
+++ b/core/res/res/values-nb/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillater innehaveren å sende hensikter til enhetsadministrator. Bør aldri være nødvendig for normale programmer."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"snu skjermen"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Lar applikasjonen rotere skjermen når som helst. Vanlige applikasjoner bør aldri trenge dette."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"sende Linux-signaler til applikasjoner"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Lar applikasjonen spørre om at et gitt signal blir sendt til alle varige prosesser."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"forbli kjørende"</string>
diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml
index ad214bf..dbb7e98 100644
--- a/core/res/res/values-nl/strings.xml
+++ b/core/res/res/values-nl/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Staat de houder toe intenties te verzenden naar een apparaatbeheerder. Nooit vereist voor normale toepassingen."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"schermstand wijzigen"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Hiermee kan een app op elk gewenst moment de oriëntatie van het scherm wijzigen. Nooit vereist voor normale apps."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"Linux-signalen verzenden naar toepassingen"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Hiermee kan de app ervoor zorgen dat het geleverde signaal wordt verzonden naar alle persistente processen."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"app altijd laten uitvoeren"</string>
diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml
index ed877bb..36d3cdd 100644
--- a/core/res/res/values-pl/strings.xml
+++ b/core/res/res/values-pl/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Zezwala posiadaczowi na wysyłanie informacji o zamiarach do administratora urządzenia. Opcja nie powinna być nigdy potrzebna w przypadku zwykłych aplikacji."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"zmienianie orientacji ekranu"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Pozwala aplikacji na zmianę orientacji ekranu w dowolnym momencie. Nigdy nie powinno być potrzeby stosowania w normalnych aplikacjach."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"wysyłanie sygnałów systemu Linux do aplikacji"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Pozwala aplikacjom żądać, aby dostarczany sygnał był wysyłany do wszystkich trwających procesów."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"sprawianie, że aplikacja jest cały czas uruchomiona"</string>
diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml
index 7ba9ad0..9cec4b6 100644
--- a/core/res/res/values-pt-rPT/strings.xml
+++ b/core/res/res/values-pt-rPT/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite ao titular enviar intenções para um administrador do dispositivo. Nunca deverá ser necessário para aplicações normais."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"mudar orientação do ecrã"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite a uma aplicação mudar a rotação do ecrã em qualquer momento. Nunca deve ser necessário para aplicações normais."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais Linux para aplicações"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite à aplicação pedir que o sinal fornecido seja enviado a todos os processos persistentes."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"fazer com que a aplicação seja sempre executada"</string>
diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml
index 750a751..57737c9 100644
--- a/core/res/res/values-pt/strings.xml
+++ b/core/res/res/values-pt/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite que o detentor envie tentativas ao administrador de um dispositivo. Não é necessário para aplicativos normais."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"alterar orientação da tela"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite que um aplicativo altere a rotação da tela a qualquer momento. Aplicativos normais não devem precisar disso em momento algum."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"enviar sinais de Linux para os aplicativos"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite que o aplicativo solicite que o sinal fornecido seja enviado a todos os processos persistentes."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"executar sempre o aplicativo"</string>
diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml
index ff2eecb..60f6049 100644
--- a/core/res/res/values-rm/strings.xml
+++ b/core/res/res/values-rm/strings.xml
@@ -275,6 +275,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permetta al possessur da trametter intenziuns a l\'administratur dal apparat periferic. Betg previs per applicaziuns normalas."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"midar l\'orientaziun dal visur"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permetta a l\'applicaziun da midar da tut temp la orientaziun dal visur. Betg previs per applicaziuns normalas."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"trametter signals Linux a las applicaziuns"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permetta a l\'applicaziun da pretender ch\'il signal furnì vegnia tramess a tut ils process persistents."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"exequir permanentamain applicaziuns"</string>
diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml
index 7fb4f73..1961903 100644
--- a/core/res/res/values-ro/strings.xml
+++ b/core/res/res/values-ro/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Permite proprietarului să trimită intenţii către un administrator al dispozitivului. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"modificare orientare ecran"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Permite unei aplicaţii să modifice rotaţia ecranului în orice moment. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"trimitere semnale Linux către aplicaţii"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Permite aplicaţiei să solicite trimiterea semnalului furnizat către toate procesele persistente."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"rulare întotdeauna a aplicaţiei"</string>
diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml
index fe5815f..83dc30f1 100644
--- a/core/res/res/values-ru/strings.xml
+++ b/core/res/res/values-ru/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Позволяет владельцу отправлять целевые значения администратору устройства. Никогда не используется обычными приложениями."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"изменять ориентацию экрана"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Позволяет приложению изменять ориентацию экрана в любое время. Не требуется для обычных приложений."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"отправлять приложениям сигналы Linux"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Позволяет приложению направлять запрос на передачу предоставленного сигнала всем постоянным процессам."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"запускать постоянную работу приложения"</string>
diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml
index d7d9725..3439f59 100644
--- a/core/res/res/values-sk/strings.xml
+++ b/core/res/res/values-sk/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Umožňuje držiteľovi odosielať informácie správcovi zariadenia. Bežné aplikácie by toto oprávnenie nemali nikdy požadovať."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"zmena orientácie obrazovky"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Umožňuje aplikácii kedykoľvek zmeniť orientáciu obrazovky. Bežné aplikácie by toto nastavenie nemali vôbec využívať."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"odoslanie signálov systému Linux aplikáciám"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Umožňuje aplikácii vyžiadať zaslanie poskytnutého signálu všetkým trvalým procesom."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"trvalé spustenie aplikácie"</string>
diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml
index cbb097b..cd4cd07 100644
--- a/core/res/res/values-sl/strings.xml
+++ b/core/res/res/values-sl/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Dovoljuje lastniku, da pošlje namere skrbniku naprave. Nikoli se ne uporablja za navadne programe."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"spreminjanje usmerjenosti zaslona"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Dovoljuje, da program kadar koli spremeni smer sukanja zaslona. Tega nikoli ni treba uporabiti za navadne programe."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"pošiljanje signalov Linuxa programom"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Dovoljuje, da program zahteva, da je posredovan signal poslan vsem trajnim procesom."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"neprekinjeno izvajanje programov"</string>
diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml
index 93faf2f..59329b4 100644
--- a/core/res/res/values-sr/strings.xml
+++ b/core/res/res/values-sr/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Омогућава власнику да шаље своје намере администратору уређаја. Обичне апликације никада не би требало да је користе."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"промена положаја екрана"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Омогућава да апликација у сваком тренутку промени ротацију екрана. Обичне апликације никада не би требало да је користе."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"слање Linux сигнала апликацијама"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Омогућава да апликација захтева да испоручени сигнал буде послат свим трајним процесима."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"омогућавање непрекидне активности апликације"</string>
diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml
index c3561d7..a3350e5 100644
--- a/core/res/res/values-sv/strings.xml
+++ b/core/res/res/values-sv/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Tillåter att innehavaren skickar avsikter till en enhetsadministratör. Vanliga program behöver aldrig göra detta."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ändra bildskärmens rikting"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Tillåter att ett program när som helst ändrar skärmens rotering. Behövs inte för vanliga program."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"skicka Linux-signaler till appar"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Tillåter att programmet begär att den angivna signalen skickas till alla beständiga processer."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"se till att programmet alltid körs"</string>
diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml
index 453f74e..ff1907a 100644
--- a/core/res/res/values-sw/strings.xml
+++ b/core/res/res/values-sw/strings.xml
@@ -396,6 +396,10 @@
     <!-- no translation found for permlab_setOrientation (3365947717163866844) -->
     <skip />
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Huruhusu programu kubadilisha uzungukaji wa skrini wakati wowote. Haipaswi kuhitajika kwa programu za kawaida za kompyuta."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
     <skip />
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Huruhusu programu kuomba kwamba mawimbi yaliyotolewa yatumwe kwa michakato yote isiyokoma."</string>
diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml
index 54b33f4..be958dc 100644
--- a/core/res/res/values-th/strings.xml
+++ b/core/res/res/values-th/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"อนุญาตให้ผู้ถือส่งเนื้อหาไปยังโปรแกรมควบคุมอุปกรณ์ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"เปลี่ยนการวางแนวหน้าจอ"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"อนุญาตให้แอปพลิเคชันเปลี่ยนการหมุนหน้าจอได้ตลอดเวลา ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"ส่งสัญญาณ Linux ไปยังแอปพลิเคชัน"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"อนุญาตให้แอปพลิเคชันร้องขอให้ส่งสัญญาณแจ้งไปยังกระบวนการที่ยังทำงานอยู่ทั้งหมด"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"ทำให้แอปพลิเคชันทำงานตลอดเวลา"</string>
diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml
index 9eeac34..b6942a4 100644
--- a/core/res/res/values-tl/strings.xml
+++ b/core/res/res/values-tl/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Pinapayagan ang holder na magpadala ng mga intensyon sa administrator ng device. Hindi kailanman dapat kailanganin para sa mga normal na application."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"baguhin ang orientation ng screen"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Pinapayagan ang isang application na baguhin ang pag-rotate ng screen anumang oras. Hindi kailanman dapat kailanganin para sa mga normal na application."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"magpadala ng mga Linux signal sa mga application"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Pinapayagan ang application na hilinging ipadala ang na-supply na signal sa lahat ng mga paulit-ulit na proseso."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"palaging patakbuhin ang mga application"</string>
diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml
index 7f1d3c3..e97d834 100644
--- a/core/res/res/values-tr/strings.xml
+++ b/core/res/res/values-tr/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Cihazın sahibinin cihaz yöneticisine amaç göndermesine izin verir. Normal uygulamalarda hiçbir zaman gerek duyulmamalıdır."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"ekran yönünü değiştir"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Uygulamaların ekran yönünü istedikleri zaman değiştirmesine izin verir. Normal uygulamalarda hiçbir zaman gerekmemelidir."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"uygulamalara Linux sinyalleri gönder"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Uygulamaların, sağlanan sinyalin tüm kalıcı işlemlere gönderilmesini istemesine izin verir."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"uygulamayı her zaman çalıştır"</string>
diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml
index 00e6333..07b11f7 100644
--- a/core/res/res/values-uk/strings.xml
+++ b/core/res/res/values-uk/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Дозволяє власнику надсилати цілі адміністратору пристрою. Ніколи не потрібний для звичайних програм."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"змінювати орієнтацію екрана"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Дозволяє програмі будь-коли змінювати обертання екрана. Ніколи не потрібний для звичайних програм."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"надсилати сигнали Linux програмам"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Дозволяє програмі подавати запит щодо надсилання наданого сигналу всім постійним процесам."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"збер. програму завжди запущ."</string>
diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml
index 45c3ad8..cfb53ac 100644
--- a/core/res/res/values-vi/strings.xml
+++ b/core/res/res/values-vi/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"Cho phép chủ nhân gửi các ý định đến quản trị viên thiết bị. Không cần thiết cho các ứng dụng thông thường."</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"thay đổi hướng màn hình"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Cho phép ứng dụng thay đổi việc xoay màn hình bất kỳ khi nào. Không cần thiết cho các ứng dụng thông thường."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"gửi tín hiệu Linux đến ứng dụng"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Cho phép ứng dụng yêu cầu tín hiệu đã cung cấp được gửi đến tất cả các quá trình liên tục."</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"đặt ứng dụng luôn chạy"</string>
diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml
index f5ee751..b45b346 100644
--- a/core/res/res/values-zh-rCN/strings.xml
+++ b/core/res/res/values-zh-rCN/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"允许持有对象将意向发送到设备管理器。普通的应用程序一律无需此权限。"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"更改屏幕显示方向"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"允许应用程序随时更改屏幕的旋转方向。普通应用程序从不需要使用此权限。"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"向应用程序发送 Linux 信号"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"允许应用程序请求将所提供的信号发送给所有持久进程。"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"让应用程序始终运行"</string>
diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml
index 1779baf..399a5b0 100644
--- a/core/res/res/values-zh-rTW/strings.xml
+++ b/core/res/res/values-zh-rTW/strings.xml
@@ -265,6 +265,10 @@
     <string name="permdesc_bindDeviceAdmin" msgid="8714424333082216979">"允許應用程式將調用請求 (intent) 傳送至裝置管理員;一般應用程式不需使用此選項。"</string>
     <string name="permlab_setOrientation" msgid="3365947717163866844">"變更螢幕顯示方向"</string>
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"允許應用程式可隨時變更螢幕旋轉方向。一般應用不應使用這項功能。"</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <string name="permlab_signalPersistentProcesses" msgid="4255467255488653854">"傳送 Linux 訊號到應用程式"</string>
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"允許應用程式要求將支援的訊號傳送到所有持續的程序。"</string>
     <string name="permlab_persistentActivity" msgid="8659652042401085862">"設定應用程式持續執行"</string>
diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml
index ad143a7..6f147e7 100644
--- a/core/res/res/values-zu/strings.xml
+++ b/core/res/res/values-zu/strings.xml
@@ -396,6 +396,10 @@
     <!-- no translation found for permlab_setOrientation (3365947717163866844) -->
     <skip />
     <string name="permdesc_setOrientation" msgid="6335814461615851863">"Ivumela uhlelo lokusebenza ukushintsha ukujikeleleza kwesikrini nganoma isiphi isikhathi. Ayisoze yadingeka izinhlelo zokusebenza ezivamile."</string>
+    <!-- no translation found for permlab_setPointerSpeed (9175371613322562934) -->
+    <skip />
+    <!-- no translation found for permdesc_setPointerSpeed (137436038503379864) -->
+    <skip />
     <!-- no translation found for permlab_signalPersistentProcesses (4255467255488653854) -->
     <skip />
     <string name="permdesc_signalPersistentProcesses" msgid="3565530463215015289">"Ivumela uhlelo lokusebenza ukucela ukuthi isiginali enikeziwe ithunyelwe kuzo zonke izinqubo eziphikelelayo"</string>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 9c55627..d5da592 100755
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -374,11 +374,11 @@
              pointer will go until that pointers go up thereby enabling touches
              with multiple pointers to be split across multiple windows. -->
         <attr name="windowEnableSplitTouch" format="boolean" />
-        
+
         <!-- Control whether a container should automatically close itself if
              the user touches outside of it.  This only applies to activities
              and dialogs.
-             
+
              <p>Note: this attribute will only be respected for applications
              that are targeting {@link android.os.Build.VERSION_CODES#HONEYCOMB}
              or later. -->
@@ -548,7 +548,7 @@
         <!-- NumberPicker input text style. -->
         <attr name="numberPickerInputTextStyle" format="reference" />
 
-        <!-- The CalndarView style. -->
+        <!-- The CalendarView style. -->
         <attr name="calendarViewStyle" format="reference" />
 
         <!-- The TimePicker style. -->
@@ -658,6 +658,8 @@
         <attr name="preferenceLayoutChild" format="reference" />
         <!-- Preference panel style -->
         <attr name="preferencePanelStyle" format="reference" />
+        <!-- Default style for switch preferences. -->
+        <attr name="switchPreferenceStyle" format="reference" />
 
         <!-- ============================ -->
         <!-- Text selection handle styles -->
@@ -1191,9 +1193,9 @@
              edge, a right gravity will clip the left edge, and neither will clip both edges. -->
         <flag name="clip_horizontal" value="0x08" />
         <!-- Push object to the beginning of its container, not changing its size. -->
-        <flag name="before" value="0x00800003" />
+        <flag name="start" value="0x00800003" />
         <!-- Push object to the end of its container, not changing its size. -->
-        <flag name="after" value="0x00800005" />
+        <flag name="end" value="0x00800005" />
     </attr>
 
     <!-- Standard orientation constant. -->
@@ -1914,7 +1916,7 @@
             <!-- Place the scroll bar on the right. -->
             <enum name="right" value="2" />
         </attr>
-        
+
         <!-- Specifies the type of layer backing this view. The default value is none.
              Refer to {@link android.view.View#setLayerType(int, android.graphics.Paint)}
              for more information.-->
@@ -1932,11 +1934,11 @@
         </attr>
 
         <!-- Defines the direction of layout drawing. This typically is associated with writing
-             direction of the language script used. The possible values are Left-to-Right,
-             Right-to-Left, Locale and Inherit from parent view. If there is nothing to inherit,
-             Locale is used. Locale fallsback to 'en-US'. Left-to-Right is the direction used in
-             'en-US'. The default for this attribute is 'inherit'. -->
-        <attr name="horizontalDirection">
+             direction of the language script used. The possible values are "ltr" for Left-to-Right,
+             "rtl" for Right-to-Left, "locale" and "inherit" from parent view. If there is nothing
+             to inherit, "locale" is used. "locale" falls back to "en-US". "ltr" is the direction
+             used in "en-US". The default for this attribute is "inherit". -->
+        <attr name="layoutDirection">
             <!-- Left-to-Right -->
             <enum name="ltr" value="0" />
             <!-- Right-to-Left -->
@@ -4814,7 +4816,7 @@
         <!-- The view id of the AppWidget subview which should be auto-advanced.
              by the widget's host. -->
         <attr name="autoAdvanceViewId" format="reference" />
-        <!-- Optional parameter which indicates if and how this widget can be 
+        <!-- Optional parameter which indicates if and how this widget can be
              resized. -->
         <attr name="resizeMode" format="integer">
             <flag name="none" value="0x0" />
@@ -5156,6 +5158,28 @@
         <!-- number of megabytes of storage MTP should reserve for free storage
              (used for emulated storage that is shared with system's data partition) -->
         <attr name="mtpReserve" format="integer" />
+        <!-- true if the storage can be shared via USB mass storage -->
+        <attr name="allowMassStorage" format="boolean" />
+    </declare-styleable>
+
+    <declare-styleable name="SwitchPreference">
+        <!-- The summary for the Preference in a PreferenceActivity screen when the
+             SwitchPreference is checked. If separate on/off summaries are not
+             needed, the summary attribute can be used instead. -->
+        <attr name="summaryOn" />
+        <!-- The summary for the Preference in a PreferenceActivity screen when the
+             SwitchPreference is unchecked. If separate on/off summaries are not
+             needed, the summary attribute can be used instead. -->
+        <attr name="summaryOff" />
+        <!-- The text used on the switch itself when in the "on" state.
+             This should be a very SHORT string, as it appears in a small space. -->
+        <attr name="switchTextOn" format="string" />
+        <!-- The text used on the switch itself when in the "off" state.
+             This should be a very SHORT string, as it appears in a small space. -->
+        <attr name="switchTextOff" format="string" />
+        <!-- The state (true for on, or false for off) that causes dependents to be disabled. By default,
+             dependents will be disabled when this is unchecked, so the value of this preference is false. -->
+        <attr name="disableDependentsState" />
     </declare-styleable>
 
 </resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index fc14a2a..bc98e84 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -156,6 +156,7 @@
 
     <!-- Regex array of allowable upstream ifaces for tethering - for example if you want
          tethering on a new interface called "foo2" add <item>"foo\\d"</item> to the array -->
+    <!-- Interfaces will be prioritized according to the order listed -->
     <string-array translatable="false" name="config_tether_upstream_regexs">
     </string-array>
 
@@ -378,6 +379,10 @@
     <!-- Diable lockscreen rotation by default -->
     <bool name="config_enableLockScreenRotation">false</bool>
 
+    <!-- Diable puk unlockscreen by default.
+         If unlock screen is disabled, the puk should be unlocked through Emergency Dialer -->
+    <bool name="config_enable_puk_unlock_screen">false</bool>
+
     <!-- Control the behavior when the user long presses the power button.
             0 - Nothing
             1 - Recent apps dialog
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 81f888d..4207d70 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -1282,6 +1282,7 @@
      Resources added in version 11 of the platform (Honeycomb / 3.0).
      =============================================================== -->
   <eat-comment />
+
   <public type="attr" name="allContactsName" id="0x010102cc" />
   <public type="attr" name="windowActionBar" id="0x010102cd" />
   <public type="attr" name="actionBarStyle" id="0x010102ce" />
@@ -1442,6 +1443,7 @@
   <public type="interpolator" name="accelerate_quad" id="0x010c0000" />
   <!-- Acceleration curve matching a quadtratic ease in function. -->
   <public type="interpolator" name="decelerate_quad" id="0x010c0001" />
+
   <!-- Acceleration curve matching a cubic ease out function. -->
   <public type="interpolator" name="accelerate_cubic" id="0x010c0002" />
   <!-- Acceleration curve matching a cubic ease in function. -->
@@ -1657,8 +1659,21 @@
   <public type="attr" name="compatibleWidthLimitDp" />
   <public type="attr" name="largestWidthLimitDp" />
 
+  <public type="style" name="Theme.Holo.Light.NoActionBar" />
+  <public type="style" name="Theme.Holo.Light.NoActionBar.Fullscreen" />
+
+  <public type="style" name="Widget.ActionBar.TabView" />
+  <public type="style" name="Widget.ActionBar.TabText" />
+  <public type="style" name="Widget.ActionBar.TabBar" />
+  <public type="style" name="Widget.Holo.ActionBar.TabView" />
+  <public type="style" name="Widget.Holo.ActionBar.TabText" />
+  <public type="style" name="Widget.Holo.ActionBar.TabBar" />
+  <public type="style" name="Widget.Holo.Light.ActionBar.TabView" />
+  <public type="style" name="Widget.Holo.Light.ActionBar.TabText" />
+  <public type="style" name="Widget.Holo.Light.ActionBar.TabBar" />
+
 <!-- ===============================================================
-     Resources added in version 13 of the platform (Ice Cream Sandwich)
+     Resources added in version 14 of the platform (Ice Cream Sandwich)
      =============================================================== -->
   <eat-comment />
   <public type="attr" name="state_hovered" />
@@ -1666,7 +1681,10 @@
   <public type="attr" name="state_drag_hovered" />
   <public type="attr" name="stopWithTask" />
 
-  <public type="style" name="Theme.Holo.Light.NoActionBar" />
+  <public type="attr" name="switchTextOn" />
+  <public type="attr" name="switchTextOff" />
+  <public type="attr" name="switchPreferenceStyle" />
+
   <public type="style" name="TextAppearance.SuggestionHighlight" />
   <public type="style" name="Theme.Holo.SplitActionBarWhenNarrow" />
   <public type="style" name="Theme.Holo.Light.SplitActionBarWhenNarrow" />
@@ -1675,7 +1693,8 @@
   <public type="attr" name="textEditSuggestionsBottomWindowLayout" />
   <public type="attr" name="textEditSuggestionsTopWindowLayout" />
   <public type="attr" name="textEditSuggestionItemLayout" />
-  <public type="attr" name="horizontalDirection" />
+
+  <public type="attr" name="layoutDirection" />
 
   <public type="attr" name="fullBackupAgent" />
   <public type="attr" name="suggestionsEnabled" />
@@ -1703,4 +1722,5 @@
   <public type="attr" name="notificationTimeout" />
   <public type="attr" name="accessibilityFlags" />
   <public type="attr" name="canRetrieveWindowContent" />
+
 </resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 8a4b74b..eb1a3ee 100755
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -87,6 +87,8 @@
     <string name="mismatchPin">The PINs you entered do not match.</string>
     <!-- Displayed when a SIM PIN password is too long or too short. -->
     <string name="invalidPin">Type a PIN that is 4 to 8 numbers.</string>
+    <!-- Displayed when a SIM PUK password is too short. -->
+    <string name="invalidPuk">Type a PUK that is 8 numbers or longer.</string>
     <!-- Displayed to prompt the user to type the PUK password to unlock
          the SIM card. -->
     <string name="needPuk">Your SIM card is PUK-locked. Type the PUK code to unlock it.</string>
@@ -457,6 +459,13 @@
       your messages or delete them without showing them to you.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_receiveEmergencyBroadcast">receive emergency broadcasts</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_receiveEmergencyBroadcast">Allows application to receive
+      and process emergency broadcast messages. This permission is only available
+      to system applications.</string>
+
+     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_sendSms">send SMS messages</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_sendSms">Allows application to send SMS
@@ -562,6 +571,13 @@
         never normally need.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_retrieve_window_content">retrieve screen content</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_retrieve_window_content">Allows application to retrieve
+        the content of the active window. Malicious applications may retrieve
+        the entire window content and examine all its text except passwords.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_shutdown">partial shutdown</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_shutdown">Puts the activity manager into a shutdown
@@ -886,6 +902,20 @@
         applications can use this to erase or modify your contact data.</string>
 
     <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_readProfile">read profile data</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_readProfile" product="default">Allows an application to read all
+        of your personal profile information. Malicious applications can use this to identify
+        you and send your personal information to other people.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permlab_writeProfile">write profile data</string>
+    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+    <string name="permdesc_writeProfile" product="default">Allows an application to modify
+        your personal profile information. Malicious applications can use this to erase or
+        modify your profile data.</string>
+
+    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permlab_readCalendar">read calendar events</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_readCalendar" product="tablet">Allows an application to read all
@@ -1323,7 +1353,7 @@
     <string name="permlab_vpn">intercept and modify all network traffic</string>
     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
     <string name="permdesc_vpn">Allows an application to intercept and
-      inspect all network traffic, for example to establish a VPN connection.
+      inspect all network traffic to establish a VPN connection.
       Malicious applications may monitor, redirect, or modify network packets
       without your knowledge.</string>
 
@@ -1664,6 +1694,15 @@
          Displayed in one line in a large font.  -->
     <string name="keyguard_password_enter_pin_code">Enter PIN code</string>
 
+    <!-- Instructions telling the user to enter their SIM PUK to unlock the keyguard.
+         Displayed in one line in a large font.  -->
+    <string name="keyguard_password_enter_puk_code">Enter PUK and new PIN code</string>
+
+    <!-- Prompt to enter SIM PUK in Edit Text Box in unlock screen -->
+    <string name="keyguard_password_enter_puk_prompt">PUK code</string>
+    <!-- Prompt to enter New SIM PIN in Edit Text Box in unlock screen -->
+    <string name="keyguard_password_enter_pin_prompt">New Pin Code</string>
+
     <!-- Displayed as hint in passwordEntry EditText on PasswordUnlockScreen [CHAR LIMIT=30]-->
     <string name="keyguard_password_entry_touch_hint"><font size="17">Touch to enter password</font></string>
 
@@ -1859,7 +1898,7 @@
 
     <!-- Do not translate.  WebView User Agent string -->
     <string name="web_user_agent" translatable="false">Mozilla/5.0 (Linux; U; <xliff:g id="x">Android %s</xliff:g>)
-        AppleWebKit/534.24 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.24</string>
+        AppleWebKit/534.27 (KHTML, like Gecko) Version/4.0 <xliff:g id="mobile">%s</xliff:g>Safari/534.27</string>
     <!-- Do not translate.  WebView User Agent targeted content -->
     <string name="web_user_agent_target_content" translatable="false">"Mobile "</string>
 
@@ -2718,6 +2757,13 @@
     <string name="l2tp_ipsec_psk_vpn_description">Pre-shared key based L2TP/IPSec VPN</string>
     <string name="l2tp_ipsec_crt_vpn_description">Certificate based L2TP/IPSec VPN</string>
 
+    <!-- Ticker text to show when VPN is active. -->
+    <string name="vpn_ticker">Activating <xliff:g id="app">%s</xliff:g> VPN...</string>
+    <!-- The title of the notification when VPN is active. -->
+    <string name="vpn_title"><xliff:g id="app">%s</xliff:g> VPN is active</string>
+    <!-- The text of the notification when VPN is active. -->
+    <string name="vpn_text">VPN is connected to <xliff:g id="profile">%s</xliff:g>. Tap to manage the network.</string>
+
     <!-- Localized strings for WebView -->
     <!-- Label for button in a WebView that will open a chooser to choose a file to upload -->
     <string name="upload_file">Choose file</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 0635528..b85d4cf 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -903,6 +903,12 @@
         <item name="android:widgetLayout">@android:layout/preference_widget_checkbox</item>
     </style>
 
+    <style name="Preference.SwitchPreference">
+        <item name="android:widgetLayout">@android:layout/preference_widget_switch</item>
+        <item name="android:switchTextOn">@android:string/capital_on</item>
+        <item name="android:switchTextOff">@android:string/capital_off</item>
+    </style>
+
     <style name="Preference.PreferenceScreen">
     </style>
 
@@ -947,6 +953,12 @@
         <item name="android:widgetLayout">@android:layout/preference_widget_checkbox</item>
     </style>
 
+    <style name="Preference.Holo.SwitchPreference">
+        <item name="android:widgetLayout">@android:layout/preference_widget_switch</item>
+        <item name="android:switchTextOn">@android:string/capital_on</item>
+        <item name="android:switchTextOff">@android:string/capital_off</item>
+    </style>
+
     <style name="Preference.Holo.PreferenceScreen">
     </style>
 
@@ -1120,17 +1132,17 @@
         <item name="android:src">?android:attr/actionModeCloseDrawable</item>
     </style>
 
-    <style name="Widget.ActionBarView_TabView">
+    <style name="Widget.ActionBar.TabView" parent="Widget">
         <item name="android:gravity">center_horizontal</item>
         <item name="android:background">@drawable/minitab_lt</item>
         <item name="android:paddingLeft">4dip</item>
         <item name="android:paddingRight">4dip</item>
     </style>
 
-    <style name="Widget.ActionBarView_TabBar">
+    <style name="Widget.ActionBar.TabBar" parent="Widget">
     </style>
 
-    <style name="Widget.ActionBarView_TabText">
+    <style name="Widget.ActionBar.TabText" parent="Widget">
         <item name="android:textAppearance">@style/TextAppearance.Widget.TextView.PopupMenu</item>
         <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
         <item name="android:textSize">18sp</item>
@@ -1785,22 +1797,22 @@
     <style name="Widget.Holo.ActionButton.TextButton" parent="Widget.Holo.ButtonBar.Button">
     </style>
 
-    <style name="Widget.Holo.ActionBarView_TabView" parent="Widget.ActionBarView_TabView">
+    <style name="Widget.Holo.ActionBar.TabView" parent="Widget.ActionBar.TabView">
         <item name="android:background">@drawable/tab_indicator_holo</item>
         <item name="android:paddingLeft">16dip</item>
         <item name="android:paddingRight">16dip</item>
     </style>
 
-    <style name="Widget.Holo.Tab" parent="Widget.Holo.ActionBarView_TabView">
+    <style name="Widget.Holo.Tab" parent="Widget.Holo.ActionBar.TabView">
     </style>
 
-    <style name="Widget.Holo.ActionBarView_TabBar" parent="Widget.ActionBarView_TabBar">
+    <style name="Widget.Holo.ActionBar.TabBar" parent="Widget.ActionBar.TabBar">
         <item name="android:divider">?android:attr/dividerVertical</item>
         <item name="android:showDividers">middle</item>
         <item name="android:dividerPadding">8dip</item>
     </style>
 
-    <style name="Widget.Holo.ActionBarView_TabText" parent="Widget.ActionBarView_TabText">
+    <style name="Widget.Holo.ActionBar.TabText" parent="Widget.ActionBar.TabText">
         <item name="android:textAppearance">@style/TextAppearance.Holo.Medium</item>
         <item name="android:textColor">?android:attr/textColorPrimary</item>
         <item name="android:textSize">18sp</item>
@@ -2119,16 +2131,16 @@
         <item name="android:contentDescription">@string/action_menu_overflow_description</item>
     </style>
 
-    <style name="Widget.Holo.Light.ActionBarView_TabView" parent="Widget.Holo.ActionBarView_TabView">
+    <style name="Widget.Holo.Light.ActionBar.TabView" parent="Widget.Holo.ActionBar.TabView">
     </style>
 
-    <style name="Widget.Holo.Light.Tab" parent="Widget.Holo.Light.ActionBarView_TabView">
+    <style name="Widget.Holo.Light.Tab" parent="Widget.Holo.Light.ActionBar.TabView">
     </style>
 
-    <style name="Widget.Holo.Light.ActionBarView_TabBar" parent="Widget.Holo.ActionBarView_TabBar">
+    <style name="Widget.Holo.Light.ActionBar.TabBar" parent="Widget.Holo.ActionBar.TabBar">
     </style>
 
-    <style name="Widget.Holo.Light.ActionBarView_TabText" parent="Widget.Holo.ActionBarView_TabText">
+    <style name="Widget.Holo.Light.ActionBar.TabText" parent="Widget.Holo.ActionBar.TabText">
     </style>
 
     <style name="Widget.Holo.Light.ActionMode" parent="Widget.Holo.ActionMode">
diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml
index 4f39da4..5f77dc5 100644
--- a/core/res/res/values/themes.xml
+++ b/core/res/res/values/themes.xml
@@ -247,6 +247,7 @@
         <item name="preferenceStyle">@android:style/Preference</item>
         <item name="preferenceInformationStyle">@android:style/Preference.Information</item>
         <item name="checkBoxPreferenceStyle">@android:style/Preference.CheckBoxPreference</item>
+        <item name="switchPreferenceStyle">@android:style/Preference.SwitchPreference</item>
         <item name="yesNoPreferenceStyle">@android:style/Preference.DialogPreference.YesNoPreference</item>
         <item name="dialogPreferenceStyle">@android:style/Preference.DialogPreference</item>
         <item name="editTextPreferenceStyle">@android:style/Preference.DialogPreference.EditTextPreference</item>
@@ -271,9 +272,9 @@
         <item name="actionModeShareDrawable">@android:drawable/ic_menu_share_holo_dark</item>
         <item name="actionModeFindDrawable">@android:drawable/ic_menu_find_holo_dark</item>
         <item name="actionModeWebSearchDrawable">@android:drawable/ic_menu_search</item>
-        <item name="actionBarTabStyle">@style/Widget.ActionBarView_TabView</item>
-        <item name="actionBarTabBarStyle">@style/Widget.ActionBarView_TabBar</item>
-        <item name="actionBarTabTextStyle">@style/Widget.ActionBarView_TabText</item>
+        <item name="actionBarTabStyle">@style/Widget.ActionBar.TabView</item>
+        <item name="actionBarTabBarStyle">@style/Widget.ActionBar.TabBar</item>
+        <item name="actionBarTabTextStyle">@style/Widget.ActionBar.TabText</item>
         <item name="actionModeStyle">@style/Widget.ActionMode</item>
         <item name="actionModeCloseButtonStyle">@style/Widget.ActionButton.CloseMode</item>
         <item name="actionBarStyle">@android:style/Widget.ActionBar</item>
@@ -687,7 +688,7 @@
         <item name="android:imeExtractEnterAnimation">@android:anim/input_method_extract_enter</item>
         <item name="android:imeExtractExitAnimation">@android:anim/input_method_extract_exit</item>
     </style>
-    
+
     <!-- Theme for the search input bar. -->
     <style name="Theme.SearchBar" parent="Theme.Holo.Light.Panel">
         <item name="windowContentOverlay">@null</item>        
@@ -1003,6 +1004,7 @@
         <item name="preferenceStyle">@android:style/Preference.Holo</item>
         <item name="preferenceInformationStyle">@android:style/Preference.Holo.Information</item>
         <item name="checkBoxPreferenceStyle">@android:style/Preference.Holo.CheckBoxPreference</item>
+        <item name="switchPreferenceStyle">@android:style/Preference.Holo.SwitchPreference</item>
         <item name="yesNoPreferenceStyle">@android:style/Preference.Holo.DialogPreference.YesNoPreference</item>
         <item name="dialogPreferenceStyle">@android:style/Preference.Holo.DialogPreference</item>
         <item name="editTextPreferenceStyle">@android:style/Preference.Holo.DialogPreference.EditTextPreference</item>
@@ -1019,9 +1021,9 @@
         <item name="actionOverflowButtonStyle">@android:style/Widget.Holo.ActionButton.Overflow</item>
         <item name="actionModeBackground">@android:drawable/cab_background_holo_dark</item>
         <item name="actionModeCloseDrawable">@android:drawable/cab_ic_close_holo</item>
-        <item name="actionBarTabStyle">@style/Widget.Holo.ActionBarView_TabView</item>
-        <item name="actionBarTabBarStyle">@style/Widget.Holo.ActionBarView_TabBar</item>
-        <item name="actionBarTabTextStyle">@style/Widget.Holo.ActionBarView_TabText</item>
+        <item name="actionBarTabStyle">@style/Widget.Holo.ActionBar.TabView</item>
+        <item name="actionBarTabBarStyle">@style/Widget.Holo.ActionBar.TabBar</item>
+        <item name="actionBarTabTextStyle">@style/Widget.Holo.ActionBar.TabText</item>
         <item name="actionModeStyle">@style/Widget.Holo.ActionMode</item>
         <item name="actionModeCloseButtonStyle">@style/Widget.Holo.ActionButton.CloseMode</item>
         <item name="actionBarStyle">@android:style/Widget.Holo.ActionBar</item>
@@ -1291,6 +1293,7 @@
         <item name="preferenceStyle">@android:style/Preference.Holo</item>
         <item name="preferenceInformationStyle">@android:style/Preference.Holo.Information</item>
         <item name="checkBoxPreferenceStyle">@android:style/Preference.Holo.CheckBoxPreference</item>
+        <item name="switchPreferenceStyle">@android:style/Preference.Holo.SwitchPreference</item>
         <item name="yesNoPreferenceStyle">@android:style/Preference.Holo.DialogPreference.YesNoPreference</item>
         <item name="dialogPreferenceStyle">@android:style/Preference.Holo.DialogPreference</item>
         <item name="editTextPreferenceStyle">@android:style/Preference.Holo.DialogPreference.EditTextPreference</item>
@@ -1307,9 +1310,9 @@
         <item name="actionOverflowButtonStyle">@android:style/Widget.Holo.Light.ActionButton.Overflow</item>
         <item name="actionModeBackground">@android:drawable/cab_background_holo_light</item>
         <item name="actionModeCloseDrawable">@android:drawable/cab_ic_close_holo</item>
-        <item name="actionBarTabStyle">@style/Widget.Holo.Light.ActionBarView_TabView</item>
-        <item name="actionBarTabBarStyle">@style/Widget.Holo.Light.ActionBarView_TabBar</item>
-        <item name="actionBarTabTextStyle">@style/Widget.Holo.Light.ActionBarView_TabText</item>
+        <item name="actionBarTabStyle">@style/Widget.Holo.Light.ActionBar.TabView</item>
+        <item name="actionBarTabBarStyle">@style/Widget.Holo.Light.ActionBar.TabBar</item>
+        <item name="actionBarTabTextStyle">@style/Widget.Holo.Light.ActionBar.TabText</item>
         <item name="actionModeStyle">@style/Widget.Holo.Light.ActionMode</item>
         <item name="actionModeCloseButtonStyle">@style/Widget.Holo.Light.ActionButton.CloseMode</item>
         <item name="actionBarStyle">@android:style/Widget.Holo.Light.ActionBar</item>
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index b02d904..00f47fa 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -12,7 +12,7 @@
 	$(call all-java-files-under, EnabledTestApp/src)
 
 LOCAL_DX_FLAGS := --core-library
-LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib
+LOCAL_STATIC_JAVA_LIBRARIES := core-tests android-common frameworks-core-util-lib mockwebserver
 LOCAL_JAVA_LIBRARIES := android.test.runner
 LOCAL_PACKAGE_NAME := FrameworksCoreTests
 
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 6084dd2..8e2d925 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -98,6 +98,9 @@
     <uses-permission android:name="android.permission.ASEC_RENAME" />
     <uses-permission android:name="android.permission.SHUTDOWN" />
 
+    <!-- accessibility test permissions -->
+    <uses-permission android:name="android.permission.RETRIEVE_WINDOW_CONTENT" />
+
     <application android:theme="@style/Theme">
         <uses-library android:name="android.test.runner" />
         <activity android:name="android.view.ViewAttachTestActivity" android:label="View Attach Test">
@@ -1225,6 +1228,13 @@
             </intent-filter>
         </activity>
 
+        <activity android:name="android.accessibilityservice.InterrogationActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+            </intent-filter>
+        </activity>
+
     </application>
 
     <instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/core/tests/coretests/res/layout/interrogation_activity.xml b/core/tests/coretests/res/layout/interrogation_activity.xml
new file mode 100644
index 0000000..28d965b
--- /dev/null
+++ b/core/tests/coretests/res/layout/interrogation_activity.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 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.
+*/
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/root"
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    >
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        >
+        <Button
+            android:id="@+id/button1"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button1"
+        />
+        <Button
+            android:id="@+id/button2"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button2"
+        />
+        <Button
+            android:id="@+id/button3"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button3"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        >
+        <Button
+            android:id="@+id/button4"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button4"
+        />
+        <Button
+            android:id="@+id/button5"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button5"
+        />
+        <Button
+            android:id="@+id/button6"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button6"
+        />
+    </LinearLayout>
+
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="fill_parent"
+        android:layout_height="fill_parent"
+        >
+        <Button
+            android:id="@+id/button7"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button7"
+        />
+        <Button
+            android:id="@+id/button8"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button8"
+        />
+        <Button
+            android:id="@+id/button9"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:text="@string/button9"
+        />
+    </LinearLayout>
+
+</LinearLayout>
diff --git a/core/tests/coretests/res/values/strings.xml b/core/tests/coretests/res/values/strings.xml
index 807386a..f51b08e 100644
--- a/core/tests/coretests/res/values/strings.xml
+++ b/core/tests/coretests/res/values/strings.xml
@@ -117,4 +117,16 @@
 
     <string name="searchable_label">SearchManager Test</string>
     <string name="searchable_hint">A search hint</string>
+
+    <!-- InterrogationActivity -->
+    <string name="button1">Button1</string>
+    <string name="button2">Button2</string>
+    <string name="button3">Button3</string>
+    <string name="button4">Button4</string>
+    <string name="button5">Button5</string>
+    <string name="button6">Button6</string>
+    <string name="button7">Button7</string>
+    <string name="button8">Button8</string>
+    <string name="button9">Button9</string>
+
 </resources>
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java
new file mode 100644
index 0000000..b4a0581
--- /dev/null
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivity.java
@@ -0,0 +1,47 @@
+/**
+ * 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.accessibilityservice;
+
+import com.android.frameworks.coretests.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.View;
+
+/**
+ * Activity for testing the accessibility APIs for "interrogation" of
+ * the screen content. These APIs allow exploring the screen and
+ * requesting an action to be performed on a given view from an
+ * AccessiiblityService.
+ */
+public class InterrogationActivity extends Activity {
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.interrogation_activity);
+
+        findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                /* do nothing */
+            }
+        });
+        findViewById(R.id.button5).setOnLongClickListener(new View.OnLongClickListener() {
+            public boolean onLongClick(View v) {
+                return true;
+            }
+        });
+    }
+}
diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
new file mode 100644
index 0000000..a20cc1f
--- /dev/null
+++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java
@@ -0,0 +1,464 @@
+/**
+ * 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.accessibilityservice;
+
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_FOCUS;
+import static android.view.accessibility.AccessibilityNodeInfo.ACTION_SELECT;
+
+import com.android.frameworks.coretests.R;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.provider.Settings;
+import android.test.ActivityInstrumentationTestCase2;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.Log;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityManager;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * Activity for testing the accessibility APIs for "interrogation" of
+ * the screen content. These APIs allow exploring the screen and
+ * requesting an action to be performed on a given view from an
+ * AccessiiblityService.
+ */
+public class InterrogationActivityTest
+        extends ActivityInstrumentationTestCase2<InterrogationActivity> {
+
+    // Timeout before give up wait for the system to process an accessibility setting change.
+    private static final int TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING = 2000;
+
+    // Helpers to figure out the first and last test methods
+    // This is a workaround for the lack of such support in JUnit3
+    private static int sTestMethodCount;
+    private static int sExecutedTestMethodCount;
+
+    // Handle to a connection to the AccessibilityManagerService
+    private static IAccessibilityServiceConnection sConnection;
+
+    // The last received accessibility event
+    private static volatile AccessibilityEvent sLastAccessibilityEvent;
+
+    public InterrogationActivityTest() {
+        super(InterrogationActivity.class);
+        sTestMethodCount = getTestMethodCount();
+    }
+
+    @LargeTest
+    public void testFindAccessibilityNodeInfoByViewId() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertNotNull(button);
+            assertEquals(0, button.getChildCount());
+
+            // bounds
+            Rect bounds = new Rect();
+            button.getBounds(bounds);
+            assertEquals(0, bounds.left);
+            assertEquals(0, bounds.top);
+            assertEquals(73, bounds.right);
+            assertEquals(48, bounds.bottom);
+
+            // char sequence attributes
+            assertEquals("com.android.frameworks.coretests", button.getPackageName());
+            assertEquals("android.widget.Button", button.getClassName());
+            assertEquals("Button5", button.getText());
+            assertNull(button.getContentDescription());
+
+            // boolean attributes
+            assertTrue(button.isFocusable());
+            assertTrue(button.isClickable());
+            assertTrue(button.isEnabled());
+            assertFalse(button.isFocused());
+            assertTrue(button.isClickable());
+            assertFalse(button.isPassword());
+            assertFalse(button.isSelected());
+            assertFalse(button.isCheckable());
+            assertFalse(button.isChecked());
+
+            // actions
+            assertEquals(ACTION_FOCUS | ACTION_SELECT | ACTION_CLEAR_SELECTION,
+                button.getActions());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testFindAccessibilityNodeInfoByViewText() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view by text
+            List<AccessibilityNodeInfo> buttons =
+                getConnection().findAccessibilityNodeInfosByViewText("butto");
+            assertEquals(9, buttons.size());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testTraverseAllViews() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // make list of expected nodes
+            List<String> classNameAndTextList = new ArrayList<String>();
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.LinearLayout");
+            classNameAndTextList.add("android.widget.ButtonButton1");
+            classNameAndTextList.add("android.widget.ButtonButton2");
+            classNameAndTextList.add("android.widget.ButtonButton3");
+            classNameAndTextList.add("android.widget.ButtonButton4");
+            classNameAndTextList.add("android.widget.ButtonButton5");
+            classNameAndTextList.add("android.widget.ButtonButton6");
+            classNameAndTextList.add("android.widget.ButtonButton7");
+            classNameAndTextList.add("android.widget.ButtonButton8");
+            classNameAndTextList.add("android.widget.ButtonButton9");
+
+            AccessibilityNodeInfo root =  getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.root);
+            assertNotNull("We must find the existing root.", root);
+
+            Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
+            fringe.add(root);
+
+            // do a BFS traversal and check nodes
+            while (!fringe.isEmpty()) {
+                AccessibilityNodeInfo current = fringe.poll();
+
+                CharSequence className = current.getClassName();
+                CharSequence text = current.getText();
+                String receivedClassNameAndText = className.toString()
+                   + ((text != null) ? text.toString() : "");
+                String expectedClassNameAndText = classNameAndTextList.remove(0);
+
+                assertEquals("Did not get the expected node info",
+                        expectedClassNameAndText, receivedClassNameAndText);
+
+                final int childCount = current.getChildCount();
+                for (int i = 0; i < childCount; i++) {
+                    AccessibilityNodeInfo child = current.getChild(i);
+                    fringe.add(child);
+                }
+            }
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testPerformAccessibilityActionFocus() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not focused
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isFocused());
+
+            // focus the view
+            assertTrue(button.performAction(ACTION_FOCUS));
+
+            // find the view again and make sure it is focused
+            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertTrue(button.isFocused());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testPerformAccessibilityActionClearFocus() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not focused
+            AccessibilityNodeInfo button =  getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isFocused());
+
+            // focus the view
+            assertTrue(button.performAction(ACTION_FOCUS));
+
+            // find the view again and make sure it is focused
+            button =  getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertTrue(button.isFocused());
+
+            // unfocus the view
+            assertTrue(button.performAction(ACTION_CLEAR_FOCUS));
+
+            // find the view again and make sure it is not focused
+            button =  getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertFalse(button.isFocused());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testPerformAccessibilityActionSelect() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not selected
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isSelected());
+
+            // select the view
+            assertTrue(button.performAction(ACTION_SELECT));
+
+            // find the view again and make sure it is selected
+            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertTrue(button.isSelected());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testPerformAccessibilityActionClearSelection() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not selected
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isSelected());
+
+            // select the view
+            assertTrue(button.performAction(ACTION_SELECT));
+
+            // find the view again and make sure it is selected
+            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertTrue(button.isSelected());
+
+            // unselect the view
+            assertTrue(button.performAction(ACTION_CLEAR_SELECTION));
+
+            // find the view again and make sure it is not selected
+            button = getConnection().findAccessibilityNodeInfoByViewId(R.id.button5);
+            assertFalse(button.isSelected());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @LargeTest
+    public void testAccessibilityEventGetSource() throws Exception {
+        beforeClassIfNeeded();
+        try {
+            // bring up the activity
+            getActivity();
+
+            // find a view and make sure it is not focused
+            AccessibilityNodeInfo button = getConnection().findAccessibilityNodeInfoByViewId(
+                    R.id.button5);
+            assertFalse(button.isSelected());
+
+            // focus the view
+            assertTrue(button.performAction(ACTION_FOCUS));
+            SystemClock.sleep(200);
+
+            // check that last event source
+            AccessibilityNodeInfo source = sLastAccessibilityEvent.getSource();
+            assertNotNull(source);
+
+            // bounds
+            Rect buttonBounds = new Rect();
+            button.getBounds(buttonBounds);
+            Rect sourceBounds = new Rect();
+            source.getBounds(sourceBounds);
+
+            assertEquals(buttonBounds.left, sourceBounds.left);
+            assertEquals(buttonBounds.right, sourceBounds.right);
+            assertEquals(buttonBounds.top, sourceBounds.top);
+            assertEquals(buttonBounds.bottom, sourceBounds.bottom);
+
+            // char sequence attributes
+            assertEquals(button.getPackageName(), source.getPackageName());
+            assertEquals(button.getClassName(), source.getClassName());
+            assertEquals(button.getText(), source.getText());
+            assertSame(button.getContentDescription(), source.getContentDescription());
+
+            // boolean attributes
+            assertSame(button.isFocusable(), source.isFocusable());
+            assertSame(button.isClickable(), source.isClickable());
+            assertSame(button.isEnabled(), source.isEnabled());
+            assertNotSame(button.isFocused(), source.isFocused());
+            assertSame(button.isLongClickable(), source.isLongClickable());
+            assertSame(button.isPassword(), source.isPassword());
+            assertSame(button.isSelected(), source.isSelected());
+            assertSame(button.isCheckable(), source.isCheckable());
+            assertSame(button.isChecked(), source.isChecked());
+        } finally {
+            afterClassIfNeeded();
+        }
+    }
+
+    @Override
+    protected void scrubClass(Class<?> testCaseClass) {
+        /* intentionally do not scrub */
+    }
+
+    /**
+     * Sets accessibility in a given state by writing the state to the
+     * settings and waiting until the accessibility manager service picks
+     * it up for max {@link #TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING}.
+     *
+     * @param state The accessibility state.
+     * @throws Exception If any error occurs.
+     */
+    private void ensureAccessibilityState(boolean state) throws Exception {
+        Context context = getInstrumentation().getContext();
+        // If the local manager ready => nothing to do.
+        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(context);
+        if (accessibilityManager.isEnabled() == state) {
+            return;
+        }
+        synchronized (this) {
+            // Check if the system already knows about the desired state. 
+            final boolean currentState = Settings.Secure.getInt(context.getContentResolver(),
+                    Settings.Secure.ACCESSIBILITY_ENABLED) == 1;
+            if (currentState != state) {
+                // Make sure we wake ourselves as the desired state is propagated.
+                accessibilityManager.addAccessibilityStateChangeListener(
+                        new AccessibilityManager.AccessibilityStateChangeListener() {
+                            public void onAccessibilityStateChanged(boolean enabled) {
+                                synchronized (this) {
+                                    notifyAll();
+                                }
+                            }
+                        });
+                Settings.Secure.putInt(context.getContentResolver(),
+                        Settings.Secure.ACCESSIBILITY_ENABLED, state ? 1 : 0);
+            }
+            // No while one attempt and that is it.
+            try {
+                wait(TIMEOUT_PROPAGATE_ACCESSIBLITY_SETTING);
+            } catch (InterruptedException ie) {
+                /* ignore */
+            }
+        }
+        if (accessibilityManager.isEnabled() != state) {
+            throw new IllegalStateException("Could not set accessibility state to: " + state);
+        }
+    }
+
+    /**
+     * Execute some set up code before any test method.
+     *
+     * NOTE: I miss Junit4's @BeforeClass
+     *
+     * @throws Exception If an error occurs.
+     */
+    private void beforeClassIfNeeded() throws Exception {
+        sExecutedTestMethodCount++;
+        if (sExecutedTestMethodCount == 1) {
+            ensureAccessibilityState(true);
+        }
+    }
+
+    /**
+     * Execute some clean up code after all test methods.
+     *
+     * NOTE: I miss Junit4's @AfterClass
+     *
+     * @throws Exception If an error occurs.
+     */
+    public void afterClassIfNeeded() throws Exception {
+        if (sExecutedTestMethodCount == sTestMethodCount) {
+            sExecutedTestMethodCount = 0;
+            ensureAccessibilityState(false);
+        }
+    }
+
+    private static IAccessibilityServiceConnection getConnection() throws Exception {
+        if (sConnection == null) {
+            IEventListener listener = new IEventListener.Stub() {
+                public void setConnection(IAccessibilityServiceConnection connection)
+                        throws RemoteException {
+                    AccessibilityServiceInfo info = new AccessibilityServiceInfo();
+                    info.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+                    info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
+                    info.notificationTimeout = 0;
+                    info.flags = AccessibilityServiceInfo.DEFAULT;
+                    connection.setServiceInfo(info);
+                }
+
+                public void onInterrupt() {}
+
+                public void onAccessibilityEvent(AccessibilityEvent event) {
+                    sLastAccessibilityEvent= AccessibilityEvent.obtain(event);
+                }
+            };
+            IAccessibilityManager manager = IAccessibilityManager.Stub.asInterface(
+                ServiceManager.getService(Context.ACCESSIBILITY_SERVICE));
+            sConnection = manager.registerEventListener(listener);
+        }
+        return sConnection;
+    }
+
+    /**
+     * @return The number of test methods.
+     */
+    private int getTestMethodCount() {
+        int testMethodCount = 0;
+        for (Method method : getClass().getMethods()) {
+            final int modifiers = method.getModifiers();
+            if (method.getName().startsWith("test")
+                    && (modifiers & Modifier.PUBLIC) != 0
+                    && (modifiers & Modifier.STATIC) == 0) {
+                testMethodCount++;
+            }
+        }
+        return testMethodCount;
+    }
+}
diff --git a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
index eb63c0d..0b72c3c 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsHistoryTest.java
@@ -16,8 +16,6 @@
 
 package android.net;
 
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.NetworkStatsHistory.UID_ALL;
 import static android.text.format.DateUtils.DAY_IN_MILLIS;
 import static android.text.format.DateUtils.HOUR_IN_MILLIS;
 import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
@@ -51,7 +49,7 @@
 
     public void testRecordSingleBucket() throws Exception {
         final long BUCKET_SIZE = HOUR_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
 
         // record data into narrow window to get single bucket
         stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 1024L, 2048L);
@@ -62,7 +60,7 @@
 
     public void testRecordEqualBuckets() throws Exception {
         final long bucketDuration = HOUR_IN_MILLIS;
-        stats = buildStats(bucketDuration);
+        stats = new NetworkStatsHistory(bucketDuration);
 
         // split equally across two buckets
         final long recordStart = TEST_START + (bucketDuration / 2);
@@ -75,7 +73,7 @@
 
     public void testRecordTouchingBuckets() throws Exception {
         final long BUCKET_SIZE = 15 * MINUTE_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
 
         // split almost completely into middle bucket, but with a few minutes
         // overlap into neighboring buckets. total record is 20 minutes.
@@ -94,7 +92,7 @@
 
     public void testRecordGapBuckets() throws Exception {
         final long BUCKET_SIZE = HOUR_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
 
         // record some data today and next week with large gap
         final long firstStart = TEST_START;
@@ -122,7 +120,7 @@
 
     public void testRecordOverlapBuckets() throws Exception {
         final long BUCKET_SIZE = HOUR_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
 
         // record some data in one bucket, and another overlapping buckets
         stats.recordData(TEST_START, TEST_START + SECOND_IN_MILLIS, 256L, 256L);
@@ -135,9 +133,80 @@
         assertBucket(stats, 1, 512L, 512L);
     }
 
+    public void testRecordEntireGapIdentical() throws Exception {
+        final long[] total = new long[2];
+
+        // first, create two separate histories far apart
+        final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+        stats1.recordData(TEST_START, TEST_START + 2 * HOUR_IN_MILLIS, 2000L, 1000L);
+
+        final long TEST_START_2 = TEST_START + DAY_IN_MILLIS;
+        final NetworkStatsHistory stats2 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+        stats2.recordData(TEST_START_2, TEST_START_2 + 2 * HOUR_IN_MILLIS, 1000L, 500L);
+
+        // combine together with identical bucket size
+        stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
+        stats.recordEntireHistory(stats1);
+        stats.recordEntireHistory(stats2);
+
+        // first verify that totals match up
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 3000L, 1500L);
+
+        // now inspect internal buckets
+        assertBucket(stats, 0, 1000L, 500L);
+        assertBucket(stats, 1, 1000L, 500L);
+        assertBucket(stats, 2, 500L, 250L);
+        assertBucket(stats, 3, 500L, 250L);
+    }
+
+    public void testRecordEntireOverlapVaryingBuckets() throws Exception {
+        final long[] total = new long[2];
+
+        // create history just over hour bucket boundary
+        final NetworkStatsHistory stats1 = new NetworkStatsHistory(HOUR_IN_MILLIS);
+        stats1.recordData(TEST_START, TEST_START + MINUTE_IN_MILLIS * 60, 600L, 600L);
+
+        final long TEST_START_2 = TEST_START + MINUTE_IN_MILLIS;
+        final NetworkStatsHistory stats2 = new NetworkStatsHistory(MINUTE_IN_MILLIS);
+        stats2.recordData(TEST_START_2, TEST_START_2 + MINUTE_IN_MILLIS * 5, 50L, 50L);
+
+        // combine together with minute bucket size
+        stats = new NetworkStatsHistory(MINUTE_IN_MILLIS);
+        stats.recordEntireHistory(stats1);
+        stats.recordEntireHistory(stats2);
+
+        // first verify that totals match up
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 650L, 650L);
+
+        // now inspect internal buckets
+        assertBucket(stats, 0, 10L, 10L);
+        assertBucket(stats, 1, 20L, 20L);
+        assertBucket(stats, 2, 20L, 20L);
+        assertBucket(stats, 3, 20L, 20L);
+        assertBucket(stats, 4, 20L, 20L);
+        assertBucket(stats, 5, 20L, 20L);
+        assertBucket(stats, 6, 10L, 10L);
+
+        // now combine using 15min buckets
+        stats = new NetworkStatsHistory(HOUR_IN_MILLIS / 4);
+        stats.recordEntireHistory(stats1);
+        stats.recordEntireHistory(stats2);
+
+        // first verify that totals match up
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 650L, 650L);
+
+        // and inspect buckets
+        assertBucket(stats, 0, 200L, 200L);
+        assertBucket(stats, 1, 150L, 150L);
+        assertBucket(stats, 2, 150L, 150L);
+        assertBucket(stats, 3, 150L, 150L);
+    }
+
     public void testRemove() throws Exception {
-        final long BUCKET_SIZE = HOUR_IN_MILLIS;
-        stats = buildStats(BUCKET_SIZE);
+        stats = new NetworkStatsHistory(HOUR_IN_MILLIS);
 
         // record some data across 24 buckets
         stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 24L, 24L);
@@ -165,13 +234,44 @@
         assertEquals(0, stats.bucketCount);
     }
 
+    public void testTotalData() throws Exception {
+        final long BUCKET_SIZE = HOUR_IN_MILLIS;
+        stats = new NetworkStatsHistory(BUCKET_SIZE);
+
+        // record uniform data across day
+        stats.recordData(TEST_START, TEST_START + DAY_IN_MILLIS, 2400L, 4800L);
+
+        final long[] total = new long[2];
+
+        // verify that total outside range is 0
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START - DAY_IN_MILLIS, total);
+        assertTotalEquals(total, 0, 0);
+
+        // verify total in first hour
+        stats.getTotalData(TEST_START, TEST_START + HOUR_IN_MILLIS, total);
+        assertTotalEquals(total, 100, 200);
+
+        // verify total across 1.5 hours
+        stats.getTotalData(TEST_START, TEST_START + (long) (1.5 * HOUR_IN_MILLIS), total);
+        assertTotalEquals(total, 150, 300);
+
+        // verify total beyond end
+        stats.getTotalData(TEST_START + (23 * HOUR_IN_MILLIS), TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 100, 200);
+
+        // verify everything total
+        stats.getTotalData(TEST_START - WEEK_IN_MILLIS, TEST_START + WEEK_IN_MILLIS, total);
+        assertTotalEquals(total, 2400, 4800);
+
+    }
+
     @Suppress
     public void testFuzzing() throws Exception {
         try {
             // fuzzing with random events, looking for crashes
             final Random r = new Random();
             for (int i = 0; i < 500; i++) {
-                stats = buildStats(r.nextLong());
+                stats = new NetworkStatsHistory(r.nextLong());
                 for (int j = 0; j < 10000; j++) {
                     if (r.nextBoolean()) {
                         // add range
@@ -191,10 +291,6 @@
         }
     }
 
-    private static NetworkStatsHistory buildStats(long bucketSize) {
-        return new NetworkStatsHistory(TYPE_MOBILE, null, UID_ALL, bucketSize);
-    }
-
     private static void assertConsistent(NetworkStatsHistory stats) {
         // verify timestamps are monotonic
         for (int i = 1; i < stats.bucketCount; i++) {
@@ -202,6 +298,11 @@
         }
     }
 
+    private static void assertTotalEquals(long[] total, long rx, long tx) {
+        assertEquals("unexpected rx", rx, total[0]);
+        assertEquals("unexpected tx", tx, total[1]);
+    }
+
     private static void assertBucket(NetworkStatsHistory stats, int index, long rx, long tx) {
         assertEquals("unexpected rx", rx, stats.rx[index]);
         assertEquals("unexpected tx", tx, stats.tx[index]);
diff --git a/core/tests/coretests/src/android/net/NetworkStatsTest.java b/core/tests/coretests/src/android/net/NetworkStatsTest.java
index 23eb9cf..8a3e871 100644
--- a/core/tests/coretests/src/android/net/NetworkStatsTest.java
+++ b/core/tests/coretests/src/android/net/NetworkStatsTest.java
@@ -47,7 +47,7 @@
                 .addEntry(TEST_IFACE, 100, 1024, 0)
                 .addEntry(TEST_IFACE, 101, 0, 1024).build();
 
-        final NetworkStats result = after.subtract(before, true);
+        final NetworkStats result = after.subtract(before);
 
         // identical data should result in zero delta
         assertEquals(0, result.rx[0]);
@@ -65,7 +65,7 @@
                 .addEntry(TEST_IFACE, 100, 1025, 2)
                 .addEntry(TEST_IFACE, 101, 3, 1028).build();
 
-        final NetworkStats result = after.subtract(before, true);
+        final NetworkStats result = after.subtract(before);
 
         // expect delta between measurements
         assertEquals(1, result.rx[0]);
@@ -84,7 +84,7 @@
                 .addEntry(TEST_IFACE, 101, 0, 1024)
                 .addEntry(TEST_IFACE, 102, 1024, 1024).build();
 
-        final NetworkStats result = after.subtract(before, true);
+        final NetworkStats result = after.subtract(before);
 
         // its okay to have new rows
         assertEquals(0, result.rx[0]);
diff --git a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
index ee89414..ee4ce95 100644
--- a/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
+++ b/core/tests/coretests/src/android/net/http/AbstractProxyTest.java
@@ -16,6 +16,10 @@
 
 package android.net.http;
 
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.RecordedRequest;
+import com.google.mockwebserver.SocketPolicy;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -33,10 +37,6 @@
 import org.apache.http.conn.scheme.Scheme;
 import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
 import org.apache.http.conn.ssl.SSLSocketFactory;
-import tests.http.MockResponse;
-import tests.http.MockWebServer;
-import tests.http.RecordedRequest;
-import tests.http.SocketPolicy;
 
 public abstract class AbstractProxyTest extends TestCase {
 
diff --git a/core/tests/coretests/src/android/net/http/CookiesTest.java b/core/tests/coretests/src/android/net/http/CookiesTest.java
index e736bc9..29e590f 100644
--- a/core/tests/coretests/src/android/net/http/CookiesTest.java
+++ b/core/tests/coretests/src/android/net/http/CookiesTest.java
@@ -16,6 +16,9 @@
 
 package android.net.http;
 
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.RecordedRequest;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.net.URISyntaxException;
@@ -30,9 +33,6 @@
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.conn.params.ConnRoutePNames;
 import org.apache.http.impl.client.DefaultHttpClient;
-import tests.http.MockResponse;
-import tests.http.MockWebServer;
-import tests.http.RecordedRequest;
 
 public final class CookiesTest extends TestCase {
 
diff --git a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
index ad3ec3d..da77298 100644
--- a/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
+++ b/core/tests/coretests/src/android/net/http/DefaultHttpClientTest.java
@@ -16,6 +16,12 @@
 
 package android.net.http;
 
+import com.google.mockwebserver.MockResponse;
+import com.google.mockwebserver.MockWebServer;
+import com.google.mockwebserver.SocketPolicy;
+import static com.google.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
+import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_INPUT_AT_END;
+import static com.google.mockwebserver.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -24,12 +30,6 @@
 import org.apache.http.HttpResponse;
 import org.apache.http.client.methods.HttpGet;
 import org.apache.http.impl.client.DefaultHttpClient;
-import tests.http.MockResponse;
-import tests.http.MockWebServer;
-import tests.http.SocketPolicy;
-import static tests.http.SocketPolicy.DISCONNECT_AT_END;
-import static tests.http.SocketPolicy.SHUTDOWN_INPUT_AT_END;
-import static tests.http.SocketPolicy.SHUTDOWN_OUTPUT_AT_END;
 
 public final class DefaultHttpClientTest extends TestCase {
 
diff --git a/core/tests/coretests/src/android/view/GravityTest.java b/core/tests/coretests/src/android/view/GravityTest.java
index 180a390..2a7a64f 100644
--- a/core/tests/coretests/src/android/view/GravityTest.java
+++ b/core/tests/coretests/src/android/view/GravityTest.java
@@ -59,11 +59,11 @@
         assertOneGravity(Gravity.CLIP_VERTICAL, Gravity.CLIP_VERTICAL, false);
         assertOneGravity(Gravity.CLIP_VERTICAL, Gravity.CLIP_VERTICAL, true);
 
-        assertOneGravity(Gravity.LEFT, Gravity.BEFORE, false);
-        assertOneGravity(Gravity.RIGHT, Gravity.BEFORE, true);
+        assertOneGravity(Gravity.LEFT, Gravity.START, false);
+        assertOneGravity(Gravity.RIGHT, Gravity.START, true);
 
-        assertOneGravity(Gravity.RIGHT, Gravity.AFTER, false);
-        assertOneGravity(Gravity.LEFT, Gravity.AFTER, true);
+        assertOneGravity(Gravity.RIGHT, Gravity.END, false);
+        assertOneGravity(Gravity.LEFT, Gravity.END, true);
     }
 
     private void assertOneGravity(int expected, int initial, boolean isRtl) {
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 5ed7966..b9c0d80 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -132,6 +132,7 @@
     <assign-permission name="android.permission.BATTERY_STATS" uid="shell" />
     <assign-permission name="android.permission.INTERNAL_SYSTEM_WINDOW" uid="shell" />
     <assign-permission name="android.permission.INJECT_EVENTS" uid="shell" />
+    <assign-permission name="android.permission.RETRIEVE_WINDOW_CONTENT" uid="shell" />
     <assign-permission name="android.permission.SET_ACTIVITY_WATCHER" uid="shell" />
     <assign-permission name="android.permission.READ_INPUT_STATE" uid="shell" />
     <assign-permission name="android.permission.SET_ORIENTATION" uid="shell" />
diff --git a/docs/html/guide/topics/graphics/renderscript.jd b/docs/html/guide/topics/graphics/renderscript.jd
index 60bffdb..180322f 100644
--- a/docs/html/guide/topics/graphics/renderscript.jd
+++ b/docs/html/guide/topics/graphics/renderscript.jd
@@ -630,7 +630,7 @@
     <dt><code>ScriptC_helloworld</code></dt>
 
     <dd>This class is generated by the Android build tools and is the reflected version of the
-    <code>helloworld.rs</code> Renderscript. It provides a a high level entry point into the
+    <code>helloworld.rs</code> Renderscript. It provides a high level entry point into the
     <code>helloworld.rs</code> native code by defining the corresponding methods that you can call
     from the traditional framework APIs.</dd>
 
diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java
index 9ac1a00..3c8aba3 100644
--- a/graphics/java/android/renderscript/Allocation.java
+++ b/graphics/java/android/renderscript/Allocation.java
@@ -100,7 +100,6 @@
     public static final int USAGE_GRAPHICS_CONSTANTS = 0x0008;
 
     /**
-     * @hide
      * USAGE_GRAPHICS_RENDER_TARGET The allcation will be used as a
      * target for offscreen rendering
      *
diff --git a/include/camera/CameraParameters.h b/include/camera/CameraParameters.h
index dc5fc84..83d4f6d 100644
--- a/include/camera/CameraParameters.h
+++ b/include/camera/CameraParameters.h
@@ -263,7 +263,7 @@
     // direction is not affected by the rotation or mirroring of
     // CAMERA_CMD_SET_DISPLAY_ORIENTATION. Coordinates 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.
+    // point. The width and height of focus areas cannot be 0 or negative.
     //
     // The fifth element is the weight. Values for weight must range from 1 to
     // 1000.  The weight should be interpreted as a per-pixel weight - all
@@ -373,7 +373,7 @@
     // direction is not affected by the rotation or mirroring of
     // CAMERA_CMD_SET_DISPLAY_ORIENTATION. 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
+    // is the lower right point. The width and height of metering areas cannot
     // be 0 or negative.
     //
     // The fifth element is the weight. Values for weight must range from 1 to
@@ -550,6 +550,7 @@
     static const char PIXEL_FORMAT_YUV422I[]; // YUY2
     static const char PIXEL_FORMAT_YUV420P[]; // YV12
     static const char PIXEL_FORMAT_RGB565[];
+    static const char PIXEL_FORMAT_RGBA8888[];
     static const char PIXEL_FORMAT_JPEG[];
 
     // Values for focus mode settings.
diff --git a/include/ui/Input.h b/include/ui/Input.h
index 3b5aba4..c9f694a 100644
--- a/include/ui/Input.h
+++ b/include/ui/Input.h
@@ -655,11 +655,6 @@
     // Oldest sample to consider when calculating the velocity.
     static const nsecs_t MAX_AGE = 200 * 1000000; // 200 ms
 
-    // When the total duration of the window of samples being averaged is less
-    // than the window size, the resulting velocity is scaled to reduce the impact
-    // of overestimation in short traces.
-    static const nsecs_t MIN_WINDOW = 100 * 1000000; // 100 ms
-
     // The minimum duration between samples when estimating velocity.
     static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms
 
diff --git a/keystore/java/android/security/Credentials.java b/keystore/java/android/security/Credentials.java
index 6b69b8a..fd6c22c 100644
--- a/keystore/java/android/security/Credentials.java
+++ b/keystore/java/android/security/Credentials.java
@@ -20,8 +20,19 @@
 import android.content.Context;
 import android.content.Intent;
 import android.util.Log;
-
+import com.android.org.bouncycastle.openssl.PEMReader;
+import com.android.org.bouncycastle.openssl.PEMWriter;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.nio.charset.Charsets;
 import java.security.KeyPair;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * {@hide}
@@ -60,6 +71,40 @@
     /** Data type for PKCS12. */
     public static final String PKCS12 = "PKCS12";
 
+    /**
+     * Convert objects to a PEM format, which is used for
+     * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY
+     * entries.
+     */
+    public static byte[] convertToPem(Object... objects) throws IOException {
+        ByteArrayOutputStream bao = new ByteArrayOutputStream();
+        Writer writer = new OutputStreamWriter(bao, Charsets.US_ASCII);
+        PEMWriter pw = new PEMWriter(writer);
+        for (Object o : objects) {
+            pw.writeObject(o);
+        }
+        pw.close();
+        return bao.toByteArray();
+    }
+    /**
+     * Convert objects from PEM format, which is used for
+     * CA_CERTIFICATE, USER_CERTIFICATE, and USER_PRIVATE_KEY
+     * entries.
+     */
+    public static List<Object> convertFromPem(byte[] bytes) throws IOException {
+        ByteArrayInputStream bai = new ByteArrayInputStream(bytes);
+        Reader reader = new InputStreamReader(bai, Charsets.US_ASCII);
+        PEMReader pr = new PEMReader(reader);
+
+        List<Object> result = new ArrayList<Object>();
+        Object o;
+        while ((o = pr.readObject()) != null) {
+            result.add(o);
+        }
+        pr.close();
+        return result;
+    }
+
     private static Credentials singleton;
 
     public static Credentials getInstance() {
diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java
index ec820cf..ba784ed 100644
--- a/keystore/java/android/security/KeyChain.java
+++ b/keystore/java/android/security/KeyChain.java
@@ -34,6 +34,7 @@
 import java.io.Closeable;
 import java.io.IOException;
 import java.security.KeyFactory;
+import java.security.KeyPair;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivateKey;
 import java.security.cert.Certificate;
@@ -185,11 +186,9 @@
             throw new IllegalArgumentException("bytes == null");
         }
         try {
-            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
-            return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(bytes));
-        } catch (NoSuchAlgorithmException e) {
-            throw new AssertionError(e);
-        } catch (InvalidKeySpecException e) {
+            KeyPair keyPair = (KeyPair) Credentials.convertFromPem(bytes).get(0);
+            return keyPair.getPrivate();
+        } catch (IOException e) {
             throw new AssertionError(e);
         }
     }
diff --git a/libs/camera/CameraParameters.cpp b/libs/camera/CameraParameters.cpp
index c295315..9392cf2 100644
--- a/libs/camera/CameraParameters.cpp
+++ b/libs/camera/CameraParameters.cpp
@@ -146,6 +146,7 @@
 const char CameraParameters::PIXEL_FORMAT_YUV422I[] = "yuv422i-yuyv";
 const char CameraParameters::PIXEL_FORMAT_YUV420P[]  = "yuv420p";
 const char CameraParameters::PIXEL_FORMAT_RGB565[] = "rgb565";
+const char CameraParameters::PIXEL_FORMAT_RGBA8888[] = "rgba8888";
 const char CameraParameters::PIXEL_FORMAT_JPEG[] = "jpeg";
 
 // Values for focus mode settings.
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index 9c9fae3..9fabf8d 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -149,24 +149,82 @@
 
 # Now build a host version for serialization
 include $(CLEAR_VARS)
+LOCAL_MODULE:= libRS
+LOCAL_MODULE_TAGS := optional
+
+intermediates := $(call intermediates-dir-for,STATIC_LIBRARIES,libRS,HOST,)
+
+# Generate custom headers
+
+GEN := $(addprefix $(intermediates)/, \
+            rsgApiStructs.h \
+            rsgApiFuncDecl.h \
+        )
+
+$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR) $< $@ <$(PRIVATE_PATH)/rs.spec
+$(GEN) : $(RSG_GENERATOR) $(LOCAL_PATH)/rs.spec
+$(GEN): $(intermediates)/%.h : $(LOCAL_PATH)/%.h.rsg
+	$(transform-generated-source)
+
+LOCAL_GENERATED_SOURCES += $(GEN)
+
+# Generate custom source files
+
+GEN := $(addprefix $(intermediates)/, \
+            rsgApi.cpp \
+            rsgApiReplay.cpp \
+        )
+
+$(GEN) : PRIVATE_PATH := $(LOCAL_PATH)
+$(GEN) : PRIVATE_CUSTOM_TOOL = $(RSG_GENERATOR) $< $@ <$(PRIVATE_PATH)/rs.spec
+$(GEN) : $(RSG_GENERATOR) $(LOCAL_PATH)/rs.spec
+$(GEN): $(intermediates)/%.cpp : $(LOCAL_PATH)/%.cpp.rsg
+	$(transform-generated-source)
+
+LOCAL_GENERATED_SOURCES += $(GEN)
+
 LOCAL_CFLAGS += -Werror -Wall -Wno-unused-parameter -Wno-unused-variable
 LOCAL_CFLAGS += -DANDROID_RS_SERIALIZE
+LOCAL_CFLAGS += -fPIC
 
 LOCAL_SRC_FILES:= \
+	rsAdapter.cpp \
 	rsAllocation.cpp \
+	rsAnimation.cpp \
 	rsComponent.cpp \
+	rsContext.cpp \
+	rsDevice.cpp \
 	rsElement.cpp \
+	rsFBOCache.cpp \
+	rsFifoSocket.cpp \
 	rsFileA3D.cpp \
+	rsFont.cpp \
+	rsLocklessFifo.cpp \
 	rsObjectBase.cpp \
+	rsMatrix2x2.cpp \
+	rsMatrix3x3.cpp \
+	rsMatrix4x4.cpp \
 	rsMesh.cpp \
+	rsMutex.cpp \
+	rsProgram.cpp \
+	rsProgramFragment.cpp \
+	rsProgramStore.cpp \
+	rsProgramRaster.cpp \
+	rsProgramVertex.cpp \
+	rsSampler.cpp \
+	rsScript.cpp \
+	rsScriptC.cpp \
+	rsScriptC_Lib.cpp \
+	rsScriptC_LibGL.cpp \
+	rsSignal.cpp \
 	rsStream.cpp \
+	rsThreadIO.cpp \
 	rsType.cpp
 
 LOCAL_STATIC_LIBRARIES := libcutils libutils
 
 LOCAL_LDLIBS := -lpthread
-LOCAL_MODULE:= libRSserialize
-LOCAL_MODULE_TAGS := optional
 
 include $(BUILD_HOST_STATIC_LIBRARY)
 
diff --git a/libs/rs/RenderScript.h b/libs/rs/RenderScript.h
index 3f2d67a..535f713 100644
--- a/libs/rs/RenderScript.h
+++ b/libs/rs/RenderScript.h
@@ -55,15 +55,7 @@
 RsContext rsContextCreate(RsDevice dev, uint32_t version);
 RsContext rsContextCreateGL(RsDevice dev, uint32_t version, RsSurfaceConfig sc, uint32_t dpi);
 
-
-
-#ifdef ANDROID_RS_SERIALIZE
-#define NO_RS_FUNCS
-#endif
-
-#ifndef NO_RS_FUNCS
 #include "rsgApiFuncDecl.h"
-#endif
 
 #ifdef __cplusplus
 };
diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp
index 94d55a6..01cc369 100644
--- a/libs/rs/driver/rsdCore.cpp
+++ b/libs/rs/driver/rsdCore.cpp
@@ -36,6 +36,7 @@
 #include <cutils/sched_policy.h>
 #include <sys/syscall.h>
 #include <string.h>
+#include <bcc/bcc.h>
 
 using namespace android;
 using namespace android::renderscript;
diff --git a/libs/rs/driver/rsdCore.h b/libs/rs/driver/rsdCore.h
index 422bb1b..f393b60 100644
--- a/libs/rs/driver/rsdCore.h
+++ b/libs/rs/driver/rsdCore.h
@@ -18,7 +18,6 @@
 #define RSD_CORE_H
 
 #include <rs_hal.h>
-#include <bcc/bcc.h>
 
 #include "rsMutex.h"
 #include "rsSignal.h"
diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp
index 05412c7..bff3660 100644
--- a/libs/rs/rsAllocation.cpp
+++ b/libs/rs/rsAllocation.cpp
@@ -168,13 +168,10 @@
 }
 
 void Allocation::addProgramToDirty(const Program *p) {
-#ifndef ANDROID_RS_SERIALIZE
     mToDirtyList.push(p);
-#endif //ANDROID_RS_SERIALIZE
 }
 
 void Allocation::removeProgramToDirty(const Program *p) {
-#ifndef ANDROID_RS_SERIALIZE
     for (size_t ct=0; ct < mToDirtyList.size(); ct++) {
         if (mToDirtyList[ct] == p) {
             mToDirtyList.removeAt(ct);
@@ -182,7 +179,6 @@
         }
     }
     rsAssert(0);
-#endif //ANDROID_RS_SERIALIZE
 }
 
 void Allocation::dumpLOGV(const char *prefix) const {
@@ -254,11 +250,9 @@
 }
 
 void Allocation::sendDirty(const Context *rsc) const {
-#ifndef ANDROID_RS_SERIALIZE
     for (size_t ct=0; ct < mToDirtyList.size(); ct++) {
         mToDirtyList[ct]->forceDirty();
     }
-#endif //ANDROID_RS_SERIALIZE
     mRSC->mHal.funcs.allocation.markDirty(rsc, this);
 }
 
@@ -312,8 +306,6 @@
 
 /////////////////
 //
-#ifndef ANDROID_RS_SERIALIZE
-
 
 namespace android {
 namespace renderscript {
@@ -413,25 +405,25 @@
 }
 
 void rsi_Allocation1DData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t lod,
-                          uint32_t count, const void *data, uint32_t sizeBytes) {
+                          uint32_t count, const void *data, size_t sizeBytes) {
     Allocation *a = static_cast<Allocation *>(va);
     a->data(rsc, xoff, lod, count, data, sizeBytes);
 }
 
 void rsi_Allocation2DElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t y, uint32_t lod, RsAllocationCubemapFace face,
-                                 const void *data, uint32_t eoff, uint32_t sizeBytes) {
+                                 const void *data, size_t eoff, uint32_t sizeBytes) { // TODO: this seems wrong, eoff and sizeBytes may be swapped
     Allocation *a = static_cast<Allocation *>(va);
     a->elementData(rsc, x, y, data, eoff, sizeBytes);
 }
 
 void rsi_Allocation1DElementData(Context *rsc, RsAllocation va, uint32_t x, uint32_t lod,
-                                 const void *data, uint32_t eoff, uint32_t sizeBytes) {
+                                 const void *data, size_t eoff, uint32_t sizeBytes) { // TODO: this seems wrong, eoff and sizeBytes may be swapped
     Allocation *a = static_cast<Allocation *>(va);
     a->elementData(rsc, x, data, eoff, sizeBytes);
 }
 
 void rsi_Allocation2DData(Context *rsc, RsAllocation va, uint32_t xoff, uint32_t yoff, uint32_t lod, RsAllocationCubemapFace face,
-                          uint32_t w, uint32_t h, const void *data, uint32_t sizeBytes) {
+                          uint32_t w, uint32_t h, const void *data, size_t sizeBytes) {
     Allocation *a = static_cast<Allocation *>(va);
     a->data(rsc, xoff, yoff, lod, face, w, h, data, sizeBytes);
 }
@@ -549,5 +541,3 @@
 
     return a->getType();
 }
-
-#endif //ANDROID_RS_SERIALIZE
diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h
index 5cf6292..f538dd1 100644
--- a/libs/rs/rsAllocation.h
+++ b/libs/rs/rsAllocation.h
@@ -59,7 +59,6 @@
 
     static Allocation * createAllocation(Context *rsc, const Type *, uint32_t usages,
                                   RsAllocationMipmapControl mc = RS_ALLOCATION_MIPMAP_NONE);
-
     virtual ~Allocation();
     void updateCache();
 
@@ -121,14 +120,6 @@
 
 private:
     Allocation(Context *rsc, const Type *, uint32_t usages, RsAllocationMipmapControl mc);
-
-    void upload2DTexture(bool isFirstUpload);
-    void update2DTexture(const void *ptr, uint32_t xoff, uint32_t yoff,
-                         uint32_t lod, RsAllocationCubemapFace face, uint32_t w, uint32_t h);
-
-    void allocScriptMemory();
-    void freeScriptMemory();
-
 };
 
 }
diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp
index bab5c58..44e9d89 100644
--- a/libs/rs/rsContext.cpp
+++ b/libs/rs/rsContext.cpp
@@ -92,9 +92,13 @@
 }
 
 uint64_t Context::getTime() const {
+#ifndef ANDROID_RS_SERIALIZE
     struct timespec t;
     clock_gettime(CLOCK_MONOTONIC, &t);
     return t.tv_nsec + ((uint64_t)t.tv_sec * 1000 * 1000 * 1000);
+#else
+    return 0;
+#endif //ANDROID_RS_SERIALIZE
 }
 
 void Context::timerReset() {
@@ -200,11 +204,11 @@
 
 void * Context::threadProc(void *vrsc) {
     Context *rsc = static_cast<Context *>(vrsc);
+#ifndef ANDROID_RS_SERIALIZE
     rsc->mNativeThreadId = gettid();
-
     setpriority(PRIO_PROCESS, rsc->mNativeThreadId, ANDROID_PRIORITY_DISPLAY);
     rsc->mThreadPriority = ANDROID_PRIORITY_DISPLAY;
-
+#endif //ANDROID_RS_SERIALIZE
     rsc->props.mLogTimes = getProp("debug.rs.profile");
     rsc->props.mLogScripts = getProp("debug.rs.script");
     rsc->props.mLogObjects = getProp("debug.rs.object");
@@ -330,10 +334,16 @@
     mObjHead = NULL;
     mError = RS_ERROR_NONE;
     mDPI = 96;
+    mIsContextLite = false;
 }
 
 Context * Context::createContext(Device *dev, const RsSurfaceConfig *sc) {
     Context * rsc = new Context();
+
+    // Temporary to avoid breaking the tools
+    if (!dev) {
+        return rsc;
+    }
     if (!rsc->initContext(dev, sc)) {
         delete rsc;
         return NULL;
@@ -341,6 +351,12 @@
     return rsc;
 }
 
+Context * Context::createContextLite() {
+    Context * rsc = new Context();
+    rsc->mIsContextLite = true;
+    return rsc;
+}
+
 bool Context::initContext(Device *dev, const RsSurfaceConfig *sc) {
     pthread_mutex_lock(&gInitMutex);
 
@@ -395,26 +411,28 @@
 Context::~Context() {
     LOGV("Context::~Context");
 
-    mIO.coreFlush();
-    rsAssert(mExit);
-    mExit = true;
-    mPaused = false;
-    void *res;
+    if (!mIsContextLite) {
+        mIO.coreFlush();
+        rsAssert(mExit);
+        mExit = true;
+        mPaused = false;
+        void *res;
 
-    mIO.shutdown();
-    int status = pthread_join(mThreadId, &res);
+        mIO.shutdown();
+        int status = pthread_join(mThreadId, &res);
 
-    if (mHal.funcs.shutdownDriver) {
-        mHal.funcs.shutdownDriver(this);
+        if (mHal.funcs.shutdownDriver) {
+            mHal.funcs.shutdownDriver(this);
+        }
+
+        // Global structure cleanup.
+        pthread_mutex_lock(&gInitMutex);
+        if (mDev) {
+            mDev->removeContext(this);
+            mDev = NULL;
+        }
+        pthread_mutex_unlock(&gInitMutex);
     }
-
-    // Global structure cleanup.
-    pthread_mutex_lock(&gInitMutex);
-    if (mDev) {
-        mDev->removeContext(this);
-        mDev = NULL;
-    }
-    pthread_mutex_unlock(&gInitMutex);
     LOGV("Context::~Context done");
 }
 
@@ -542,7 +560,7 @@
 
     LOGE(" RS width %i, height %i", mWidth, mHeight);
     LOGE(" RS running %i, exit %i, paused %i", mRunning, mExit, mPaused);
-    LOGE(" RS pThreadID %li, nativeThreadID %i", mThreadId, mNativeThreadId);
+    LOGE(" RS pThreadID %li, nativeThreadID %i", (long int)mThreadId, mNativeThreadId);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////
@@ -595,7 +613,7 @@
     rsc->setFont(font);
 }
 
-void rsi_AssignName(Context *rsc, RsObjectBase obj, const char *name, uint32_t name_length) {
+void rsi_AssignName(Context *rsc, RsObjectBase obj, const char *name, size_t name_length) {
     ObjectBase *ob = static_cast<ObjectBase *>(obj);
     rsc->assignName(ob, name, name_length);
 }
@@ -627,7 +645,7 @@
 }
 
 void rsi_ContextDestroyWorker(Context *rsc) {
-    rsc->destroyWorkerThreadResources();;
+    rsc->destroyWorkerThreadResources();
 }
 
 void rsi_ContextDestroy(Context *rsc) {
diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h
index 4ba00fe..309fe95 100644
--- a/libs/rs/rsContext.h
+++ b/libs/rs/rsContext.h
@@ -24,7 +24,6 @@
 
 #include "rs_hal.h"
 
-#ifndef ANDROID_RS_SERIALIZE
 #include "rsMutex.h"
 #include "rsThreadIO.h"
 #include "rsMatrix4x4.h"
@@ -42,8 +41,6 @@
 #include "rsgApiStructs.h"
 #include "rsLocklessFifo.h"
 
-#endif // ANDROID_RS_SERIALIZE
-
 // ---------------------------------------------------------------------------
 namespace android {
 
@@ -67,8 +64,6 @@
 #define CHECK_OBJ_OR_NULL(o)
 #endif
 
-#ifndef ANDROID_RS_SERIALIZE
-
 class Context {
 public:
     struct Hal {
@@ -79,6 +74,7 @@
     Hal mHal;
 
     static Context * createContext(Device *, const RsSurfaceConfig *sc);
+    static Context * createContextLite();
     ~Context();
 
     static pthread_mutex_t gInitMutex;
@@ -243,6 +239,7 @@
     static void * helperThreadProc(void *);
 
     bool mHasSurface;
+    bool mIsContextLite;
 
     Vector<ObjectBase *> mNames;
 
@@ -259,46 +256,6 @@
     uint32_t mAverageFPS;
 };
 
-#else
-
-class Context {
-public:
-    Context() {
-        mObjHead = NULL;
-    }
-    ~Context() {
-        ObjectBase::zeroAllUserRef(this);
-    }
-
-    struct Hal {
-        void * drv;
-
-        RsdHalFunctions funcs;
-    };
-    Hal mHal;
-
-    ElementState mStateElement;
-    TypeState mStateType;
-
-    struct {
-        bool mLogTimes;
-        bool mLogScripts;
-        bool mLogObjects;
-        bool mLogShaders;
-        bool mLogShadersAttr;
-        bool mLogShadersUniforms;
-        bool mLogVisual;
-    } props;
-
-    void setError(RsError e, const char *msg = NULL) {  }
-
-    mutable const ObjectBase * mObjHead;
-
-protected:
-
-};
-#endif //ANDROID_RS_SERIALIZE
-
 } // renderscript
 } // android
 #endif
diff --git a/libs/rs/rsFifoSocket.cpp b/libs/rs/rsFifoSocket.cpp
index 848bba5..8b8008d 100644
--- a/libs/rs/rsFifoSocket.cpp
+++ b/libs/rs/rsFifoSocket.cpp
@@ -70,9 +70,9 @@
 }
 
 void FifoSocket::readReturn(const void *data, size_t bytes) {
-    LOGE("readReturn %p %i", data, bytes);
+    LOGE("readReturn %p %Zu", data, bytes);
     size_t ret = ::send(sv[1], data, bytes, 0);
-    LOGE("readReturn %i", ret);
+    LOGE("readReturn %Zu", ret);
     rsAssert(ret == bytes);
 }
 
diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp
index b625504..ce674f42 100644
--- a/libs/rs/rsFont.cpp
+++ b/libs/rs/rsFont.cpp
@@ -21,9 +21,11 @@
 #include "rsProgramFragment.h"
 #include <cutils/properties.h>
 
+#ifndef ANDROID_RS_SERIALIZE
 #include <ft2build.h>
 #include FT_FREETYPE_H
 #include FT_BITMAP_H
+#endif //ANDROID_RS_SERIALIZE
 
 using namespace android;
 using namespace android::renderscript;
@@ -35,6 +37,7 @@
 }
 
 bool Font::init(const char *name, float fontSize, uint32_t dpi, const void *data, uint32_t dataLen) {
+#ifndef ANDROID_RS_SERIALIZE
     if (mInitialized) {
         LOGE("Reinitialization of fonts not supported");
         return false;
@@ -65,6 +68,7 @@
     mHasKerning = FT_HAS_KERNING(mFace);
 
     mInitialized = true;
+#endif //ANDROID_RS_SERIALIZE
     return true;
 }
 
@@ -230,6 +234,7 @@
 }
 
 void Font::updateGlyphCache(CachedGlyphInfo *glyph) {
+#ifndef ANDROID_RS_SERIALIZE
     FT_Error error = FT_Load_Glyph( mFace, glyph->mGlyphIndex, FT_LOAD_RENDER );
     if (error) {
         LOGE("Couldn't load glyph.");
@@ -270,15 +275,16 @@
     glyph->mBitmapMinV = (float)startY / (float)cacheHeight;
     glyph->mBitmapMaxU = (float)endX / (float)cacheWidth;
     glyph->mBitmapMaxV = (float)endY / (float)cacheHeight;
+#endif //ANDROID_RS_SERIALIZE
 }
 
 Font::CachedGlyphInfo *Font::cacheGlyph(uint32_t glyph) {
     CachedGlyphInfo *newGlyph = new CachedGlyphInfo();
     mCachedGlyphs.add(glyph, newGlyph);
-
+#ifndef ANDROID_RS_SERIALIZE
     newGlyph->mGlyphIndex = FT_Get_Char_Index(mFace, glyph);
     newGlyph->mIsValid = false;
-
+#endif //ANDROID_RS_SERIALIZE
     updateGlyphCache(newGlyph);
 
     return newGlyph;
@@ -309,9 +315,11 @@
 }
 
 Font::~Font() {
+#ifndef ANDROID_RS_SERIALIZE
     if (mFace) {
         FT_Done_Face(mFace);
     }
+#endif
 
     for (uint32_t i = 0; i < mCachedGlyphs.size(); i ++) {
         CachedGlyphInfo *glyph = mCachedGlyphs.valueAt(i);
@@ -324,7 +332,9 @@
     mMaxNumberOfQuads = 1024;
     mCurrentQuadIndex = 0;
     mRSC = NULL;
+#ifndef ANDROID_RS_SERIALIZE
     mLibrary = NULL;
+#endif //ANDROID_RS_SERIALIZE
 
     // Get the renderer properties
     char property[PROPERTY_VALUE_MAX];
@@ -363,7 +373,7 @@
 
     rsAssert(!mActiveFonts.size());
 }
-
+#ifndef ANDROID_RS_SERIALIZE
 FT_Library FontState::getLib() {
     if (!mLibrary) {
         FT_Error error = FT_Init_FreeType(&mLibrary);
@@ -375,6 +385,8 @@
 
     return mLibrary;
 }
+#endif //ANDROID_RS_SERIALIZE
+
 
 void FontState::init(Context *rsc) {
     mRSC = rsc;
@@ -393,6 +405,7 @@
     }
 }
 
+#ifndef ANDROID_RS_SERIALIZE
 bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
     // If the glyph is too tall, don't cache it
     if ((uint32_t)bitmap->rows > mCacheLines[mCacheLines.size()-1]->mMaxHeight) {
@@ -466,6 +479,7 @@
 
     return true;
 }
+#endif //ANDROID_RS_SERIALIZE
 
 void FontState::initRenderState() {
     String8 shaderString("varying vec2 varTex0;\n");
@@ -791,13 +805,15 @@
     mCacheLines.clear();
 
     mDefault.clear();
-
+#ifndef ANDROID_RS_SERIALIZE
     if (mLibrary) {
         FT_Done_FreeType( mLibrary );
         mLibrary = NULL;
     }
+#endif //ANDROID_RS_SERIALIZE
 }
 
+#ifndef ANDROID_RS_SERIALIZE
 bool FontState::CacheTextureLine::fitBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY) {
     if ((uint32_t)bitmap->rows > mMaxHeight) {
         return false;
@@ -813,6 +829,7 @@
 
     return false;
 }
+#endif //ANDROID_RS_SERIALIZE
 
 namespace android {
 namespace renderscript {
diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h
index d18c0d9..b0e1430 100644
--- a/libs/rs/rsFont.h
+++ b/libs/rs/rsFont.h
@@ -199,8 +199,10 @@
     float mWhiteThreshold;
 
     // Free type library, we only need one copy
+#ifndef ANDROID_RS_SERIALIZE
     FT_LibraryRec_ *mLibrary;
     FT_LibraryRec_ *getLib();
+#endif //ANDROID_RS_SERIALIZE
     Vector<Font*> mActiveFonts;
 
     // Render state for the font
@@ -217,7 +219,9 @@
         return (uint8_t*)mTextTexture->getPtr();
     }
 
+#ifndef ANDROID_RS_SERIALIZE
     bool cacheBitmap(FT_Bitmap_ *bitmap, uint32_t *retOriginX, uint32_t *retOriginY);
+#endif //ANDROID_RS_SERIALIZE
     const Type* getCacheTextureType() {
         return mTextTexture->getType();
     }
diff --git a/libs/rs/rsMesh.cpp b/libs/rs/rsMesh.cpp
index 62e388c..359d09f 100644
--- a/libs/rs/rsMesh.cpp
+++ b/libs/rs/rsMesh.cpp
@@ -161,8 +161,6 @@
     return mesh;
 }
 
-#ifndef ANDROID_RS_SERIALIZE
-
 void Mesh::render(Context *rsc) const {
     for (uint32_t ct = 0; ct < mHal.state.primitivesCount; ct ++) {
         renderPrimitive(rsc, ct);
@@ -255,9 +253,9 @@
 namespace renderscript {
 
 RsMesh rsi_MeshCreate(Context *rsc,
-                      RsAllocation *vtx, uint32_t vtxCount,
-                      RsAllocation *idx, uint32_t idxCount,
-                      uint32_t *primType, uint32_t primTypeCount) {
+                      RsAllocation * vtx, size_t vtxCount,
+                      RsAllocation * idx, size_t idxCount,
+                      uint32_t * primType, size_t primTypeCount) {
     rsAssert(idxCount == primTypeCount);
     Mesh *sm = new Mesh(rsc, vtxCount, idxCount);
     sm->incUserRef();
@@ -309,5 +307,3 @@
         }
     }
 }
-
-#endif
diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp
index 0823d82..356ff77 100644
--- a/libs/rs/rsProgramFragment.cpp
+++ b/libs/rs/rsProgramFragment.cpp
@@ -127,8 +127,8 @@
 namespace renderscript {
 
 RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc, const char * shaderText,
-                             uint32_t shaderLength, const uint32_t * params,
-                             uint32_t paramLength) {
+                             size_t shaderLength, const uint32_t * params,
+                             size_t paramLength) {
     ProgramFragment *pf = new ProgramFragment(rsc, shaderText, shaderLength, params, paramLength);
     pf->incUserRef();
     //LOGE("rsi_ProgramFragmentCreate %p", pf);
diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp
index e6790cb..058a456 100644
--- a/libs/rs/rsProgramVertex.cpp
+++ b/libs/rs/rsProgramVertex.cpp
@@ -223,8 +223,8 @@
 namespace renderscript {
 
 RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, const char * shaderText,
-                             uint32_t shaderLength, const uint32_t * params,
-                             uint32_t paramLength) {
+                             size_t shaderLength, const uint32_t * params,
+                             size_t paramLength) {
     ProgramVertex *pv = new ProgramVertex(rsc, shaderText, shaderLength, params, paramLength);
     pv->incUserRef();
     return pv;
diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp
index 7641cab..f62c72e 100644
--- a/libs/rs/rsScript.cpp
+++ b/libs/rs/rsScript.cpp
@@ -53,7 +53,7 @@
     }
 }
 
-void Script::setVar(uint32_t slot, const void *val, uint32_t len) {
+void Script::setVar(uint32_t slot, const void *val, size_t len) {
     //LOGE("setVar %i %p %i", slot, val, len);
     if (slot >= mHal.info.exportedVariableCount) {
         LOGE("Script::setVar unable to set allocation, invalid slot index");
@@ -82,14 +82,14 @@
     //LOGE("rsi_ScriptBindAllocation %i  %p  %p", slot, a, a->getPtr());
 }
 
-void rsi_ScriptSetTimeZone(Context * rsc, RsScript vs, const char * timeZone, uint32_t length) {
+void rsi_ScriptSetTimeZone(Context * rsc, RsScript vs, const char * timeZone, size_t length) {
     Script *s = static_cast<Script *>(vs);
     s->mEnviroment.mTimeZone = timeZone;
 }
 
 void rsi_ScriptForEach(Context *rsc, RsScript vs, uint32_t slot,
                        RsAllocation vain, RsAllocation vaout,
-                       const void *params, uint32_t paramLen) {
+                       const void *params, size_t paramLen) {
     Script *s = static_cast<Script *>(vs);
     s->runForEach(rsc,
                   static_cast<const Allocation *>(vain), static_cast<Allocation *>(vaout),
@@ -108,7 +108,7 @@
     s->Invoke(rsc, slot, NULL, 0);
 }
 
-void rsi_ScriptInvokeV(Context *rsc, RsScript vs, uint32_t slot, const void *data, uint32_t len) {
+void rsi_ScriptInvokeV(Context *rsc, RsScript vs, uint32_t slot, const void *data, size_t len) {
     Script *s = static_cast<Script *>(vs);
     s->Invoke(rsc, slot, data, len);
 }
@@ -139,7 +139,7 @@
     s->setVar(slot, &value, sizeof(value));
 }
 
-void rsi_ScriptSetVarV(Context *rsc, RsScript vs, uint32_t slot, const void *data, uint32_t len) {
+void rsi_ScriptSetVarV(Context *rsc, RsScript vs, uint32_t slot, const void *data, size_t len) {
     Script *s = static_cast<Script *>(vs);
     s->setVar(slot, data, len);
 }
diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h
index 088c8d1..c0324dd 100644
--- a/libs/rs/rsScript.h
+++ b/libs/rs/rsScript.h
@@ -70,7 +70,7 @@
 
     void initSlots();
     void setSlot(uint32_t slot, Allocation *a);
-    void setVar(uint32_t slot, const void *val, uint32_t len);
+    void setVar(uint32_t slot, const void *val, size_t len);
     void setVarObj(uint32_t slot, ObjectBase *val);
 
     virtual void runForEach(Context *rsc,
@@ -80,7 +80,7 @@
                             size_t usrBytes,
                             const RsScriptCall *sc = NULL) = 0;
 
-    virtual void Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len) = 0;
+    virtual void Invoke(Context *rsc, uint32_t slot, const void *data, size_t len) = 0;
     virtual void setupScript(Context *rsc) = 0;
     virtual uint32_t run(Context *) = 0;
 protected:
diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp
index 6d0701d..b230bb5 100644
--- a/libs/rs/rsScriptC.cpp
+++ b/libs/rs/rsScriptC.cpp
@@ -19,11 +19,6 @@
 #include "utils/Timers.h"
 #include "utils/StopWatch.h"
 
-#include <GLES/gl.h>
-#include <GLES/glext.h>
-
-#include <bcc/bcc.h>
-
 using namespace android;
 using namespace android::renderscript;
 
@@ -129,7 +124,7 @@
     rsc->mHal.funcs.script.invokeForEach(rsc, this, ain, aout, usr, usrBytes, sc);
 }
 
-void ScriptC::Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len) {
+void ScriptC::Invoke(Context *rsc, uint32_t slot, const void *data, size_t len) {
     if (slot >= mHal.info.exportedFunctionCount) {
         rsc->setError(RS_ERROR_BAD_SCRIPT, "Calling invoke on bad script");
         return;
@@ -269,7 +264,7 @@
 RsScript rsi_ScriptCCreate(Context *rsc,
                            const char *resName, size_t resName_length,
                            const char *cacheDir, size_t cacheDir_length,
-                           const char *text, uint32_t text_length)
+                           const char *text, size_t text_length)
 {
     ScriptC *s = new ScriptC(rsc);
 
diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h
index 4c85745..5c191d9 100644
--- a/libs/rs/rsScriptC.h
+++ b/libs/rs/rsScriptC.h
@@ -39,7 +39,7 @@
     const Allocation *ptrToAllocation(const void *) const;
 
 
-    virtual void Invoke(Context *rsc, uint32_t slot, const void *data, uint32_t len);
+    virtual void Invoke(Context *rsc, uint32_t slot, const void *data, size_t len);
 
     virtual uint32_t run(Context *);
 
diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp
index 4429556..ab164c3 100644
--- a/libs/rs/rsThreadIO.cpp
+++ b/libs/rs/rsThreadIO.cpp
@@ -149,7 +149,7 @@
         mToClientSocket.read(&mLastClientHeader, sizeof(mLastClientHeader));
     } else {
         size_t bytesData = 0;
-        const uint32_t *d = (const uint32_t *)mToClient.get(&mLastClientHeader.cmdID, &bytesData);
+        const uint32_t *d = (const uint32_t *)mToClient.get(&mLastClientHeader.cmdID, (uint32_t*)&bytesData);
         if (bytesData >= sizeof(uint32_t)) {
             mLastClientHeader.userID = d[0];
             mLastClientHeader.bytes = bytesData - sizeof(uint32_t);
diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h
index 44c7e71..7bb09bb 100644
--- a/libs/rs/rs_hal.h
+++ b/libs/rs/rs_hal.h
@@ -18,7 +18,6 @@
 #define RS_HAL_H
 
 #include <RenderScriptDefines.h>
-#include <ui/egl/android_natives.h>
 
 namespace android {
 namespace renderscript {
diff --git a/libs/ui/Input.cpp b/libs/ui/Input.cpp
index e95dbe4..0af7f80 100644
--- a/libs/ui/Input.cpp
+++ b/libs/ui/Input.cpp
@@ -700,7 +700,6 @@
 
 const uint32_t VelocityTracker::HISTORY_SIZE;
 const nsecs_t VelocityTracker::MAX_AGE;
-const nsecs_t VelocityTracker::MIN_WINDOW;
 const nsecs_t VelocityTracker::MIN_DURATION;
 
 VelocityTracker::VelocityTracker() {
@@ -891,14 +890,6 @@
 
         // Make sure we used at least one sample.
         if (samplesUsed != 0) {
-            // Scale the velocity linearly if the window of samples is small.
-            nsecs_t totalDuration = newestMovement.eventTime - oldestMovement.eventTime;
-            if (totalDuration < MIN_WINDOW) {
-                float scale = float(totalDuration) / float(MIN_WINDOW);
-                accumVx *= scale;
-                accumVy *= scale;
-            }
-
             *outVx = accumVx;
             *outVy = accumVy;
             return true;
diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp
index f933199f..87549fe 100644
--- a/libs/utils/BackupHelpers.cpp
+++ b/libs/utils/BackupHelpers.cpp
@@ -525,6 +525,7 @@
     String8 prefix;
 
     const int isdir = S_ISDIR(s.st_mode);
+    if (isdir) s.st_size = 0;   // directories get no actual data in the tar stream
 
     // !!! TODO: use mmap when possible to avoid churning the buffer cache
     // !!! TODO: this will break with symlinks; need to use readlink(2)
diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp
index dd0052a..58e0811 100644
--- a/libs/utils/RefBase.cpp
+++ b/libs/utils/RefBase.cpp
@@ -418,18 +418,20 @@
     if (c != 1) return;
     
     if ((impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK) {
-        if (impl->mStrong == INITIAL_STRONG_VALUE)
-            if (impl->mBase)
+        if (impl->mStrong == INITIAL_STRONG_VALUE) {
+            if (impl->mBase) {
                 impl->mBase->destroy();
-        else {
+            }
+        } else {
             // LOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
             delete impl;
         }
     } else {
         impl->mBase->onLastWeakRef(id);
         if ((impl->mFlags&OBJECT_LIFETIME_FOREVER) != OBJECT_LIFETIME_FOREVER) {
-            if (impl->mBase)
+            if (impl->mBase) {
                 impl->mBase->destroy();
+            }
         }
     }
 }
@@ -551,8 +553,10 @@
 
 RefBase::~RefBase()
 {
-    if (mRefs->mWeak == 0) {
-        delete mRefs;
+    if ((mRefs->mFlags & OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK) {
+        if (mRefs->mWeak == 0) {
+            delete mRefs;
+        }
     }
 }
 
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index a54cf288..7634c6c 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -76,8 +76,9 @@
     public static final int FILE_TYPE_PNG     = 33;
     public static final int FILE_TYPE_BMP     = 34;
     public static final int FILE_TYPE_WBMP    = 35;
+    public static final int FILE_TYPE_WEBP    = 36;
     private static final int FIRST_IMAGE_FILE_TYPE = FILE_TYPE_JPEG;
-    private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WBMP;
+    private static final int LAST_IMAGE_FILE_TYPE = FILE_TYPE_WEBP;
    
     // Playlist file types
     public static final int FILE_TYPE_M3U      = 41;
@@ -213,6 +214,7 @@
         addFileType("PNG", FILE_TYPE_PNG, "image/png", MtpConstants.FORMAT_PNG);
         addFileType("BMP", FILE_TYPE_BMP, "image/x-ms-bmp", MtpConstants.FORMAT_BMP);
         addFileType("WBMP", FILE_TYPE_WBMP, "image/vnd.wap.wbmp");
+        addFileType("WEBP", FILE_TYPE_WBMP, "image/webp");
  
         addFileType("M3U", FILE_TYPE_M3U, "audio/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
         addFileType("M3U", FILE_TYPE_M3U, "application/x-mpegurl", MtpConstants.FORMAT_M3U_PLAYLIST);
diff --git a/media/java/android/mtp/MtpServer.java b/media/java/android/mtp/MtpServer.java
index c065ca8..687cc44 100644
--- a/media/java/android/mtp/MtpServer.java
+++ b/media/java/android/mtp/MtpServer.java
@@ -61,10 +61,6 @@
         native_send_object_removed(handle);
     }
 
-    public void setPtpMode(boolean usePtp) {
-        native_set_ptp_mode(usePtp);
-    }
-
     public void addStorage(MtpStorage storage) {
         native_add_storage(storage);
     }
@@ -78,7 +74,6 @@
     private native final void native_stop();
     private native final void native_send_object_added(int handle);
     private native final void native_send_object_removed(int handle);
-    private native final void native_set_ptp_mode(boolean usePtp);
     private native final void native_add_storage(MtpStorage storage);
     private native final void native_remove_storage(int storageId);
 }
diff --git a/media/jni/android_mtp_MtpServer.cpp b/media/jni/android_mtp_MtpServer.cpp
index 4d84cb7..e36e6db 100644
--- a/media/jni/android_mtp_MtpServer.cpp
+++ b/media/jni/android_mtp_MtpServer.cpp
@@ -70,14 +70,12 @@
     MtpDatabase*    mDatabase;
     MtpServer*      mServer;
     MtpStorageList  mStorageList;
-    bool            mUsePtp;
     int             mFd;
 
 public:
     MtpThread(MtpDatabase* database)
         :   mDatabase(database),
             mServer(NULL),
-            mUsePtp(false),
             mFd(-1)
     {
     }
@@ -85,10 +83,6 @@
     virtual ~MtpThread() {
     }
 
-    void setPtpMode(bool usePtp) {
-        mUsePtp = usePtp;
-    }
-
     void addStorage(MtpStorage *storage) {
         mStorageList.push(storage);
         if (mServer)
@@ -119,9 +113,6 @@
 
         mFd = open("/dev/mtp_usb", O_RDWR);
         if (mFd >= 0) {
-            ioctl(mFd, MTP_SET_INTERFACE_MODE,
-                    (mUsePtp ? MTP_INTERFACE_MODE_PTP : MTP_INTERFACE_MODE_MTP));
-
             mServer = new MtpServer(mFd, mDatabase, AID_MEDIA_RW, 0664, 0775);
             for (size_t i = 0; i < mStorageList.size(); i++) {
                 mServer->addStorage(mStorageList[i]);
@@ -224,18 +215,6 @@
 }
 
 static void
-android_mtp_MtpServer_set_ptp_mode(JNIEnv *env, jobject thiz, jboolean usePtp)
-{
-#ifdef HAVE_ANDROID_OS
-    sMutex.lock();
-    MtpThread *thread = sThread.get();
-    if (thread)
-        thread->setPtpMode(usePtp);
-    sMutex.unlock();
-#endif
-}
-
-static void
 android_mtp_MtpServer_add_storage(JNIEnv *env, jobject thiz, jobject jstorage)
 {
 #ifdef HAVE_ANDROID_OS
@@ -290,7 +269,6 @@
     {"native_stop",                 "()V",  (void *)android_mtp_MtpServer_stop},
     {"native_send_object_added",    "(I)V", (void *)android_mtp_MtpServer_send_object_added},
     {"native_send_object_removed",  "(I)V", (void *)android_mtp_MtpServer_send_object_removed},
-    {"native_set_ptp_mode",         "(Z)V", (void *)android_mtp_MtpServer_set_ptp_mode},
     {"native_add_storage",          "(Landroid/mtp/MtpStorage;)V",
                                             (void *)android_mtp_MtpServer_add_storage},
     {"native_remove_storage",       "(I)V", (void *)android_mtp_MtpServer_remove_storage},
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 8a180d8..6cb3847 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -26,13 +26,6 @@
 #include <system/audio.h>
 
 // ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
-// ----------------------------------------------------------------------------
 
 namespace android {
 
diff --git a/media/libmedia/IMediaMetadataRetriever.cpp b/media/libmedia/IMediaMetadataRetriever.cpp
index ebe821f..07152d8 100644
--- a/media/libmedia/IMediaMetadataRetriever.cpp
+++ b/media/libmedia/IMediaMetadataRetriever.cpp
@@ -30,26 +30,6 @@
 // foreground thread.
 #ifndef DISABLE_GROUP_SCHEDULE_HACK
 
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
-
-static int myTid() {
-#ifdef HAVE_GETTID
-    return gettid();
-#else
-    return getpid();
-#endif
-}
-
 #undef LOG_TAG
 #define LOG_TAG "IMediaMetadataRetriever"
 #include <utils/Log.h>
@@ -60,18 +40,18 @@
 static void sendSchedPolicy(Parcel& data)
 {
     SchedPolicy policy;
-    get_sched_policy(myTid(), &policy);
+    get_sched_policy(gettid(), &policy);
     data.writeInt32(policy);
 }
 
 static void setSchedPolicy(const Parcel& data)
 {
     SchedPolicy policy = (SchedPolicy) data.readInt32();
-    set_sched_policy(myTid(), policy);
+    set_sched_policy(gettid(), policy);
 }
 static void restoreSchedPolicy()
 {
-    set_sched_policy(myTid(), SP_FOREGROUND);
+    set_sched_policy(gettid(), SP_FOREGROUND);
 }
 }; // end namespace android
 #endif
diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp
index 88157d2..8b953e0 100644
--- a/media/libmedia/JetPlayer.cpp
+++ b/media/libmedia/JetPlayer.cpp
@@ -23,13 +23,6 @@
 #include <media/JetPlayer.h>
 
 
-#ifdef HAVE_GETTID
-static pid_t myTid() { return gettid(); }
-#else
-static pid_t myTid() { return getpid(); }
-#endif
-
-
 namespace android
 {
 
@@ -188,7 +181,7 @@
     // signal main thread that we started
     {
         Mutex::Autolock l(mMutex);
-        mTid = myTid();
+        mTid = gettid();
         LOGV("JetPlayer::render(): render thread(%d) signal", mTid);
         mCondition.signal();
     }
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index eae93ff..54a6547 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -68,18 +68,6 @@
 
 #include <OMX.h>
 
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
-
 namespace {
 using android::media::Metadata;
 using android::status_t;
@@ -387,14 +375,6 @@
     return NO_ERROR;
 }
 
-static int myTid() {
-#ifdef HAVE_GETTID
-    return gettid();
-#else
-    return getpid();
-#endif
-}
-
 status_t MediaPlayerService::dump(int fd, const Vector<String16>& args)
 {
     const size_t SIZE = 256;
@@ -426,7 +406,7 @@
         }
 
         result.append(" Files opened and/or mapped:\n");
-        snprintf(buffer, SIZE, "/proc/%d/maps", myTid());
+        snprintf(buffer, SIZE, "/proc/%d/maps", gettid());
         FILE *f = fopen(buffer, "r");
         if (f) {
             while (!feof(f)) {
@@ -446,13 +426,13 @@
             result.append("\n");
         }
 
-        snprintf(buffer, SIZE, "/proc/%d/fd", myTid());
+        snprintf(buffer, SIZE, "/proc/%d/fd", gettid());
         DIR *d = opendir(buffer);
         if (d) {
             struct dirent *ent;
             while((ent = readdir(d)) != NULL) {
                 if (strcmp(ent->d_name,".") && strcmp(ent->d_name,"..")) {
-                    snprintf(buffer, SIZE, "/proc/%d/fd/%s", myTid(), ent->d_name);
+                    snprintf(buffer, SIZE, "/proc/%d/fd/%s", gettid(), ent->d_name);
                     struct stat s;
                     if (lstat(buffer, &s) == 0) {
                         if ((s.st_mode & S_IFMT) == S_IFLNK) {
diff --git a/media/libmediaplayerservice/MetadataRetrieverClient.cpp b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
index 8f776b4..06fb103 100644
--- a/media/libmediaplayerservice/MetadataRetrieverClient.cpp
+++ b/media/libmediaplayerservice/MetadataRetrieverClient.cpp
@@ -40,18 +40,6 @@
 #include "MetadataRetrieverClient.h"
 #include "StagefrightMetadataRetriever.h"
 
-/* desktop Linux needs a little help with gettid() */
-#if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
-#define __KERNEL__
-# include <linux/unistd.h>
-#ifdef _syscall0
-_syscall0(pid_t,gettid)
-#else
-pid_t gettid() { return syscall(__NR_gettid);}
-#endif
-#undef __KERNEL__
-#endif
-
 namespace android {
 
 extern player_type getPlayerType(const char* url);
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
index 589c625..7e04523 100644
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ b/media/libmediaplayerservice/MidiFile.cpp
@@ -29,17 +29,12 @@
 #include <libsonivox/eas_reverb.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <unistd.h>
 
 #include <system/audio.h>
 
 #include "MidiFile.h"
 
-#ifdef HAVE_GETTID
-static pid_t myTid() { return gettid(); }
-#else
-static pid_t myTid() { return getpid(); }
-#endif
-
 // ----------------------------------------------------------------------------
 
 namespace android {
@@ -455,7 +450,7 @@
     // signal main thread that we started
     {
         Mutex::Autolock l(mMutex);
-        mTid = myTid();
+        mTid = gettid();
         LOGV("render thread(%d) signal", mTid);
         mCondition.signal();
     }
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index f6a8b17..d0f6730 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -2862,8 +2862,8 @@
      * For historical reasons, any user data start
      * with "\0xA9", must be followed by its assoicated
      * language code.
-     * 0x0012: locale en
-     * 0x15c7: language 5575
+     * 0x0012: text string length
+     * 0x15c7: lang (locale) code: en
      */
     writeInt32(0x001215c7);
     writeLatitude(mLatitudex10000);
diff --git a/media/libstagefright/colorconversion/Android.mk b/media/libstagefright/colorconversion/Android.mk
index 702a7b4..62ba40f 100644
--- a/media/libstagefright/colorconversion/Android.mk
+++ b/media/libstagefright/colorconversion/Android.mk
@@ -9,10 +9,6 @@
         $(TOP)/frameworks/base/include/media/stagefright/openmax \
         $(TOP)/hardware/msm7k
 
-ifneq ($(filter crespo crespo4g,$(TARGET_DEVICE)),)
-LOCAL_CFLAGS += -DTHIS_IS_CRESPO=1
-endif
-
 LOCAL_MODULE:= libstagefright_color_conversion
 
 include $(BUILD_STATIC_LIBRARY)
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index 1828ac8..a4e8ee4 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -59,7 +59,6 @@
     size_t bufWidth, bufHeight;
 
     switch (mColorFormat) {
-#ifndef THIS_IS_CRESPO
         case OMX_COLOR_FormatYUV420Planar:
         {
             halFormat = HAL_PIXEL_FORMAT_YV12;
@@ -67,7 +66,6 @@
             bufHeight = (mHeight + 1) & ~1;
             break;
         }
-#endif
 
         default:
             halFormat = HAL_PIXEL_FORMAT_RGB_565;
@@ -89,11 +87,7 @@
             native_window_set_usage(
             mNativeWindow.get(),
             GRALLOC_USAGE_SW_READ_NEVER | GRALLOC_USAGE_SW_WRITE_OFTEN
-            | GRALLOC_USAGE_HW_TEXTURE
-#ifndef THIS_IS_CRESPO
-            | GRALLOC_USAGE_EXTERNAL_DISP
-#endif
-            ));
+            | GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_EXTERNAL_DISP));
 
     // Width must be multiple of 32???
     CHECK_EQ(0, native_window_set_buffers_geometry(
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
index 79fd2cb..e7f98de 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
@@ -123,63 +123,6 @@
       intent.setDataAndType(path, mimetype);
       startActivity(intent);
     }
-    
-    @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
-      switch (keyCode) {
-          case KeyEvent.KEYCODE_0:
-            MediaPlayer mp = new MediaPlayer();
-            try{
-              mp.setDataSource(MediaNames.VIDEO_RTSP3GP);
-              Log.v("emily","awb  " + testfilepath);
-              mp.setDisplay(mSurfaceView.getHolder());
-              mp.prepare();
-              mp.start();
-            }catch (Exception e){}
-              break;
-          
-          //start the music player intent with the test URL from PV    
-          case KeyEvent.KEYCODE_1:
-            startPlayback(MediaNames.STREAM_MP3_1);
-            break;
-          
-          case KeyEvent.KEYCODE_2:
-            startPlayback(MediaNames.STREAM_MP3_2);
-            break;
-          
-          case KeyEvent.KEYCODE_3:
-            startPlayback(MediaNames.STREAM_MP3_3);
-            break;
-          
-          case KeyEvent.KEYCODE_4:
-            startPlayback(MediaNames.STREAM_MP3_4);
-            break;
-          
-          case KeyEvent.KEYCODE_5:
-            startPlayback(MediaNames.STREAM_MP3_5);
-            break;
-          
-          case KeyEvent.KEYCODE_6:
-            startPlayback(MediaNames.STREAM_MP3_6);
-            break;
-          
-          case KeyEvent.KEYCODE_7:
-            startPlayback(MediaNames.STREAM_MP3_7);
-            break;
-          
-          case KeyEvent.KEYCODE_8:
-            startPlayback(MediaNames.STREAM_MP3_8);
-            break;
-          
-          case KeyEvent.KEYCODE_9:
-            startPlayback(MediaNames.STREAM_MP3_9);
-            break;
-          
-              
-              
-      }
-      return super.onKeyDown(keyCode, event);
-     
-  }  
 
   public static boolean checkStreamingServer() throws Exception {
       InetAddress address = InetAddress.getByAddress(MediaNames.STREAM_SERVER);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
index ca6e999..d6e1346 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaNames.java
@@ -50,19 +50,9 @@
     public static final long PAUSE_WAIT_TIME = 3000;
     public static final long WAIT_TIME = 2000;
     public static final long WAIT_SNAPSHOT_TIME = 5000;
-  
-    //Streaming Video
-    public static final String VIDEO_HTTP3GP = "http://pvs.pv.com/jj/lipsync0.3gp";  
-    public static final String VIDEO_RTSP3GP = "rtsp://63.241.31.203/public/jj/md.3gp";
-    public static final String VIDEO_RTSP3GP2 = "rtsp://pvs.pv.com/public/live_dvd1.3gp";
-    public static final String VIDEO_RTSP3GP3 = 
-      "rtsp://ehug.rtsp-youtube.l.google.com/" +
-      "Ci4LENy73wIaJQmeRVCJq4HuQBMYDSANFEIJbXYtZ29vZ2xlSARSB2RldGFpbHMM/0/0/0/video.3gp";
-    //public static final String VIDEO_RTSP3GP = "rtsp://193.159.241.21/sp/alizee05.3gp";
-  
+
     //local video
     public static final String VIDEO_MP4 = "/sdcard/media_api/video/MPEG4_320_AAC_64.mp4";
-    public static final String VIDEO_LONG_3GP = "/sdcard/media_api/video/radiohead.3gp";
     public static final String VIDEO_SHORT_3GP = "/sdcard/media_api/video/short.3gp";
     public static final String VIDEO_LARGE_SIZE_3GP = "/sdcard/media_api/video/border_large.3gp";
     public static final String VIDEO_H263_AAC = "/sdcard/media_api/video/H263_56_AAC_24.3gp";
@@ -73,205 +63,9 @@
     public static final String VIDEO_HIGHRES_H263 = "/sdcard/media_api/video/H263_500_AMRNB_12.3gp";
     public static final String VIDEO_HIGHRES_MP4 = "/sdcard/media_api/video/H264_500_AAC_128.3gp";
     
-    //ringtone
-    public static final String ringtone = "/sdcard/media_api/ringtones/F1_NewVoicemail.mp3";
-
-    //streaming mp3
-    public static final String STREAM_MP3_1 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/mp3/chadthi_jawani_128kbps.mp3";
-    public static final String STREAM_MP3_2 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/mp3/dualStereo.mp3";
-    public static final String STREAM_MP3_3 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/15%20Keep%20Holding%20On.mp3";
-    public static final String STREAM_MP3_4 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/1%20-%20Apologize.mp3";
-    public static final String STREAM_MP3_5 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads/" +
-      "03%20You're%20Gonna%20Miss%20This.mp3";
-    public static final String STREAM_MP3_6 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads" +
-      "/02%20Looney%20Tunes%20%C3%82%C2%B7%20Light%20Cavalry%20Overture%20(LP%20Version).mp3";
-    public static final String STREAM_MP3_7 = 
-      "http://wms.pv.com:7070/mediadownloadcontent/UserUploads" +
-      "/01%20Love%20Song%20(Album%20Version).mp3";
-    public static final String STREAM_MP3_8 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/UserUploads/1%20-%20Apologize.mp3";
-    public static final String STREAM_MP3_9 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/UserUploads" +
-      "/1%20-%20Smile%20(Explicit%20Version).mp3";
-    public static final String STREAM_MP3_10 = 
-      "http://wms.pv.com:7070/MediaDownloadContent/UserUploads/beefcake.mp3";
-
-    //Sonivox
-    public static String MIDIFILES[] = { 
-        "/sdcard/media_api/music/Leadsol.mxmf",
-        "/sdcard/media_api/music/abba.imy", "/sdcard/media_api/music/ants.mid",
-        "/sdcard/media_api/music/greensleeves.rtttl", "/sdcard/media_api/music/test.ota"};
-  
-    //Performance measurement
-    public static String[] WAVFILES = { 
-        "/sdcard/media_api/music_perf/WAV/M1F1-AlawWE-AFsp.wav",
-        "/sdcard/media_api/music_perf/WAV/M1F1-float64-AFsp.wav",
-        "/sdcard/media_api/music_perf/WAV/song.wav",
-        "/sdcard/media_api/music_perf/WAV/WAVEtest.wav",
-        "/sdcard/media_api/music_perf/WAV/WAVEtest_out.wav",
-        "/sdcard/media_api/music_perf/WAV/test_out.wav"};
-        
-    public static String[] AMRNBFILES = { 
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.9kbps_6.24kbps_8khz_mono_NMC.amr",
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_5.15kbps_5.46kbps_8khz_mono_NMC.amr",
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.4kbps_7.80kbps_8khz_mono_NMC.amr",
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_7.95kbps_9.6kbps_8khz_mono_NMC.amr",
-        "/sdcard/media_api/music_perf/AMR/AI_AMR-NB_10.2kbps_10.48kbps_8khz_mono_NMC.amr"};
-  
-    public static String[] AMRWBFILES = { 
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_15.85kbps_16kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_18.25kbps_18kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_19.85kbps_20kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.05kbps_23kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/NIN_AMR-WB_23.85kbps_24kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_19.85kbps_20kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.05kbps_23kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/PD_AMR-WB_23.85kbps_24kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.05kbps_23kbps.amr",
-        "/sdcard/media_api/music_perf/AMRWB/WC_AMR-WB_23.85kbps_24kbps.amr", };
- 
-    public static String[] MP3FILES = { 
-        "/sdcard/media_api/music_perf/MP3/NIN_56kbps_32khz_stereo_VBR_MCA.MP3",
-        "/sdcard/media_api/music_perf/MP3/NIN_80kbps_32khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_80kbps_44.1khz_stereo_VBR_MCA.mp3", 
-        "/sdcard/media_api/music_perf/MP3/NIN_80kbps_48khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_112kbps_32khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_112kbps_44.1khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_112kbps_48khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_192kbps_32khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_192kbps_44.1khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_192kbps_48khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_256kbps_44.1khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/NIN_256kbps_48khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_112kbps_32khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_112kbps_44.1khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_112kbps_48khz_stereo_VBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_192kbps_32khz_mono_CBR_DPA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_256kbps_44.1khz_mono_CBR_DPA.mp3",
-        "/sdcard/media_api/music_perf/MP3/PD_256kbps_48khz_mono_CBR_MCA.mp3",
-        "/sdcard/media_api/music_perf/MP3/WC_256kbps_44.1khz_mono_CBR_DPA.mp3",
-        "/sdcard/media_api/music_perf/MP3/WC_256kbps_48khz_mono_CBR_DPA.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Apologize.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Because_Of_You.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Complicated.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Glamorous.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Im_With_You.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Smile.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/Suddenly_I_See.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/When You Say Nothing At All.mp3",
-        "/sdcard/media_api/music_perf/regular_album_photo/my_happy_ending.mp3"};
-  
-    public static String[] AACFILES = { 
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_24kbps_12khz_Mono_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/AI_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/NIN_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PD_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/PV_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_22.05khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_32khz_Stereo_CBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_56kbps_44.1khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_1pCBR_SSE.mp4",
-        "/sdcard/media_api/music_perf/AAC/WC_AAC_80kbps_32khz_Stereo_CBR_SSE.mp4",      
-    };
-    
-    public static String[] VIDEOFILES = { "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_10fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_12fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_15fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_SSE.mp4",
-      "/sdcard/media_api/video_perf/AI_CTO_Mpeg4_32kbps_7.5fps_SQCIF_128x96+AAC_8kbps_8khz_mono_QTE.mp4",
-      "/sdcard/media_api/video_perf/AI_WMV_1024kbps_20fps_QCIF_176x144_noaudio_SSE.wmv",
-      "/sdcard/media_api/video_perf/AI_WMV_1024kbps_25fps_QCIF_176x144_noaudio_SSE.wmv",
-      "/sdcard/media_api/video_perf/Chicken.wmv",
-      "/sdcard/media_api/video_perf/MP_qcif_15fps_100kbps_48kHz_192kbps_30secs.wmv",
-      "/sdcard/media_api/video_perf/NIN_CTO_H264_123kbps_5fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_10.2fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_12fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_H264_96kbps_15fps_QCIF_176x144+AMR_12.2kbps_8khz_mono_QTE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_mono_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_10fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_12fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_15fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_CTO_Mpeg4_128kbps_7.5fps_QCIF_176x144+AAC+_32kbps_48khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_H263_128kbps_10fps_QCIF_174x144_noaudio_SSE.mp4",
-      "/sdcard/media_api/video_perf/NIN_H263_128kbps_15fps_QCIF_174x144_noaudio_SSE.mp4",
-      "/sdcard/media_api/video_perf/NIN_H263_48kbps_10fps_QCIF_174x144_noaudio_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_H263_48kbps_12fps_QCIF_174x144_noaudio_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_H264_123kbps_15fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/NIN_H264_123kbps_7.5fps_QCIF_176x144+AAC_32kbps_22khz_stereo_SSE.3gp",
-      "/sdcard/media_api/video_perf/PV_H264_2000kbps_20fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4",
-      "/sdcard/media_api/video_perf/PV_H264_2000kbps_25fps_CIF_352x288+AAC_96kbps_48khz_stereo_SSE.mp4",
-      "/sdcard/media_api/video_perf/PV_H264_2000kbps_30fps_CIF_352x288+AAC_128kbps_48khz_stereo_SSE.mp4",
-      "/sdcard/media_api/video_perf/Stevie-1.wmv",
-      "/sdcard/media_api/video_perf/WC_H264_1600kbps_20fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4",
-      "/sdcard/media_api/video_perf/WC_H264_1600kbps_25fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4",
-      "/sdcard/media_api/video_perf/WC_H264_1600kbps_30fps_QCIF_176x144+AAC_96kbps_48khz_mono_SSE.mp4",
-      "/sdcard/media_api/video_perf/bugs.wmv",
-      "/sdcard/media_api/video_perf/niceday.wmv",
-      "/sdcard/media_api/video_perf/eaglesatopnflpe.wmv",
-     
-    };
-    
-    //wma - only support up to wma 9
-    public static String[] WMASUPPORTED = {
-      "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/AI_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/NIN_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/PD_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/PV_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_32kbps_44.1khz_mono_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMASUPPORTED/WC_WMA9.2_48kbps_44.1khz_mono_CBR_DPA.wma"
-      
-    };
-    
-    public static String[] WMAUNSUPPORTED = { 
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_127kbps_48khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_44.1khz_stereo_2pVBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_48khz_stereo_2pVBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_88khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_128kbps_96khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_44.1khz_stereo_2pVBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_88khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_192kbps_96khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_44khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_48khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_88khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_256kbps_96khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_44khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_48khz_stereo_CBR_DPA.wma",
-      "/sdcard/media_api/music_perf/WMAUNSUPPORTED/AI_WMA10_384kbps_88khz_stereo_CBR_DPA.wma"
-    };
-    
     //Media Recorder
     public static final String RECORDER_OUTPUT = "/sdcard/media_api/recorderOutput.amr";
-    
+
     //video thumbnail
     public static final String THUMBNAIL_OUTPUT = "/sdcard/media_api/videoThumbnail.png";
     public static final String GOLDEN_THUMBNAIL_OUTPUT = "/sdcard/media_api/goldenThumbnail.png";
@@ -318,7 +112,7 @@
       "/sdcard/media_api/metadata/test34.wmv",
       "/sdcard/media_api/metadata/test_metadata.mp4",
   };
-  
+
   public static final String[] METADATA_RETRIEVAL_TEST_FILES = {
       // Raw AAC is not supported
       // "/sdcard/media_api/test_raw.aac",
@@ -405,54 +199,8 @@
           null, null, null, null, null, null, "295", "1", null}
   };
 
-  public static final String META_DATA_OTHERS [][] = {
-      {"/sdcard/media_api/metaDataTestMedias/3GP/cat.3gp", null, null, null,
-          null, null, "20080309T002415.000Z", null,
-          null, null, "63916", "2", null},
-      {"/sdcard/media_api/metaDataTestMedias/AMR/AMR_NB.amr", null, null, null,
-          null, null, null, null,
-          null, null, "126540", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/AMRWB/AMR_WB.amr", null, null, null,
-          null, null, null, null,
-          null, null, "231180", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/M4A/Jaws Of Life_ver1.m4a", "1/8", "Suspended Animation",
-          "John Petrucci", null, null, "20070510T125223.000Z", 
-          "12", "Jaws Of Life", "2005", "449329", "1", "m4a composer"},
-      {"/sdcard/media_api/metaDataTestMedias/M4V/sample_iPod.m4v", null, null, 
-          null, null, null, "20051220T202015.000Z", 
-          null, null, null, "85500", "2", null},
-      {"/sdcard/media_api/metaDataTestMedias/MIDI/MIDI.mid", null, "Suspended Animation", 
-          "John Petrucci", null, null, "20070510T125223.000Z", 
-          null, null, "2005", "231180", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/MP4/kung_fu_panda_h264.mp4", "2/0", "mp4 album Kung Fu Panda",
-          "mp4 artist Kung Fu Panda", null, null, "20080517T091451.000Z", 
-          "40", "Kung Fu Panda", "2008", "128521", "2", "mp4 composer"},
-      {"/sdcard/media_api/metaDataTestMedias/OGG/Ring_Classic_02.ogg", null, "Suspended Animation", 
-          "John Petrucci", null, null, "20070510T125223.000Z", 
-          null, null, "2005", "231180", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/OGG/When You Say Nothing At All.ogg", 
-          null, "Suspended Animation", "John Petrucci", 
-          null, null, "20070510T125223.000Z", null, null, "2005", "231180", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/WAV/Im With You.wav", null, null, 
-          null, null, null, null, 
-          null, null, null, "224000", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/WMA/WMA9.wma", "6", "Ten Songs in the Key of Betrayal", 
-          "Alien Crime Syndicate", "Alien Crime Syndicate", 
-          "wma 9 Composer", "20040521T175729.483Z", 
-          "Rock", "Run for the Money", "2004", "134479", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/WMA/WMA10.wma", "09", "wma 10 Album", 
-          "wma 10 Album Artist", "wma 10 Artist", "wma 10 Composer", "20070705T063625.097Z", 
-          "Acid Jazz", "wma 10 Title", "2010", "126574", "1", null},
-      {"/sdcard/media_api/metaDataTestMedias/WMV/bugs.wmv", "8", "wmv 9 Album", 
-          null, "wmv 9 Artist ", null, "20051122T155247.540Z", 
-          null, "Looney Tunes - Hare-Breadth Hurry", "2005", "193482", "2", null},
-      {"/sdcard/media_api/metaDataTestMedias/WMV/clips_ver7.wmv", "50", "wmv 7 Album", 
-          null, "Hallau Shoots & Company", null, "20020226T170045.891Z", 
-          null, "CODEC Shootout", "1986", "43709", "2", null}
-  };
-  
   //output recorded video
-  
+
   public static final String RECORDED_HVGA_H263 = "/sdcard/HVGA_H263.3gp";
   public static final String RECORDED_QVGA_H263 = "/sdcard/QVGA_H263.3gp";
   public static final String RECORDED_SQVGA_H263 = "/sdcard/SQVGA_H263.3gp";
@@ -474,22 +222,7 @@
  
   public static final long RECORDED_TIME = 5000;
   public static final long VALID_VIDEO_DURATION = 2000;
-  
-  //Videos for the mediaplayer stress test
-  public static String[] H263_STRESS = { 
-      "/sdcard/media_api/video_stress/h263/H263_CIF.3gp",
-      "/sdcard/media_api/video_stress/h263/H263_QCIF.3gp",
-      "/sdcard/media_api/video_stress/h263/H263_QVGA.3gp",
-      "/sdcard/media_api/video_stress/h263/H263_SQVGA.3gp"
-  };
-  
-  public static String[] MPEG4_STRESS = { 
-    "/sdcard/media_api/video_stress/h263/mpeg4_CIF.mp4",
-    "/sdcard/media_api/video_stress/h263/mpeg4_QCIF.3gp",
-    "/sdcard/media_api/video_stress/h263/mpeg4_QVGA.3gp",
-    "/sdcard/media_api/video_stress/h263/mpeg4_SQVGA.mp4"
-  };
-  
+
   //Streaming test files
   public static final byte [] STREAM_SERVER = new byte[] {(byte)75,(byte)17,(byte)48,(byte)204};
   public static final String STREAM_H264_480_360_1411k = 
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
index 6ded74d..00e0a52 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaMetadataTest.java
@@ -47,14 +47,8 @@
         CORRUPTED_ID3V2_TYER, CORRUPTED_ID3V2_TYER_2, CORRUPTED_ID3V2_TIT
     }
     
-    public static enum NON_MP3_TEST_FILE{
-         THREE3GP, AMRNB, AMRWB, M4A1, M4V, MIDI,
-         H264, OGG1, OGG2, WAV, WMA9, WMA10, WMV9, WMV7
-    }
-    
     public static METADATA_EXPECTEDRESULT meta;
     public static MP3_TEST_FILE mp3_test_file;
-    public static NON_MP3_TEST_FILE non_mp3_test_file;
    
     @MediumTest
     public static void testID3V1V2Metadata() throws Exception {
@@ -116,88 +110,11 @@
         validateMetatData(mp3_test_file.CORRUPTED_ID3V2_TIT.ordinal(), MediaNames.META_DATA_MP3);
     }
    
-    @MediumTest
-    public static void test3gp_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.THREE3GP.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testAmr_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.AMRNB.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testAmrWb_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.AMRWB.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testM4A1_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.M4A1.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testM4v_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.M4V.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testH264_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.H264.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    //bug# 1440489
-    @Suppress
-    @MediumTest
-    public static void testOgg1_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.OGG1.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testOgg2_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.OGG2.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testMidi_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.MIDI.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @MediumTest
-    public static void testWav_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WAV.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testWma9_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WMA9.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testWma10_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WMA10.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testWmv9_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WMV9.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
-    
-    @Suppress
-    @MediumTest
-    public static void testWmv10_Metadata() throws Exception {
-        validateMetatData(non_mp3_test_file.WMV7.ordinal(), MediaNames.META_DATA_OTHERS);
-    }
      
     private static void validateMetatData(int fileIndex, String meta_data_file[][]) {
         Log.v(TAG, "filePath = "+ meta_data_file[fileIndex][0]);
         if ((meta_data_file[fileIndex][0].endsWith("wma") && !MediaProfileReader.getWMAEnable()) ||
             (meta_data_file[fileIndex][0].endsWith("wmv") && !MediaProfileReader.getWMVEnable())) {
-            Log.v(TAG, "Skip test since windows media is not supported");
             return;
         }
         String value = null;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
index d22025c..3a9564d 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java
@@ -369,13 +369,7 @@
       boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_MP4);
       assertTrue("Local MP4 SeekTo", isSeek);          
     }
-    
-    @LargeTest
-    public void testVideoLong3gpSeekTo() throws Exception {
-      boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_LONG_3GP);
-      assertTrue("Local 3gp SeekTo", isSeek);         
-    }
-   
+
     @LargeTest
     public void testVideoH263AACSeekTo() throws Exception {
       boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_H263_AAC);
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index 34affa7..3b5b9a3 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -100,109 +100,6 @@
             MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
     }
 
-    public void createDB() {
-        mDB = SQLiteDatabase.openOrCreateDatabase("/sdcard/perf.db", null);
-        mDB.execSQL("CREATE TABLE IF NOT EXISTS perfdata (_id INTEGER PRIMARY KEY," + 
-                "file TEXT," + "setdatatime LONG," + "preparetime LONG," +
-                "playtime LONG" + ");");
-        //clean the table before adding new data
-        mDB.execSQL("DELETE FROM perfdata");
-    }
-
-    public void audioPlaybackStartupTime(String[] testFile) {
-        long t1 = 0;
-        long t2 = 0;
-        long t3 = 0;
-        long t4 = 0;
-        long setDataSourceDuration = 0;
-        long prepareDuration = 0;
-        long startDuration = 0;
-        long totalSetDataTime = 0;
-        long totalPrepareTime = 0;
-        long totalStartDuration = 0;
-
-        int numberOfFiles = testFile.length;
-        Log.v(TAG, "File length " + numberOfFiles);
-        for (int k = 0; k < numberOfFiles; k++) {
-            MediaPlayer mp = new MediaPlayer();
-            try {
-                t1 = SystemClock.uptimeMillis();
-                FileInputStream fis = new FileInputStream(testFile[k]);
-                FileDescriptor fd = fis.getFD();
-                mp.setDataSource(fd);
-                fis.close();
-                t2 = SystemClock.uptimeMillis();
-                mp.prepare();
-                t3 = SystemClock.uptimeMillis();
-                mp.start();
-                t4 = SystemClock.uptimeMillis();
-            } catch (Exception e) {
-                Log.v(TAG, e.toString());
-            }
-            setDataSourceDuration = t2 - t1;
-            prepareDuration = t3 - t2;
-            startDuration = t4 - t3;
-            totalSetDataTime = totalSetDataTime + setDataSourceDuration;
-            totalPrepareTime = totalPrepareTime + prepareDuration;
-            totalStartDuration = totalStartDuration + startDuration;
-            mDB.execSQL("INSERT INTO perfdata (file, setdatatime, preparetime," +
-                    " playtime) VALUES (" + '"' + testFile[k] + '"' + ',' +
-                    setDataSourceDuration + ',' + prepareDuration +
-            		',' + startDuration + ");");
-            Log.v(TAG, "File name " + testFile[k]);
-            mp.stop();
-            mp.release();
-        }
-        Log.v(TAG, "setDataSource average " + totalSetDataTime / numberOfFiles);
-        Log.v(TAG, "prepare average " + totalPrepareTime / numberOfFiles);
-        Log.v(TAG, "start average " + totalStartDuration / numberOfFiles);
-
-    }
-
-    @Suppress
-    public void testStartUpTime() throws Exception {
-        createDB();
-        audioPlaybackStartupTime(MediaNames.MP3FILES);
-        audioPlaybackStartupTime(MediaNames.AACFILES);
-
-        //close the database after all transactions
-        if (mDB.isOpen()) {
-            mDB.close();
-        }
-    }
-
-    public void wmametadatautility(String[] testFile) {
-        long t1 = 0;
-        long t2 = 0;
-        long sum = 0;
-        long duration = 0;
-        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
-        String value;
-        for (int i = 0, n = testFile.length; i < n; ++i) {
-            try {
-                t1 = SystemClock.uptimeMillis();
-                retriever.setDataSource(testFile[i]);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_COMPOSER);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_GENRE);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
-                value = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_YEAR);
-                value =
-                    retriever
-                    .extractMetadata(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER);
-                t2 = SystemClock.uptimeMillis();
-                duration = t2 - t1;
-                Log.v(TAG, "Time taken = " + duration);
-                sum = sum + duration;
-            } catch (Exception e) {
-                Log.v(TAG, e.getMessage());
-            }
-
-        }
-        Log.v(TAG, "Average duration = " + sum / testFile.length);
-    }
-
     private void initializeMessageLooper() {
         final ConditionVariable startDone = new ConditionVariable();
         new Thread() {
@@ -421,13 +318,6 @@
         return true;
     }
 
-    @Suppress
-    public void testWmaParseTime() throws Exception {
-        // createDB();
-        wmametadatautility(MediaNames.WMASUPPORTED);
-    }
-
-
     // Test case 1: Capture the memory usage after every 20 h263 playback
     @LargeTest
     public void testH263VideoPlaybackMemoryUsage() throws Exception {
diff --git a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
index 52bfc28..ed413e6 100644
--- a/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
+++ b/packages/BackupRestoreConfirmation/src/com/android/backupconfirm/BackupRestoreConfirmation.java
@@ -60,6 +60,7 @@
     IBackupManager mBackupManager;
     FullObserver mObserver;
     int mToken;
+    boolean mDidAcknowledge;
 
     TextView mStatusView;
     Button mAllowButton;
@@ -70,6 +71,7 @@
         Context mContext;
         ObserverHandler(Context context) {
             mContext = context;
+            mDidAcknowledge = false;
         }
 
         @Override
@@ -157,11 +159,7 @@
         mAllowButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                try {
-                    mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver);
-                } catch (RemoteException e) {
-                    // TODO: bail gracefully if we can't contact the backup manager
-                }
+                sendAcknowledgement(mToken, true, mObserver);
                 mAllowButton.setEnabled(false);
                 mDenyButton.setEnabled(false);
             }
@@ -170,11 +168,7 @@
         mDenyButton.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
-                try {
-                    mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, mObserver);
-                } catch (RemoteException e) {
-                    // TODO: bail gracefully if we can't contact the backup manager
-                }
+                sendAcknowledgement(mToken, false, mObserver);
                 mAllowButton.setEnabled(false);
                 mDenyButton.setEnabled(false);
             }
@@ -187,14 +181,21 @@
 
         // We explicitly equate departure from the UI with refusal.  This includes the
         // implicit configuration-changed stop/restart cycle.
-        try {
-            mBackupManager.acknowledgeFullBackupOrRestore(mToken, false, null);
-        } catch (RemoteException e) {
-            // if this fails we'll still time out with no acknowledgment
-        }
+        sendAcknowledgement(mToken, false, null);
         finish();
     }
 
+    void sendAcknowledgement(int token, boolean allow, IFullBackupRestoreObserver observer) {
+        if (!mDidAcknowledge) {
+            mDidAcknowledge = true;
+            try {
+                mBackupManager.acknowledgeFullBackupOrRestore(mToken, true, mObserver);
+            } catch (RemoteException e) {
+                // TODO: bail gracefully if we can't contact the backup manager
+            }
+        }
+    }
+
     /**
      * The observer binder for showing backup/restore progress.  This binder just bounces
      * the notifications onto the main thread.
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
index 0f5f095..9cbf704 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
@@ -17,6 +17,8 @@
 package com.android.providers.settings;
 
 import com.android.internal.content.PackageHelper;
+import com.android.internal.telephony.BaseCommands;
+import com.android.internal.telephony.Phone;
 import com.android.internal.telephony.RILConstants;
 import com.android.internal.util.XmlUtils;
 import com.android.internal.widget.LockPatternUtils;
@@ -1300,8 +1302,13 @@
             }
     
             // Set the preferred network mode to 0 = Global, CDMA default
-            int type = SystemProperties.getInt("ro.telephony.default_network",
-                    RILConstants.PREFERRED_NETWORK_MODE);
+            int type;
+            if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) {
+                type = Phone.NT_MODE_GLOBAL;
+            } else {
+                type = SystemProperties.getInt("ro.telephony.default_network",
+                        RILConstants.PREFERRED_NETWORK_MODE);
+            }
             loadSetting(stmt, Settings.Secure.PREFERRED_NETWORK_MODE, type);
     
             // Enable or disable Cell Broadcast SMS
diff --git a/packages/SharedStorageBackup/Android.mk b/packages/SharedStorageBackup/Android.mk
new file mode 100644
index 0000000..1d4f4da7
--- /dev/null
+++ b/packages/SharedStorageBackup/Android.mk
@@ -0,0 +1,33 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+LOCAL_PACKAGE_NAME := SharedStorageBackup
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
+
+########################
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/packages/SharedStorageBackup/AndroidManifest.xml b/packages/SharedStorageBackup/AndroidManifest.xml
new file mode 100644
index 0000000..258059c
--- /dev/null
+++ b/packages/SharedStorageBackup/AndroidManifest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.android.sharedstoragebackup" >
+
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
+
+    <application android:allowClearUserData="false"
+                 android:permission="android.permission.CONFIRM_FULL_BACKUP"
+                 android:fullBackupAgent=".SharedStorageAgent"
+                 android:allowBackup="false" >
+    </application>
+</manifest>
diff --git a/packages/SharedStorageBackup/proguard.flags b/packages/SharedStorageBackup/proguard.flags
new file mode 100644
index 0000000..f43cb81
--- /dev/null
+++ b/packages/SharedStorageBackup/proguard.flags
@@ -0,0 +1 @@
+-keep class com.android.sharedstoragebackup.SharedStorageAgent
diff --git a/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
new file mode 100644
index 0000000..b02ca2e
--- /dev/null
+++ b/packages/SharedStorageBackup/src/com/android/sharedstoragebackup/SharedStorageAgent.java
@@ -0,0 +1,93 @@
+package com.android.sharedstoragebackup;
+
+import android.app.backup.FullBackup;
+import android.app.backup.FullBackupAgent;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.content.Context;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageVolume;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.IOException;
+
+public class SharedStorageAgent extends FullBackupAgent {
+    static final String TAG = "SharedStorageAgent";
+    static final boolean DEBUG = true;
+
+    StorageVolume[] mVolumes;
+
+    @Override
+    public void onCreate() {
+        StorageManager mgr = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
+        if (mgr != null) {
+            mVolumes = mgr.getVolumeList();
+        } else {
+            Slog.e(TAG, "Unable to access Storage Manager");
+        }
+    }
+
+    @Override
+    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
+            ParcelFileDescriptor newState) throws IOException {
+        // If there are shared-storage volumes available, run the inherited directory-
+        // hierarchy backup process on them.  By convention in the Storage Manager, the
+        // "primary" shared storage volume is first in the list.
+        if (mVolumes != null) {
+            for (int i = 0; i < mVolumes.length; i++) {
+                StorageVolume v = mVolumes[i];
+                // Express the contents of volume N this way in the tar stream:
+                //     shared/N/path/to/file
+                // The restore will then extract to the given volume
+                String domain = FullBackup.SHARED_PREFIX + i;
+                processTree(null, domain, v.getPath(), null, data);
+            }
+        }
+    }
+
+    /**
+     * Incremental onRestore() implementation is not used.
+     */
+    @Override
+    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)
+            throws IOException {
+    }
+
+    /**
+     * Full restore of one file to shared storage
+     */
+    @Override
+    public void onRestoreFile(ParcelFileDescriptor data, long size,
+            int type, String domain, String relpath, long mode, long mtime)
+            throws IOException {
+        Slog.d(TAG, "Shared restore: [ " + domain + " : " + relpath + "]");
+
+        File outFile = null;
+
+        // The file path must be in the semantic form [number]/path/to/file...
+        int slash = relpath.indexOf('/');
+        if (slash > 0) {
+            try {
+                int i = Integer.parseInt(relpath.substring(0, slash));
+                if (i <= mVolumes.length) {
+                    outFile = new File(mVolumes[i].getPath(), relpath.substring(slash + 1));
+                    if (DEBUG) Slog.i(TAG, " => " + outFile.getAbsolutePath());
+                } else {
+                    Slog.w(TAG, "Cannot restore data for unavailable volume " + i);
+                }
+            } catch (NumberFormatException e) {
+                if (DEBUG) Slog.w(TAG, "Bad volume number token: " + relpath.substring(0, slash));
+            }
+        } else {
+            if (DEBUG) Slog.i(TAG, "Can't find volume-number token");
+        }
+        if (outFile == null) {
+            Slog.e(TAG, "Skipping data with malformed path " + relpath);
+        }
+
+        FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, false);
+    }
+}
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..e2584e3
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..58b8510
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
new file mode 100644
index 0000000..2795c34
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
new file mode 100644
index 0000000..bbed6a6
--- /dev/null
+++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png
Binary files differ
diff --git a/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
new file mode 100644
index 0000000..977e002
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_zoom_pressed" />
+    <item android:drawable="@drawable/ic_sysbar_zoom_default" />
+</selector>
+
diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
index d9f3f23..707a8cb 100644
--- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml
+++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml
@@ -75,6 +75,13 @@
                     systemui:keyCode="82"
                     android:visibility="invisible"
                     />
+                <com.android.systemui.statusbar.policy.CompatModeButton
+                    android:id="@+id/compat_button"
+                    android:layout_width="80dip"
+                    android:layout_height="match_parent"
+                    android:src="@drawable/ic_sysbar_zoom"
+                    android:visibility="invisible"
+                    />
             </LinearLayout>
 
             <!-- fake space bar zone -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
new file mode 100644
index 0000000..9b44f78
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.policy;
+
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.RemoteException;
+import android.util.AttributeSet;
+import android.util.Slog;
+import android.view.View;
+import android.widget.ImageView;
+
+import com.android.systemui.R;
+
+public class CompatModeButton extends ImageView implements View.OnClickListener {
+    private static final String TAG = "StatusBar.CompatModeButton";
+
+    private ActivityManager mAM;
+
+    public CompatModeButton(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public CompatModeButton(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs);
+
+        setClickable(true);
+
+        mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+
+        setOnClickListener(this);
+
+        refresh();
+    }
+
+    @Override
+    public void onClick(View v) {
+        mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_TOGGLE);
+    }
+
+    public void refresh() {
+        setVisibility(
+                (mAM.getFrontActivityScreenCompatMode() == ActivityManager.COMPAT_MODE_NEVER)
+                ? View.GONE
+                : View.VISIBLE
+            );
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
index 70a78df..3175a99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkController.java
@@ -446,12 +446,14 @@
     }
 
     boolean isCdmaEri() {
-        final int iconIndex = mServiceState.getCdmaEriIconIndex();
-        if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
-            final int iconMode = mServiceState.getCdmaEriIconMode();
-            if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
-                    || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
-                return true;
+        if (mServiceState != null) {
+            final int iconIndex = mServiceState.getCdmaEriIconIndex();
+            if (iconIndex != EriInfo.ROAMING_INDICATOR_OFF) {
+                final int iconMode = mServiceState.getCdmaEriIconMode();
+                if (iconMode == EriInfo.ROAMING_ICON_MODE_NORMAL
+                        || iconMode == EriInfo.ROAMING_ICON_MODE_FLASH) {
+                    return true;
+                }
             }
         }
         return false;
@@ -854,7 +856,7 @@
         pw.print("  mDataActivity=");
         pw.println(mDataActivity);
         pw.print("  mServiceState=");
-        pw.println(mServiceState.toString());
+        pw.println(mServiceState);
         pw.print("  mNetworkName=");
         pw.println(mNetworkName);
         pw.print("  mNetworkNameDefault=");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
index 5b5801d..ffb45ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java
@@ -69,6 +69,7 @@
 import com.android.systemui.statusbar.*;
 import com.android.systemui.statusbar.policy.BatteryController;
 import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CompatModeButton;
 import com.android.systemui.statusbar.policy.LocationController;
 import com.android.systemui.statusbar.policy.NetworkController;
 import com.android.systemui.statusbar.policy.Prefs;
@@ -963,6 +964,10 @@
 
         // See above re: lights-out policy for legacy apps.
         if (visible) setLightsOn(true);
+
+        // XXX: HACK: not sure if this is the best way to catch a new activity that might require a
+        // change in compatibility features, but it's a start.
+        ((CompatModeButton) mBarContents.findViewById(R.id.compat_button)).refresh();
     }
 
     public void setImeWindowStatus(IBinder token, int vis, int backDisposition) {
diff --git a/packages/VpnDialogs/Android.mk b/packages/VpnDialogs/Android.mk
new file mode 100644
index 0000000..89f010a
--- /dev/null
+++ b/packages/VpnDialogs/Android.mk
@@ -0,0 +1,27 @@
+#
+# 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := VpnDialogs
+
+include $(BUILD_PACKAGE)
diff --git a/packages/VpnDialogs/AndroidManifest.xml b/packages/VpnDialogs/AndroidManifest.xml
new file mode 100644
index 0000000..4e6784c
--- /dev/null
+++ b/packages/VpnDialogs/AndroidManifest.xml
@@ -0,0 +1,22 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.vpndialogs">
+
+    <application android:label="VpnDialogs">
+        <activity android:name=".ConfirmDialog"
+                android:theme="@style/transparent">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".ManageDialog"
+                android:theme="@style/transparent"
+                android:noHistory="true">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+        </activity>
+    </application>
+</manifest>
diff --git a/packages/VpnDialogs/res/layout/confirm.xml b/packages/VpnDialogs/res/layout/confirm.xml
new file mode 100644
index 0000000..249b6e6
--- /dev/null
+++ b/packages/VpnDialogs/res/layout/confirm.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content">
+
+    <ImageView android:id="@+id/icon"
+            android:layout_width="@android:dimen/app_icon_size"
+            android:layout_height="@android:dimen/app_icon_size"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentLeft="true"
+            android:layout_marginRight="1mm"/>
+
+    <TextView android:id="@+id/warning"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentRight="true"
+            android:layout_below="@id/icon"
+            android:padding="2mm"
+            android:text="@string/warning"
+            android:textSize="18sp"/>
+
+    <TextView android:id="@+id/prompt"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentTop="true"
+            android:layout_alignParentRight="true"
+            android:layout_toRightOf="@id/icon"
+            android:layout_above="@id/warning"
+            android:gravity="center_vertical"
+            android:textSize="20sp"/>
+
+    <CheckBox android:id="@+id/check"
+            android:layout_width="fill_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_alignParentRight="true"
+            android:layout_below="@id/warning"
+            android:text="@string/accept"
+
+
+            android:textSize="18sp"
+            android:checked="false"/>
+
+</RelativeLayout>
diff --git a/packages/VpnDialogs/res/layout/manage.xml b/packages/VpnDialogs/res/layout/manage.xml
new file mode 100644
index 0000000..330b8e3
--- /dev/null
+++ b/packages/VpnDialogs/res/layout/manage.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:stretchColumns="0,1">
+
+    <TableRow>
+        <TextView android:text="@string/session" style="@style/label"/>
+        <TextView android:id="@+id/session" style="@style/value"/>
+    </TableRow>
+
+    <TableRow>
+        <TextView android:text="@string/duration" style="@style/label"/>
+        <TextView android:id="@+id/duration" style="@style/value"/>
+    </TableRow>
+
+    <TableRow>
+        <TextView android:text="@string/data_transmitted" style="@style/label"/>
+        <TextView android:id="@+id/data_transmitted" style="@style/value"/>
+    </TableRow>
+
+    <TableRow>
+        <TextView android:text="@string/data_received" style="@style/label"/>
+        <TextView android:id="@+id/data_received" style="@style/value"/>
+    </TableRow>
+
+</TableLayout>
diff --git a/packages/VpnDialogs/res/values/strings.xml b/packages/VpnDialogs/res/values/strings.xml
new file mode 100644
index 0000000..8186e26
--- /dev/null
+++ b/packages/VpnDialogs/res/values/strings.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+
+    <string name="prompt"><xliff:g id="app">%s</xliff:g>
+        attempts to create a VPN connection.
+    </string>
+
+    <string name="warning">By proceeding, you are giving the application
+        permission to intercept all network traffic.
+        <b>Do NOT accept unless you trust the application.</b> Otherwise,
+        you run the risk of having your data compromised by a malicious
+        software.
+    </string>
+
+    <string name="accept">I trust this application.</string>
+
+    <string name="configure">Configure</string>
+    <string name="disconnect">Disconnect</string>
+
+    <string name="session">Session:</string>
+    <string name="duration">Duration:</string>
+    <string name="data_transmitted">Data Transmitted:</string>
+    <string name="data_received">Data Received:</string>
+
+    <string name="blank_value">--</string>
+    <string name="data_value_format">
+        <xliff:g id="number">%1$s</xliff:g> bytes /
+        <xliff:g id="number">%2$s</xliff:g> packets
+    </string>
+</resources>
diff --git a/packages/VpnDialogs/res/values/styles.xml b/packages/VpnDialogs/res/values/styles.xml
new file mode 100644
index 0000000..6eae218
--- /dev/null
+++ b/packages/VpnDialogs/res/values/styles.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+
+<resources>
+
+    <style name="transparent">
+        <item name="android:windowBackground">@android:color/transparent</item>
+        <item name="android:windowNoTitle">true</item>
+        <item name="android:windowIsFloating">true</item>
+    </style>
+
+    <style name="label">
+        <item name="android:gravity">center_vertical|right</item>
+        <item name="android:paddingRight">5mm</item>
+        <item name="android:textSize">16sp</item>
+    </style>
+
+    <style name="value">
+        <item name="android:gravity">center_vertical|left</item>
+        <item name="android:textSize">18sp</item>
+        <item name="android:textStyle">bold</item>
+        <item name="android:text">@string/blank_value</item>
+    </style>
+</resources>
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
new file mode 100644
index 0000000..8fa6c7a
--- /dev/null
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ConfirmDialog.java
@@ -0,0 +1,116 @@
+/*
+ * 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 com.android.vpndialogs;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class ConfirmDialog extends Activity implements CompoundButton.OnCheckedChangeListener,
+        DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+    private static final String TAG = "VpnConfirm";
+
+    private String mPackageName;
+
+    private ConnectivityManager mService;
+
+    private AlertDialog mDialog;
+    private Button mButton;
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        try {
+            mPackageName = getCallingPackage();
+            mService = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+
+            if (mPackageName.equals(mService.prepareVpn(null))) {
+                setResult(RESULT_OK);
+                finish();
+                return;
+            }
+
+            PackageManager pm = getPackageManager();
+            ApplicationInfo app = pm.getApplicationInfo(mPackageName, 0);
+
+            View view = View.inflate(this, R.layout.confirm, null);
+            ((ImageView) view.findViewById(R.id.icon)).setImageDrawable(app.loadIcon(pm));
+            ((TextView) view.findViewById(R.id.prompt)).setText(
+                    getString(R.string.prompt, app.loadLabel(pm)));
+            ((CompoundButton) view.findViewById(R.id.check)).setOnCheckedChangeListener(this);
+
+            mDialog = new AlertDialog.Builder(this)
+                    .setIcon(android.R.drawable.ic_dialog_alert)
+                    .setTitle(android.R.string.dialog_alert_title)
+                    .setView(view)
+                    .setPositiveButton(android.R.string.ok, this)
+                    .setNegativeButton(android.R.string.cancel, this)
+                    .setCancelable(false)
+                    .create();
+            mDialog.setOnDismissListener(this);
+            mDialog.show();
+
+            mButton = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
+            mButton.setEnabled(false);
+        } catch (Exception e) {
+            Log.e(TAG, "onResume", e);
+            finish();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mDialog != null) {
+            mDialog.setOnDismissListener(null);
+            mDialog.dismiss();
+        }
+    }
+
+    @Override
+    public void onCheckedChanged(CompoundButton button, boolean checked) {
+        mButton.setEnabled(checked);
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        try {
+            if (which == AlertDialog.BUTTON_POSITIVE &&
+                    mPackageName.equals(mService.prepareVpn(mPackageName))) {
+                setResult(RESULT_OK);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "onClick", e);
+        }
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        finish();
+    }
+}
diff --git a/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
new file mode 100644
index 0000000..853e625
--- /dev/null
+++ b/packages/VpnDialogs/src/com/android/vpndialogs/ManageDialog.java
@@ -0,0 +1,183 @@
+/*
+ * 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 com.android.vpndialogs;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.os.Handler;
+import android.os.Message;
+import android.os.SystemClock;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+
+public class ManageDialog extends Activity implements
+        DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+    private static final String TAG = "VpnManage";
+
+    private String mPackageName;
+    private String mInterfaceName;
+    private long mStartTime;
+
+    private ConnectivityManager mService;
+
+    private AlertDialog mDialog;
+    private TextView mDuration;
+    private TextView mDataTransmitted;
+    private TextView mDataReceived;
+
+    private Updater mUpdater;
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        try {
+            Intent intent = getIntent();
+            // TODO: Move constants into VpnBuilder.
+            mPackageName = intent.getStringExtra("packageName");
+            mInterfaceName = intent.getStringExtra("interfaceName");
+            mStartTime = intent.getLongExtra("startTime", 0);
+
+            mService = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
+
+            PackageManager pm = getPackageManager();
+            ApplicationInfo app = pm.getApplicationInfo(mPackageName, 0);
+
+            View view = View.inflate(this, R.layout.manage, null);
+            String session = intent.getStringExtra("session");
+            if (session != null) {
+                ((TextView) view.findViewById(R.id.session)).setText(session);
+            }
+            mDuration = (TextView) view.findViewById(R.id.duration);
+            mDataTransmitted = (TextView) view.findViewById(R.id.data_transmitted);
+            mDataReceived = (TextView) view.findViewById(R.id.data_received);
+
+            mDialog = new AlertDialog.Builder(this)
+                    .setIcon(app.loadIcon(pm))
+                    .setTitle(app.loadLabel(pm))
+                    .setView(view)
+                    .setPositiveButton(R.string.configure, this)
+                    .setNeutralButton(R.string.disconnect, this)
+                    .setNegativeButton(android.R.string.cancel, this)
+                    .create();
+            mDialog.setOnDismissListener(this);
+            mDialog.show();
+
+            mUpdater = new Updater();
+            mUpdater.sendEmptyMessage(0);
+        } catch (Exception e) {
+            Log.e(TAG, "onResume", e);
+            finish();
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        if (mDialog != null) {
+            mDialog.setOnDismissListener(null);
+            mDialog.dismiss();
+        }
+    }
+
+    @Override
+    public void onClick(DialogInterface dialog, int which) {
+        try {
+            if (which == AlertDialog.BUTTON_POSITIVE) {
+                Intent intent = new Intent(Intent.ACTION_MAIN);
+                intent.setPackage(mPackageName);
+                startActivity(intent);
+            } else if (which == AlertDialog.BUTTON_NEUTRAL) {
+                mService.prepareVpn(mPackageName);
+            }
+        } catch (Exception e) {
+            Log.e(TAG, "onClick", e);
+        }
+    }
+
+    @Override
+    public void onDismiss(DialogInterface dialog) {
+        finish();
+    }
+
+    private class Updater extends Handler {
+        public void handleMessage(Message message) {
+            removeMessages(0);
+
+            if (mDialog.isShowing()) {
+                if (mStartTime != 0) {
+                    long seconds = (SystemClock.elapsedRealtime() - mStartTime) / 1000;
+                    mDuration.setText(String.format("%02d:%02d:%02d",
+                            seconds / 3600, seconds / 60 % 60, seconds % 60));
+                }
+
+                String[] numbers = getStatistics();
+                if (numbers != null) {
+                    // [1] and [2] are received data in bytes and packets.
+                    mDataReceived.setText(getString(R.string.data_value_format,
+                            numbers[1], numbers[2]));
+
+                    // [9] and [10] are transmitted data in bytes and packets.
+                    mDataTransmitted.setText(getString(R.string.data_value_format,
+                            numbers[9], numbers[10]));
+                }
+                sendEmptyMessageDelayed(0, 1000);
+            }
+        }
+    }
+
+    private String[] getStatistics() {
+        DataInputStream in = null;
+        try {
+            // See dev_seq_printf_stats() in net/core/dev.c.
+            in = new DataInputStream(new FileInputStream("/proc/net/dev"));
+            String prefix = mInterfaceName + ':';
+
+            while (true) {
+                String line = in.readLine().trim();
+                if (line.startsWith(prefix)) {
+                    String[] numbers = line.substring(prefix.length()).split(" +");
+                    if (numbers.length == 17) {
+                        return numbers;
+                    }
+                    break;
+                }
+            }
+        } catch (Exception e) {
+            // ignore
+        } finally {
+            try {
+                in.close();
+            } catch (Exception e) {
+                // ignore
+            }
+        }
+        return null;
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
index 5ed67a9..72209f6 100644
--- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
+++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java
@@ -480,11 +480,11 @@
     }
 
     /**
-     * Report that the user succesfully entered the sim pin so we
+     * Report that the user succesfully entered the sim pin or puk so we
      * have the information earlier than waiting for the intent
      * broadcast from the telephony code.
      */
-    public void reportSimPinUnlocked() {
+    public void reportSimUnlocked() {
         mSimState = IccCard.State.READY;
     }
 
diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
index 2fda3aa..874acd0 100644
--- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
+++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java
@@ -116,6 +116,11 @@
         SimPin,
 
         /**
+         * Unlock by entering a sim puk.
+         */
+        SimPuk,
+
+        /**
          * Unlock by entering an account's login and password.
          */
         Account,
@@ -222,8 +227,10 @@
             public void goToUnlockScreen() {
                 final IccCard.State simState = mUpdateMonitor.getSimState();
                 if (stuckOnLockScreenBecauseSimMissing()
-                         || (simState == IccCard.State.PUK_REQUIRED)){
-                    // stuck on lock screen when sim missing or puk'd
+                         || (simState == IccCard.State.PUK_REQUIRED
+                             && !mLockPatternUtils.isPukUnlockScreenEnable())){
+                    // stuck on lock screen when sim missing or
+                    // puk'd but puk unlock screen is disabled
                     return;
                 }
                 if (!isSecure()) {
@@ -522,8 +529,10 @@
                 secure = mLockPatternUtils.isLockPatternEnabled();
                 break;
             case SimPin:
-                secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED
-                            || mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
+                secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED;
+                break;
+            case SimPuk:
+                secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
                 break;
             case Account:
                 secure = true;
@@ -592,6 +601,10 @@
 
     View createUnlockScreenFor(UnlockMode unlockMode) {
         View unlockView = null;
+
+        if (DEBUG) Log.d(TAG,
+                "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
+
         if (unlockMode == UnlockMode.Pattern) {
             PatternUnlockScreen view = new PatternUnlockScreen(
                     mContext,
@@ -600,10 +613,15 @@
                     mUpdateMonitor,
                     mKeyguardScreenCallback,
                     mUpdateMonitor.getFailedAttempts());
-            if (DEBUG) Log.d(TAG,
-                "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
             view.setEnableFallback(mEnableFallback);
             unlockView = view;
+        } else if (unlockMode == UnlockMode.SimPuk) {
+            unlockView = new SimPukUnlockScreen(
+                    mContext,
+                    mConfiguration,
+                    mUpdateMonitor,
+                    mKeyguardScreenCallback,
+                    mLockPatternUtils);
         } else if (unlockMode == UnlockMode.SimPin) {
             unlockView = new SimUnlockScreen(
                     mContext,
@@ -654,7 +672,9 @@
      */
     private Mode getInitialMode() {
         final IccCard.State simState = mUpdateMonitor.getSimState();
-        if (stuckOnLockScreenBecauseSimMissing() || (simState == IccCard.State.PUK_REQUIRED)) {
+        if (stuckOnLockScreenBecauseSimMissing() ||
+                (simState == IccCard.State.PUK_REQUIRED &&
+                        !mLockPatternUtils.isPukUnlockScreenEnable())) {
             return Mode.LockScreen;
         } else {
             // Show LockScreen first for any screen other than Pattern unlock.
@@ -676,8 +696,10 @@
     private UnlockMode getUnlockMode() {
         final IccCard.State simState = mUpdateMonitor.getSimState();
         UnlockMode currentMode;
-        if (simState == IccCard.State.PIN_REQUIRED || simState == IccCard.State.PUK_REQUIRED) {
+        if (simState == IccCard.State.PIN_REQUIRED) {
             currentMode = UnlockMode.SimPin;
+        } else if (simState == IccCard.State.PUK_REQUIRED) {
+            currentMode = UnlockMode.SimPuk;
         } else {
             final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality();
             switch (mode) {
diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java
index a9d5ce4..7331bda 100644
--- a/policy/src/com/android/internal/policy/impl/LockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/LockScreen.java
@@ -232,7 +232,7 @@
 
         /** {@inheritDoc} */
         public void onGrabbedStateChange(View v, int grabbedState) {
-            if (DBG) Log.v(TAG, "*** LockScreen accel is " 
+            if (DBG) Log.v(TAG, "*** LockScreen accel is "
                     + (mEnergyWave.isHardwareAccelerated() ? "on":"off"));
             // Don't poke the wake lock when returning to a state where the handle is
             // not grabbed since that can happen when the system (instead of the user)
@@ -579,10 +579,16 @@
                 mScreenLocked.setText(R.string.lockscreen_sim_puk_locked_instructions);
 
                 // layout
-                mScreenLocked.setVisibility(View.VISIBLE);
-                mEmergencyCallText.setVisibility(View.VISIBLE);
-                mEmergencyCallButton.setVisibility(View.VISIBLE);
-                disableUnlock();
+                if (mLockPatternUtils.isPukUnlockScreenEnable()) {
+                    mScreenLocked.setVisibility(View.INVISIBLE);
+                    mEmergencyCallText.setVisibility(View.GONE);
+                    enableUnlock();
+                } else {
+                    mScreenLocked.setVisibility(View.VISIBLE);
+                    mEmergencyCallText.setVisibility(View.VISIBLE);
+                    mEmergencyCallButton.setVisibility(View.VISIBLE);
+                    disableUnlock();
+                }
                 break;
         }
     }
diff --git a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
new file mode 100644
index 0000000..544bb3d
--- /dev/null
+++ b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java
@@ -0,0 +1,470 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.policy.impl;
+
+import android.app.Dialog;
+import android.app.ProgressDialog;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+
+import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.IccCard;
+import com.android.internal.widget.LockPatternUtils;
+
+import android.text.Editable;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.internal.R;
+
+/**
+ * Displays a dialer like interface to unlock the SIM PUK.
+ */
+public class SimPukUnlockScreen extends LinearLayout implements KeyguardScreen,
+    View.OnClickListener, KeyguardUpdateMonitor.InfoCallback {
+
+    private static final int DIGIT_PRESS_WAKE_MILLIS = 5000;
+
+    private final KeyguardUpdateMonitor mUpdateMonitor;
+    private final KeyguardScreenCallback mCallback;
+
+    private TextView mHeaderText;
+    private TextView mPukText;
+    private TextView mPinText;
+
+    private TextView mFocusedEntry;
+
+    private TextView mOkButton;
+    private Button mEmergencyCallButton;
+
+    private View mDelPukButton;
+    private View mDelPinButton;
+
+    private ProgressDialog mSimUnlockProgressDialog = null;
+
+    private LockPatternUtils mLockPatternUtils;
+
+    private int mCreationOrientation;
+
+    private int mKeyboardHidden;
+
+    private static final char[] DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+    public SimPukUnlockScreen(Context context, Configuration configuration,
+            KeyguardUpdateMonitor updateMonitor, KeyguardScreenCallback callback,
+            LockPatternUtils lockpatternutils) {
+        super(context);
+        mUpdateMonitor = updateMonitor;
+        mCallback = callback;;
+
+        mCreationOrientation = configuration.orientation;
+        mKeyboardHidden = configuration.hardKeyboardHidden;
+        mLockPatternUtils = lockpatternutils;
+
+        LayoutInflater inflater = LayoutInflater.from(context);
+        if (mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO) {
+            inflater.inflate(
+                    R.layout.keyguard_screen_sim_puk_landscape, this, true);
+        } else {
+            inflater.inflate(
+                    R.layout.keyguard_screen_sim_puk_portrait, this, true);
+            new TouchInput();
+        }
+
+        mHeaderText = (TextView) findViewById(R.id.headerText);
+        mPukText = (TextView) findViewById(R.id.pukDisplay);
+        mPukText.setOnClickListener(new OnClickListener() {
+           public void onClick(View v) {
+               requestFocus(mPukText);
+               mCallback.pokeWakelock();
+           }
+        });
+        mPinText = (TextView) findViewById(R.id.pinDisplay);
+        mPinText.setOnClickListener(this);
+
+        mDelPukButton = findViewById(R.id.pukDel);
+        mDelPukButton.setOnClickListener(this);
+        mDelPinButton = findViewById(R.id.pinDel);
+        mDelPinButton.setOnClickListener(this);
+
+
+        mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
+        mOkButton = (TextView) findViewById(R.id.ok);
+
+        mHeaderText.setText(R.string.keyguard_password_enter_puk_code);
+        mPukText.setFocusable(false);
+        mPinText.setFocusable(false);
+        mOkButton.setOnClickListener(this);
+
+        requestFocus(mPukText);
+
+        if (mLockPatternUtils.isEmergencyCallCapable()) {
+            mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+            mEmergencyCallButton.setOnClickListener(this);
+        } else {
+            mEmergencyCallButton.setVisibility(View.GONE);
+        }
+
+        setFocusableInTouchMode(true);
+    }
+
+    private void requestFocus(TextView entry) {
+        mFocusedEntry = entry;
+        mFocusedEntry.setText("");
+    }
+
+    /** {@inheritDoc} */
+    public boolean needsInput() {
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    public void onPause() {
+
+    }
+
+    /** {@inheritDoc} */
+    public void onResume() {
+        // start fresh
+        mHeaderText.setText(R.string.keyguard_password_enter_puk_code);
+        requestFocus(mPukText);
+        mPinText.setText("");
+
+        if (mLockPatternUtils.isEmergencyCallCapable()) {
+            mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+        }
+    }
+
+    /** {@inheritDoc} */
+    public void cleanUp() {
+        // dismiss the dialog.
+        if (mSimUnlockProgressDialog != null) {
+            mSimUnlockProgressDialog.dismiss();
+            mSimUnlockProgressDialog = null;
+        }
+        mUpdateMonitor.removeCallback(this);
+    }
+
+
+    /**
+     * Since the IPC can block, we want to run the request in a separate thread
+     * with a callback.
+     */
+    private abstract class CheckSimPuk extends Thread {
+
+        private final String mPin, mPuk;
+
+        protected CheckSimPuk(String puk, String pin) {
+            mPuk = puk;
+            mPin = pin;
+        }
+
+        abstract void onSimLockChangedResponse(boolean success);
+
+        @Override
+        public void run() {
+            try {
+                final boolean result = ITelephony.Stub.asInterface(ServiceManager
+                        .checkService("phone")).supplyPuk(mPuk, mPin);
+
+                post(new Runnable() {
+                    public void run() {
+                        onSimLockChangedResponse(result);
+                    }
+                });
+            } catch (RemoteException e) {
+                post(new Runnable() {
+                    public void run() {
+                        onSimLockChangedResponse(false);
+                    }
+                });
+            }
+        }
+    }
+
+    public void onClick(View v) {
+        if (v == mDelPukButton) {
+            final Editable digits = mPukText.getEditableText();
+            final int len = digits.length();
+            if (len > 0) {
+                digits.delete(len-1, len);
+            }
+            mCallback.pokeWakelock();
+        } else if (v == mDelPinButton) {
+            final Editable digits = mPinText.getEditableText();
+            final int len = digits.length();
+            if (len > 0) {
+                digits.delete(len-1, len);
+            }
+            mCallback.pokeWakelock();
+        } else if (v == mPinText) {
+            requestFocus(mPinText);
+            mCallback.pokeWakelock();
+        } else if (v == mEmergencyCallButton) {
+            mCallback.takeEmergencyCallAction();
+        } else if (v == mOkButton) {
+            checkPuk();
+        }
+    }
+
+    private Dialog getSimUnlockProgressDialog() {
+        if (mSimUnlockProgressDialog == null) {
+            mSimUnlockProgressDialog = new ProgressDialog(mContext);
+            mSimUnlockProgressDialog.setMessage(
+                    mContext.getString(R.string.lockscreen_sim_unlock_progress_dialog_message));
+            mSimUnlockProgressDialog.setIndeterminate(true);
+            mSimUnlockProgressDialog.setCancelable(false);
+            mSimUnlockProgressDialog.getWindow().setType(
+                    WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
+            if (!mContext.getResources().getBoolean(
+                    com.android.internal.R.bool.config_sf_slowBlur)) {
+                mSimUnlockProgressDialog.getWindow().setFlags(
+                        WindowManager.LayoutParams.FLAG_BLUR_BEHIND,
+                        WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
+            }
+        }
+        return mSimUnlockProgressDialog;
+    }
+
+    private void checkPuk() {
+        // make sure that the puk is at least 8 digits long.
+        if (mPukText.getText().length() < 8) {
+            // otherwise, display a message to the user, and don't submit.
+            mHeaderText.setText(R.string.invalidPuk);
+            mPukText.setText("");
+            mCallback.pokeWakelock();
+            return;
+        }
+
+        if (mPinText.getText().length() < 4
+                || mPinText.getText().length() > 8) {
+            // otherwise, display a message to the user, and don't submit.
+            mHeaderText.setText(R.string.invalidPin);
+            mPinText.setText("");
+            mCallback.pokeWakelock();
+            return;
+        }
+
+        getSimUnlockProgressDialog().show();
+
+        new CheckSimPuk(mPukText.getText().toString(),
+                mPinText.getText().toString()) {
+            void onSimLockChangedResponse(boolean success) {
+                if (mSimUnlockProgressDialog != null) {
+                    mSimUnlockProgressDialog.hide();
+                }
+                if (success) {
+                    // before closing the keyguard, report back that
+                    // the sim is unlocked so it knows right away
+                    mUpdateMonitor.reportSimUnlocked();
+                    mCallback.goToUnlockScreen();
+                } else {
+                    mHeaderText.setText(R.string.badPuk);
+                    mPukText.setText("");
+                    mPinText.setText("");
+                }
+                mCallback.pokeWakelock();
+            }
+        }.start();
+    }
+
+
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        if (keyCode == KeyEvent.KEYCODE_BACK) {
+            mCallback.goToLockScreen();
+            return true;
+        }
+        final char match = event.getMatch(DIGITS);
+        if (match != 0) {
+            reportDigit(match - '0');
+            return true;
+        }
+        if (keyCode == KeyEvent.KEYCODE_DEL) {
+            mFocusedEntry.onKeyDown(keyCode, event);
+            final Editable digits = mFocusedEntry.getEditableText();
+            final int len = digits.length();
+            if (len > 0) {
+                digits.delete(len-1, len);
+            }
+            mCallback.pokeWakelock();
+            return true;
+        }
+
+        if (keyCode == KeyEvent.KEYCODE_ENTER) {
+            checkPuk();
+            return true;
+        }
+
+        return false;
+    }
+
+    private void reportDigit(int digit) {
+        mFocusedEntry.append(Integer.toString(digit));
+    }
+
+    void updateConfiguration() {
+        Configuration newConfig = getResources().getConfiguration();
+        if (newConfig.orientation != mCreationOrientation) {
+            mCallback.recreateMe(newConfig);
+        } else if (newConfig.hardKeyboardHidden != mKeyboardHidden) {
+            mKeyboardHidden = newConfig.hardKeyboardHidden;
+            final boolean isKeyboardOpen =
+                (mKeyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO);
+            if (mUpdateMonitor.isKeyguardBypassEnabled() && isKeyboardOpen) {
+                mCallback.goToUnlockScreen();
+            }
+        }
+
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        updateConfiguration();
+    }
+
+    /** {@inheritDoc} */
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        updateConfiguration();
+    }
+
+    /**
+     * Helper class to handle input from touch dialer.  Only relevant when
+     * the keyboard is shut.
+     */
+    private class TouchInput implements View.OnClickListener {
+        private TextView mZero;
+        private TextView mOne;
+        private TextView mTwo;
+        private TextView mThree;
+        private TextView mFour;
+        private TextView mFive;
+        private TextView mSix;
+        private TextView mSeven;
+        private TextView mEight;
+        private TextView mNine;
+        private TextView mCancelButton;
+
+        private TouchInput() {
+            mZero = (TextView) findViewById(R.id.zero);
+            mOne = (TextView) findViewById(R.id.one);
+            mTwo = (TextView) findViewById(R.id.two);
+            mThree = (TextView) findViewById(R.id.three);
+            mFour = (TextView) findViewById(R.id.four);
+            mFive = (TextView) findViewById(R.id.five);
+            mSix = (TextView) findViewById(R.id.six);
+            mSeven = (TextView) findViewById(R.id.seven);
+            mEight = (TextView) findViewById(R.id.eight);
+            mNine = (TextView) findViewById(R.id.nine);
+            mCancelButton = (TextView) findViewById(R.id.cancel);
+
+            mZero.setText("0");
+            mOne.setText("1");
+            mTwo.setText("2");
+            mThree.setText("3");
+            mFour.setText("4");
+            mFive.setText("5");
+            mSix.setText("6");
+            mSeven.setText("7");
+            mEight.setText("8");
+            mNine.setText("9");
+
+            mZero.setOnClickListener(this);
+            mOne.setOnClickListener(this);
+            mTwo.setOnClickListener(this);
+            mThree.setOnClickListener(this);
+            mFour.setOnClickListener(this);
+            mFive.setOnClickListener(this);
+            mSix.setOnClickListener(this);
+            mSeven.setOnClickListener(this);
+            mEight.setOnClickListener(this);
+            mNine.setOnClickListener(this);
+            mCancelButton.setOnClickListener(this);
+        }
+
+
+        public void onClick(View v) {
+            if (v == mCancelButton) {
+                mCallback.goToLockScreen();
+                return;
+            }
+
+            final int digit = checkDigit(v);
+            if (digit >= 0) {
+                mCallback.pokeWakelock(DIGIT_PRESS_WAKE_MILLIS);
+                reportDigit(digit);
+            }
+        }
+
+        private int checkDigit(View v) {
+            int digit = -1;
+            if (v == mZero) {
+                digit = 0;
+            } else if (v == mOne) {
+                digit = 1;
+            } else if (v == mTwo) {
+                digit = 2;
+            } else if (v == mThree) {
+                digit = 3;
+            } else if (v == mFour) {
+                digit = 4;
+            } else if (v == mFive) {
+                digit = 5;
+            } else if (v == mSix) {
+                digit = 6;
+            } else if (v == mSeven) {
+                digit = 7;
+            } else if (v == mEight) {
+                digit = 8;
+            } else if (v == mNine) {
+                digit = 9;
+            }
+            return digit;
+        }
+    }
+
+    public void onPhoneStateChanged(String newState) {
+        if (mLockPatternUtils.isEmergencyCallCapable()) {
+            mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+        }
+    }
+
+    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
+
+    }
+
+    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {
+
+    }
+
+    public void onRingerModeChanged(int state) {
+
+    }
+
+    public void onTimeChanged() {
+
+    }
+}
diff --git a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
index 486e7aa..7255c27 100644
--- a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
+++ b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java
@@ -92,16 +92,17 @@
         mBackSpaceButton = findViewById(R.id.backspace);
         mBackSpaceButton.setOnClickListener(this);
 
-        mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
-        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
         mOkButton = (TextView) findViewById(R.id.ok);
 
         mHeaderText.setText(R.string.keyguard_password_enter_pin_code);
         mPinText.setFocusable(false);
 
-        mEmergencyCallButton.setOnClickListener(this);
         mOkButton.setOnClickListener(this);
 
+        mEmergencyCallButton = (Button) findViewById(R.id.emergencyCall);
+        mEmergencyCallButton.setOnClickListener(this);
+        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+
         setFocusableInTouchMode(true);
     }
 
@@ -229,7 +230,7 @@
                 if (success) {
                     // before closing the keyguard, report back that
                     // the sim is unlocked so it knows right away
-                    mUpdateMonitor.reportSimPinUnlocked();
+                    mUpdateMonitor.reportSimUnlocked();
                     mCallback.goToUnlockScreen();
                 } else {
                     mHeaderText.setText(R.string.keyguard_password_wrong_pin_code);
@@ -291,9 +292,8 @@
                 mCallback.goToUnlockScreen();
             }
         }
-        
     }
-    
+
     @Override
     protected void onAttachedToWindow() {
         super.onAttachedToWindow();
@@ -403,7 +403,7 @@
     }
 
     public void onPhoneStateChanged(String newState) {
-        mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
+            mLockPatternUtils.updateEmergencyCallButtonState(mEmergencyCallButton);
     }
 
     public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index f806624..d4a8ddb 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -53,13 +53,6 @@
 #include <media/EffectVisualizerApi.h>
 
 // ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
-// ----------------------------------------------------------------------------
 
 
 namespace android {
diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp
index ef8d957..dd91788 100644
--- a/services/audioflinger/AudioPolicyService.cpp
+++ b/services/audioflinger/AudioPolicyService.cpp
@@ -39,13 +39,6 @@
 #include <hardware/audio_policy.h>
 #include <hardware/audio_policy_hal.h>
 
-// ----------------------------------------------------------------------------
-// the sim build doesn't have gettid
-
-#ifndef HAVE_GETTID
-# define gettid getpid
-#endif
-
 namespace android {
 
 static const char *kDeadlockedString = "AudioPolicyService may be deadlocked\n";
diff --git a/services/camera/libcameraservice/CameraHardwareInterface.h b/services/camera/libcameraservice/CameraHardwareInterface.h
index f9fa30e..7a18831 100644
--- a/services/camera/libcameraservice/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/CameraHardwareInterface.h
@@ -438,18 +438,23 @@
     }
 
     static void __data_cb(int32_t msg_type,
-                          const camera_memory_t *data,
+                          const camera_memory_t *data, unsigned int index,
                           void *user)
     {
         LOGV("%s", __FUNCTION__);
         CameraHardwareInterface *__this =
                 static_cast<CameraHardwareInterface *>(user);
         sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
-        __this->mDataCb(msg_type, mem, __this->mCbUser);
+        if (index >= mem->mNumBufs) {
+            LOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+                 index, mem->mNumBufs);
+            return;
+        }
+        __this->mDataCb(msg_type, mem->mBuffers[index], __this->mCbUser);
     }
 
     static void __data_cb_timestamp(nsecs_t timestamp, int32_t msg_type,
-                             const camera_memory_t *data,
+                             const camera_memory_t *data, unsigned index,
                              void *user)
     {
         LOGV("%s", __FUNCTION__);
@@ -459,38 +464,85 @@
         // drop all references, it will be destroyed (as well as the enclosed
         // MemoryHeapBase.
         sp<CameraHeapMemory> mem(static_cast<CameraHeapMemory *>(data->handle));
-        __this->mDataCbTimestamp(timestamp, msg_type, mem, __this->mCbUser);
+        if (index >= mem->mNumBufs) {
+            LOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+                 index, mem->mNumBufs);
+            return;
+        }
+        __this->mDataCbTimestamp(timestamp, msg_type, mem->mBuffers[index], __this->mCbUser);
     }
 
     // This is a utility class that combines a MemoryHeapBase and a MemoryBase
     // in one.  Since we tend to use them in a one-to-one relationship, this is
     // handy.
 
-    class CameraHeapMemory : public MemoryBase {
+    class CameraHeapMemory : public RefBase {
     public:
-        CameraHeapMemory(size_t size) :
-            MemoryBase(new MemoryHeapBase(size), 0, size)
+        CameraHeapMemory(int fd, size_t buf_size, uint_t num_buffers = 1) :
+                         mBufSize(buf_size),
+                         mNumBufs(num_buffers)
         {
-            handle.data = getHeap()->base();
-            handle.size = size;
-            handle.handle = this;
+            mHeap = new MemoryHeapBase(fd, buf_size * num_buffers);
+            commonInitialization();
         }
 
+        CameraHeapMemory(size_t buf_size, uint_t num_buffers = 1) :
+                         mBufSize(buf_size),
+                         mNumBufs(num_buffers)
+        {
+            mHeap = new MemoryHeapBase(buf_size * num_buffers);
+            commonInitialization();
+        }
+
+        void commonInitialization()
+        {
+            handle.data = mHeap->base();
+            handle.size = mBufSize * mNumBufs;
+            handle.handle = this;
+
+            mBuffers = new sp<MemoryBase>[mNumBufs];
+            for (uint_t i = 0; i < mNumBufs; i++)
+                mBuffers[i] = new MemoryBase(mHeap,
+                                             i * mBufSize,
+                                             mBufSize);
+
+            handle.release = __put_memory;
+        }
+
+        virtual ~CameraHeapMemory()
+        {
+            delete [] mBuffers;
+        }
+
+        size_t mBufSize;
+        uint_t mNumBufs;
+        sp<MemoryHeapBase> mHeap;
+        sp<MemoryBase> *mBuffers;
+
         camera_memory_t handle;
     };
 
-    static camera_memory_t* __get_memory(size_t size,
-                                    void *user __attribute__((unused)))
+    static camera_memory_t* __get_memory(int fd, size_t buf_size, uint_t num_bufs,
+                                         void *user __attribute__((unused)))
     {
-        // We allocate the object here, but we do not assign it to a strong
-        // pointer yet.  The HAL will pass it back to us via the data callback
-        // or the data-timestamp callback, and from there on we will wrap it
-        // within a strong pointer.
-
-        CameraHeapMemory *mem = new CameraHeapMemory(size);
+        CameraHeapMemory *mem;
+        if (fd < 0)
+            mem = new CameraHeapMemory(buf_size, num_bufs);
+        else
+            mem = new CameraHeapMemory(fd, buf_size, num_bufs);
+        mem->incStrong(mem);
         return &mem->handle;
     }
 
+    static void __put_memory(camera_memory_t *data)
+    {
+        if (!data)
+            return;
+
+        CameraHeapMemory *mem = static_cast<CameraHeapMemory *>(data->handle);
+        mem->decStrong(mem);
+    }
+
     static ANativeWindow *__to_anw(void *user)
     {
         CameraHardwareInterface *__this =
@@ -541,7 +593,7 @@
     static int __set_buffer_count(struct preview_stream_ops* w, int count)
     {
         ANativeWindow *a = anw(w);
-	return native_window_set_buffer_count(a, count);
+        return native_window_set_buffer_count(a, count);
     }
 
     static int __set_buffers_geometry(struct preview_stream_ops* w,
diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp
index c42e3ab..fcc6198 100644
--- a/services/input/InputReader.cpp
+++ b/services/input/InputReader.cpp
@@ -200,23 +200,6 @@
     return edgeFlags;
 }
 
-static void clampPositionUsingPointerBounds(
-        const sp<PointerControllerInterface>& pointerController, float* x, float* y) {
-    float minX, minY, maxX, maxY;
-    if (pointerController->getBounds(&minX, &minY, &maxX, &maxY)) {
-        if (*x < minX) {
-            *x = minX;
-        } else if (*x > maxX) {
-            *x = maxX;
-        }
-        if (*y < minY) {
-            *y = minY;
-        } else if (*y > maxY) {
-            *y = maxY;
-        }
-    }
-}
-
 static float calculateCommonVector(float a, float b) {
     if (a > 0 && b > 0) {
         return a < b ? a : b;
@@ -787,8 +770,8 @@
             mConfig.pointerGestureTapSlop);
     dump.appendFormat(INDENT3 "MultitouchSettleInterval: %0.1fms\n",
             mConfig.pointerGestureMultitouchSettleInterval * 0.000001f);
-    dump.appendFormat(INDENT3 "MultitouchMinSpeed: %0.1fpx/s\n",
-            mConfig.pointerGestureMultitouchMinSpeed);
+    dump.appendFormat(INDENT3 "MultitouchMinDistance: %0.1fpx\n",
+            mConfig.pointerGestureMultitouchMinDistance);
     dump.appendFormat(INDENT3 "SwipeTransitionAngleCosine: %0.1f\n",
             mConfig.pointerGestureSwipeTransitionAngleCosine);
     dump.appendFormat(INDENT3 "SwipeMaxWidthRatio: %0.1f\n",
@@ -3509,11 +3492,18 @@
         cancelPreviousGesture = false;
     }
 
-    // Switch pointer presentation.
-    mPointerController->setPresentation(
-            mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
-                    ? PointerControllerInterface::PRESENTATION_SPOT
-                    : PointerControllerInterface::PRESENTATION_POINTER);
+    // Update the pointer presentation and spots.
+    if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
+        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT);
+        if (finishPreviousGesture || cancelPreviousGesture) {
+            mPointerController->clearSpots();
+        }
+        mPointerController->setSpots(mPointerGesture.currentGestureCoords,
+                mPointerGesture.currentGestureIdToIndex,
+                mPointerGesture.currentGestureIdBits);
+    } else {
+        mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER);
+    }
 
     // Show or hide the pointer if needed.
     switch (mPointerGesture.currentGestureMode) {
@@ -3712,12 +3702,6 @@
                 mPointerGesture.currentGestureIdBits.clear();
 
                 mPointerGesture.pointerVelocityControl.reset();
-
-                if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                    mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
-                    mPointerGesture.spotIdBits.clear();
-                    moveSpotsLocked();
-                }
                 return true;
             }
         }
@@ -3798,22 +3782,18 @@
     if (isQuietTime) {
         // Case 1: Quiet time. (QUIET)
 #if DEBUG_GESTURES
-        LOGD("Gestures: QUIET for next %0.3fms",
-                (mPointerGesture.quietTime + QUIET_INTERVAL - when) * 0.000001f);
+        LOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime
+                + mConfig->pointerGestureQuietInterval - when) * 0.000001f);
 #endif
-        *outFinishPreviousGesture = true;
+        if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) {
+            *outFinishPreviousGesture = true;
+        }
 
         mPointerGesture.activeGestureId = -1;
         mPointerGesture.currentGestureMode = PointerGesture::QUIET;
         mPointerGesture.currentGestureIdBits.clear();
 
         mPointerGesture.pointerVelocityControl.reset();
-
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-            mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
-            mPointerGesture.spotIdBits.clear();
-            moveSpotsLocked();
-        }
     } else if (isPointerDown(mCurrentTouch.buttonState)) {
         // Case 2: Button is pressed. (BUTTON_CLICK_OR_DRAG)
         // The pointer follows the active touch point.
@@ -3899,32 +3879,11 @@
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_X, x);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
         mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-            if (activeTouchId >= 0) {
-                // Collapse all spots into one point at the pointer location.
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_DRAG;
-                mPointerGesture.spotIdBits.clear();
-                for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
-                    uint32_t id = mCurrentTouch.pointers[i].id;
-                    mPointerGesture.spotIdBits.markBit(id);
-                    mPointerGesture.spotIdToIndex[id] = i;
-                    mPointerGesture.spotCoords[i] = mPointerGesture.currentGestureCoords[0];
-                }
-            } else {
-                // No fingers.  Generate a spot at the pointer location so the
-                // anchor appears to be pressed.
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_BUTTON_CLICK;
-                mPointerGesture.spotIdBits.clear();
-                mPointerGesture.spotIdBits.markBit(0);
-                mPointerGesture.spotIdToIndex[0] = 0;
-                mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
-            }
-            moveSpotsLocked();
-        }
     } else if (mCurrentTouch.pointerCount == 0) {
         // Case 3. No fingers down and button is not pressed. (NEUTRAL)
-        *outFinishPreviousGesture = true;
+        if (mPointerGesture.lastGestureMode != PointerGesture::NEUTRAL) {
+            *outFinishPreviousGesture = true;
+        }
 
         // Watch for taps coming out of HOVER or TAP_DRAG mode.
         // Checking for taps after TAP_DRAG allows us to detect double-taps.
@@ -3965,15 +3924,6 @@
                     mPointerGesture.currentGestureCoords[0].setAxisValue(
                             AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
 
-                    if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                        mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_TAP;
-                        mPointerGesture.spotIdBits.clear();
-                        mPointerGesture.spotIdBits.markBit(lastActiveTouchId);
-                        mPointerGesture.spotIdToIndex[lastActiveTouchId] = 0;
-                        mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
-                        moveSpotsLocked();
-                    }
-
                     tapped = true;
                 } else {
 #if DEBUG_GESTURES
@@ -3999,12 +3949,6 @@
             mPointerGesture.activeGestureId = -1;
             mPointerGesture.currentGestureMode = PointerGesture::NEUTRAL;
             mPointerGesture.currentGestureIdBits.clear();
-
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
-                mPointerGesture.spotIdBits.clear();
-                moveSpotsLocked();
-            }
         }
     } else if (mCurrentTouch.pointerCount == 1) {
         // Case 4. Exactly one finger down, button is not pressed. (HOVER or TAP_DRAG)
@@ -4067,7 +4011,9 @@
 #if DEBUG_GESTURES
             LOGD("Gestures: HOVER");
 #endif
-            *outFinishPreviousGesture = true;
+            if (mPointerGesture.lastGestureMode != PointerGesture::HOVER) {
+                *outFinishPreviousGesture = true;
+            }
             mPointerGesture.activeGestureId = 0;
             down = false;
         }
@@ -4094,16 +4040,6 @@
             mPointerGesture.tapX = x;
             mPointerGesture.tapY = y;
         }
-
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-            mPointerGesture.spotGesture = down ? PointerControllerInterface::SPOT_GESTURE_DRAG
-                    : PointerControllerInterface::SPOT_GESTURE_HOVER;
-            mPointerGesture.spotIdBits.clear();
-            mPointerGesture.spotIdBits.markBit(activeTouchId);
-            mPointerGesture.spotIdToIndex[activeTouchId] = 0;
-            mPointerGesture.spotCoords[0] = mPointerGesture.currentGestureCoords[0];
-            moveSpotsLocked();
-        }
     } else {
         // Case 5. At least two fingers down, button is not pressed. (PRESS, SWIPE or FREEFORM)
         // We need to provide feedback for each finger that goes down so we cannot wait
@@ -4131,8 +4067,8 @@
             // Reset the gesture.
 #if DEBUG_GESTURES
             LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, "
-                    "settle time remaining %0.3fms",
-                    (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
+                    "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
+                            + mConfig->pointerGestureMultitouchSettleInterval - when)
                             * 0.000001f);
 #endif
             *outCancelPreviousGesture = true;
@@ -4147,101 +4083,138 @@
             mPointerGesture.referenceIdBits.clear();
             mPointerGesture.pointerVelocityControl.reset();
 
-            if (settled && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS
-                    && mLastTouch.idBits.hasBit(mPointerGesture.activeTouchId)) {
-                // The spot is already visible and has settled, use it as the reference point
-                // for the gesture.  Other spots will be positioned relative to this one.
+            // Use the centroid and pointer location as the reference points for the gesture.
 #if DEBUG_GESTURES
-                LOGD("Gestures: Using active spot as reference for MULTITOUCH, "
-                        "settle time expired %0.3fms ago",
-                        (when - mPointerGesture.firstTouchTime - MULTITOUCH_SETTLE_INTERVAL)
-                                * 0.000001f);
+            LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
+                    "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime
+                            + mConfig->pointerGestureMultitouchSettleInterval - when)
+                            * 0.000001f);
 #endif
-                const PointerData& d = mLastTouch.pointers[mLastTouch.idToIndex[
-                        mPointerGesture.activeTouchId]];
-                mPointerGesture.referenceTouchX = d.x;
-                mPointerGesture.referenceTouchY = d.y;
-                const PointerCoords& c = mPointerGesture.spotCoords[mPointerGesture.spotIdToIndex[
-                        mPointerGesture.activeTouchId]];
-                mPointerGesture.referenceGestureX = c.getAxisValue(AMOTION_EVENT_AXIS_X);
-                mPointerGesture.referenceGestureY = c.getAxisValue(AMOTION_EVENT_AXIS_Y);
+            mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
+                    &mPointerGesture.referenceTouchY);
+            mPointerController->getPosition(&mPointerGesture.referenceGestureX,
+                    &mPointerGesture.referenceGestureY);
+        }
+
+        // Clear the reference deltas for fingers not yet included in the reference calculation.
+        for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value);
+                !idBits.isEmpty(); ) {
+            uint32_t id = idBits.firstMarkedBit();
+            idBits.clearBit(id);
+
+            mPointerGesture.referenceDeltas[id].dx = 0;
+            mPointerGesture.referenceDeltas[id].dy = 0;
+        }
+        mPointerGesture.referenceIdBits = mCurrentTouch.idBits;
+
+        // Add delta for all fingers and calculate a common movement delta.
+        float commonDeltaX = 0, commonDeltaY = 0;
+        BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
+        for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
+            bool first = (idBits == commonIdBits);
+            uint32_t id = idBits.firstMarkedBit();
+            idBits.clearBit(id);
+
+            const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
+            const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
+            PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+            delta.dx += cpd.x - lpd.x;
+            delta.dy += cpd.y - lpd.y;
+
+            if (first) {
+                commonDeltaX = delta.dx;
+                commonDeltaY = delta.dy;
             } else {
-                // Use the centroid and pointer location as the reference points for the gesture.
-#if DEBUG_GESTURES
-                LOGD("Gestures: Using centroid as reference for MULTITOUCH, "
-                        "settle time remaining %0.3fms",
-                        (mPointerGesture.firstTouchTime + MULTITOUCH_SETTLE_INTERVAL - when)
-                                * 0.000001f);
-#endif
-                mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX,
-                        &mPointerGesture.referenceTouchY);
-                mPointerController->getPosition(&mPointerGesture.referenceGestureX,
-                        &mPointerGesture.referenceGestureY);
+                commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
+                commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
             }
         }
 
+        // Consider transitions from PRESS to SWIPE or MULTITOUCH.
         if (mPointerGesture.currentGestureMode == PointerGesture::PRESS) {
-            float d;
-            if (mCurrentTouch.pointerCount > 2) {
-                // There are more than two pointers, switch to FREEFORM.
-#if DEBUG_GESTURES
-                LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
-                        mCurrentTouch.pointerCount);
-#endif
-                *outCancelPreviousGesture = true;
-                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
-            } else if (((d = distance(
-                    mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
-                    mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
-                            > mLocked.pointerGestureMaxSwipeWidth)) {
-                // There are two pointers but they are too far apart, switch to FREEFORM.
-#if DEBUG_GESTURES
-                LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
-                        d, mLocked.pointerGestureMaxSwipeWidth);
-#endif
-                *outCancelPreviousGesture = true;
-                mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
-            } else {
-                // There are two pointers.  Wait for both pointers to start moving
-                // before deciding whether this is a SWIPE or FREEFORM gesture.
-                uint32_t id1 = mCurrentTouch.pointers[0].id;
-                uint32_t id2 = mCurrentTouch.pointers[1].id;
+            float dist[MAX_POINTER_ID + 1];
+            int32_t distOverThreshold = 0;
+            for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
+                uint32_t id = idBits.firstMarkedBit();
+                idBits.clearBit(id);
 
-                float vx1, vy1, vx2, vy2;
-                mPointerGesture.velocityTracker.getVelocity(id1, &vx1, &vy1);
-                mPointerGesture.velocityTracker.getVelocity(id2, &vx2, &vy2);
+                PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
+                dist[id] = hypotf(delta.dx * mLocked.pointerGestureXZoomScale,
+                        delta.dy * mLocked.pointerGestureYZoomScale);
+                if (dist[id] > mConfig->pointerGestureMultitouchMinDistance) {
+                    distOverThreshold += 1;
+                }
+            }
 
-                float speed1 = hypotf(vx1, vy1);
-                float speed2 = hypotf(vx2, vy2);
-                if (speed1 >= mConfig->pointerGestureMultitouchMinSpeed
-                        && speed2 >= mConfig->pointerGestureMultitouchMinSpeed) {
-                    // Calculate the dot product of the velocity vectors.
-                    // When the vectors are oriented in approximately the same direction,
-                    // the angle betweeen them is near zero and the cosine of the angle
-                    // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
-                    float dot = vx1 * vx2 + vy1 * vy2;
-                    float cosine = dot / (speed1 * speed2); // denominator always > 0
-                    if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
-                        // Pointers are moving in the same direction.  Switch to SWIPE.
+            // Only transition when at least two pointers have moved further than
+            // the minimum distance threshold.
+            if (distOverThreshold >= 2) {
+                float d;
+                if (mCurrentTouch.pointerCount > 2) {
+                    // There are more than two pointers, switch to FREEFORM.
 #if DEBUG_GESTURES
-                        LOGD("Gestures: PRESS transitioned to SWIPE, "
-                                "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
-                                "cosine %0.3f >= %0.3f",
-                                speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
-                                cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+                    LOGD("Gestures: PRESS transitioned to FREEFORM, number of pointers %d > 2",
+                            mCurrentTouch.pointerCount);
 #endif
-                        mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
-                    } else {
-                        // Pointers are moving in different directions.  Switch to FREEFORM.
+                    *outCancelPreviousGesture = true;
+                    mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                } else if (((d = distance(
+                        mCurrentTouch.pointers[0].x, mCurrentTouch.pointers[0].y,
+                        mCurrentTouch.pointers[1].x, mCurrentTouch.pointers[1].y))
+                                > mLocked.pointerGestureMaxSwipeWidth)) {
+                    // There are two pointers but they are too far apart for a SWIPE,
+                    // switch to FREEFORM.
 #if DEBUG_GESTURES
-                        LOGD("Gestures: PRESS transitioned to FREEFORM, "
-                                "speed1 %0.3f >= %0.3f, speed2 %0.3f >= %0.3f, "
-                                "cosine %0.3f < %0.3f",
-                                speed1, MULTITOUCH_MIN_SPEED, speed2, MULTITOUCH_MIN_SPEED,
-                                cosine, SWIPE_TRANSITION_ANGLE_COSINE);
+                    LOGD("Gestures: PRESS transitioned to FREEFORM, distance %0.3f > %0.3f",
+                            d, mLocked.pointerGestureMaxSwipeWidth);
 #endif
-                        *outCancelPreviousGesture = true;
-                        mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                    *outCancelPreviousGesture = true;
+                    mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                } else {
+                    // There are two pointers.  Wait for both pointers to start moving
+                    // before deciding whether this is a SWIPE or FREEFORM gesture.
+                    uint32_t id1 = mCurrentTouch.pointers[0].id;
+                    uint32_t id2 = mCurrentTouch.pointers[1].id;
+                    float dist1 = dist[id1];
+                    float dist2 = dist[id2];
+                    if (dist1 >= mConfig->pointerGestureMultitouchMinDistance
+                            && dist2 >= mConfig->pointerGestureMultitouchMinDistance) {
+                        // Calculate the dot product of the displacement vectors.
+                        // When the vectors are oriented in approximately the same direction,
+                        // the angle betweeen them is near zero and the cosine of the angle
+                        // approches 1.0.  Recall that dot(v1, v2) = cos(angle) * mag(v1) * mag(v2).
+                        PointerGesture::Delta& delta1 = mPointerGesture.referenceDeltas[id1];
+                        PointerGesture::Delta& delta2 = mPointerGesture.referenceDeltas[id2];
+                        float dx1 = delta1.dx * mLocked.pointerGestureXZoomScale;
+                        float dy1 = delta1.dy * mLocked.pointerGestureYZoomScale;
+                        float dx2 = delta2.dx * mLocked.pointerGestureXZoomScale;
+                        float dy2 = delta2.dy * mLocked.pointerGestureYZoomScale;
+                        float dot = dx1 * dx2 + dy1 * dy2;
+                        float cosine = dot / (dist1 * dist2); // denominator always > 0
+                        if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) {
+                            // Pointers are moving in the same direction.  Switch to SWIPE.
+#if DEBUG_GESTURES
+                            LOGD("Gestures: PRESS transitioned to SWIPE, "
+                                    "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                    "cosine %0.3f >= %0.3f",
+                                    dist1, mConfig->pointerGestureMultitouchMinDistance,
+                                    dist2, mConfig->pointerGestureMultitouchMinDistance,
+                                    cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+#endif
+                            mPointerGesture.currentGestureMode = PointerGesture::SWIPE;
+                        } else {
+                            // Pointers are moving in different directions.  Switch to FREEFORM.
+#if DEBUG_GESTURES
+                            LOGD("Gestures: PRESS transitioned to FREEFORM, "
+                                    "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, "
+                                    "cosine %0.3f < %0.3f",
+                                    dist1, mConfig->pointerGestureMultitouchMinDistance,
+                                    dist2, mConfig->pointerGestureMultitouchMinDistance,
+                                    cosine, mConfig->pointerGestureSwipeTransitionAngleCosine);
+#endif
+                            *outCancelPreviousGesture = true;
+                            mPointerGesture.currentGestureMode = PointerGesture::FREEFORM;
+                        }
                     }
                 }
             }
@@ -4258,67 +4231,28 @@
             }
         }
 
-        // Clear the reference deltas for fingers not yet included in the reference calculation.
-        for (BitSet32 idBits(mCurrentTouch.idBits.value & ~mPointerGesture.referenceIdBits.value);
-                !idBits.isEmpty(); ) {
-            uint32_t id = idBits.firstMarkedBit();
-            idBits.clearBit(id);
-
-            mPointerGesture.referenceDeltas[id].dx = 0;
-            mPointerGesture.referenceDeltas[id].dy = 0;
-        }
-        mPointerGesture.referenceIdBits = mCurrentTouch.idBits;
-
-        // Move the reference points based on the overall group motion of the fingers.
-        // The objective is to calculate a vector delta that is common to the movement
-        // of all fingers.
-        BitSet32 commonIdBits(mLastTouch.idBits.value & mCurrentTouch.idBits.value);
-        if (!commonIdBits.isEmpty()) {
-            float commonDeltaX = 0, commonDeltaY = 0;
-            for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
-                bool first = (idBits == commonIdBits);
+        // Move the reference points based on the overall group motion of the fingers
+        // except in PRESS mode while waiting for a transition to occur.
+        if (mPointerGesture.currentGestureMode != PointerGesture::PRESS
+                && (commonDeltaX || commonDeltaY)) {
+            for (BitSet32 idBits(mPointerGesture.referenceIdBits); !idBits.isEmpty(); ) {
                 uint32_t id = idBits.firstMarkedBit();
                 idBits.clearBit(id);
 
-                const PointerData& cpd = mCurrentTouch.pointers[mCurrentTouch.idToIndex[id]];
-                const PointerData& lpd = mLastTouch.pointers[mLastTouch.idToIndex[id]];
                 PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
-                delta.dx += cpd.x - lpd.x;
-                delta.dy += cpd.y - lpd.y;
-
-                if (first) {
-                    commonDeltaX = delta.dx;
-                    commonDeltaY = delta.dy;
-                } else {
-                    commonDeltaX = calculateCommonVector(commonDeltaX, delta.dx);
-                    commonDeltaY = calculateCommonVector(commonDeltaY, delta.dy);
-                }
+                delta.dx = 0;
+                delta.dy = 0;
             }
 
-            if (commonDeltaX || commonDeltaY) {
-                for (BitSet32 idBits(commonIdBits); !idBits.isEmpty(); ) {
-                    uint32_t id = idBits.firstMarkedBit();
-                    idBits.clearBit(id);
+            mPointerGesture.referenceTouchX += commonDeltaX;
+            mPointerGesture.referenceTouchY += commonDeltaY;
 
-                    PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id];
-                    delta.dx = 0;
-                    delta.dy = 0;
-                }
+            commonDeltaX *= mLocked.pointerGestureXMovementScale;
+            commonDeltaY *= mLocked.pointerGestureYMovementScale;
+            mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
 
-                mPointerGesture.referenceTouchX += commonDeltaX;
-                mPointerGesture.referenceTouchY += commonDeltaY;
-
-                commonDeltaX *= mLocked.pointerGestureXMovementScale;
-                commonDeltaY *= mLocked.pointerGestureYMovementScale;
-                mPointerGesture.pointerVelocityControl.move(when, &commonDeltaX, &commonDeltaY);
-
-                mPointerGesture.referenceGestureX += commonDeltaX;
-                mPointerGesture.referenceGestureY += commonDeltaY;
-
-                clampPositionUsingPointerBounds(mPointerController,
-                        &mPointerGesture.referenceGestureX,
-                        &mPointerGesture.referenceGestureY);
-            }
+            mPointerGesture.referenceGestureX += commonDeltaX;
+            mPointerGesture.referenceGestureY += commonDeltaY;
         }
 
         // Report gestures.
@@ -4344,10 +4278,6 @@
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
                     mPointerGesture.referenceGestureY);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_PRESS;
-            }
         } else if (mPointerGesture.currentGestureMode == PointerGesture::SWIPE) {
             // SWIPE mode.
 #if DEBUG_GESTURES
@@ -4370,10 +4300,6 @@
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_Y,
                     mPointerGesture.referenceGestureY);
             mPointerGesture.currentGestureCoords[0].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_SWIPE;
-            }
         } else if (mPointerGesture.currentGestureMode == PointerGesture::FREEFORM) {
             // FREEFORM mode.
 #if DEBUG_GESTURES
@@ -4475,33 +4401,6 @@
                         "activeGestureId=%d", mPointerGesture.activeGestureId);
 #endif
             }
-
-            if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-                mPointerGesture.spotGesture = PointerControllerInterface::SPOT_GESTURE_FREEFORM;
-            }
-        }
-
-        // Update spot locations for PRESS, SWIPE and FREEFORM.
-        // We use the same calculation as we do to calculate the gesture pointers
-        // for FREEFORM so that the spots smoothly track gestures.
-        if (mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) {
-            mPointerGesture.spotIdBits.clear();
-            for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) {
-                uint32_t id = mCurrentTouch.pointers[i].id;
-                mPointerGesture.spotIdBits.markBit(id);
-                mPointerGesture.spotIdToIndex[id] = i;
-
-                float x = (mCurrentTouch.pointers[i].x - mPointerGesture.referenceTouchX)
-                        * mLocked.pointerGestureXZoomScale + mPointerGesture.referenceGestureX;
-                float y = (mCurrentTouch.pointers[i].y - mPointerGesture.referenceTouchY)
-                        * mLocked.pointerGestureYZoomScale + mPointerGesture.referenceGestureY;
-
-                mPointerGesture.spotCoords[i].clear();
-                mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_X, x);
-                mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_Y, y);
-                mPointerGesture.spotCoords[i].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
-            }
-            moveSpotsLocked();
         }
     }
 
@@ -4544,11 +4443,6 @@
     return true;
 }
 
-void TouchInputMapper::moveSpotsLocked() {
-    mPointerController->setSpots(mPointerGesture.spotGesture,
-            mPointerGesture.spotCoords, mPointerGesture.spotIdToIndex, mPointerGesture.spotIdBits);
-}
-
 void TouchInputMapper::dispatchMotion(nsecs_t when, uint32_t policyFlags, uint32_t source,
         int32_t action, int32_t flags, int32_t metaState, int32_t buttonState, int32_t edgeFlags,
         const PointerProperties* properties, const PointerCoords* coords,
diff --git a/services/input/InputReader.h b/services/input/InputReader.h
index 1d4ad87..55315f7 100644
--- a/services/input/InputReader.h
+++ b/services/input/InputReader.h
@@ -101,8 +101,8 @@
     nsecs_t pointerGestureMultitouchSettleInterval;
 
     // The transition from PRESS to SWIPE or FREEFORM gesture mode is made when
-    // both of the pointers are moving at least this fast.
-    float pointerGestureMultitouchMinSpeed; // in pixels per second
+    // at least two pointers have moved at least this far from their starting place.
+    float pointerGestureMultitouchMinDistance; // in pixels
 
     // The transition from PRESS to SWIPE gesture mode can only occur when the
     // cosine of the angle between the two vectors is greater than or equal to than this value
@@ -134,7 +134,7 @@
             filterTouchEvents(false),
             filterJumpyTouchEvents(false),
             virtualKeyQuietTime(0),
-            pointerVelocityControlParameters(1.0f, 80.0f, 400.0f, 4.0f),
+            pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f),
             wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f),
             pointerGestureQuietInterval(100 * 1000000LL), // 100 ms
             pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second
@@ -142,10 +142,10 @@
             pointerGestureTapDragInterval(150 * 1000000LL), // 150 ms
             pointerGestureTapSlop(10.0f), // 10 pixels
             pointerGestureMultitouchSettleInterval(100 * 1000000LL), // 100 ms
-            pointerGestureMultitouchMinSpeed(150.0f), // 150 pixels per second
-            pointerGestureSwipeTransitionAngleCosine(0.5f), // cosine of 45degrees
-            pointerGestureSwipeMaxWidthRatio(0.333f),
-            pointerGestureMovementSpeedRatio(0.3f),
+            pointerGestureMultitouchMinDistance(15), // 15 pixels
+            pointerGestureSwipeTransitionAngleCosine(0.2588f), // cosine of 75 degrees
+            pointerGestureSwipeMaxWidthRatio(0.25f),
+            pointerGestureMovementSpeedRatio(0.8f),
             pointerGestureZoomSpeedRatio(0.3f) { }
 };
 
@@ -1140,12 +1140,6 @@
         PointerProperties lastGestureProperties[MAX_POINTERS];
         PointerCoords lastGestureCoords[MAX_POINTERS];
 
-        // Pointer coords and ids for the current spots.
-        PointerControllerInterface::SpotGesture spotGesture;
-        BitSet32 spotIdBits; // same set of ids as touch ids
-        uint32_t spotIdToIndex[MAX_POINTER_ID + 1];
-        PointerCoords spotCoords[MAX_POINTERS];
-
         // Time the pointer gesture last went down.
         nsecs_t downTime;
 
@@ -1192,8 +1186,6 @@
             currentGestureIdBits.clear();
             lastGestureMode = NEUTRAL;
             lastGestureIdBits.clear();
-            spotGesture = PointerControllerInterface::SPOT_GESTURE_NEUTRAL;
-            spotIdBits.clear();
             downTime = 0;
             velocityTracker.clear();
             resetTap();
@@ -1219,7 +1211,6 @@
     void dispatchPointerGestures(nsecs_t when, uint32_t policyFlags, bool isTimeout);
     bool preparePointerGestures(nsecs_t when,
             bool* outCancelPreviousGesture, bool* outFinishPreviousGesture, bool isTimeout);
-    void moveSpotsLocked();
 
     // Dispatches a motion event.
     // If the changedId is >= 0 and the action is POINTER_DOWN or POINTER_UP, the
diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp
index c18ebcf..12c7cba 100644
--- a/services/input/PointerController.cpp
+++ b/services/input/PointerController.cpp
@@ -240,15 +240,15 @@
     }
 }
 
-void PointerController::setSpots(SpotGesture spotGesture,
-        const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+void PointerController::setSpots(const PointerCoords* spotCoords,
+        const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
 #if DEBUG_POINTER_UPDATES
-    LOGD("setSpots: spotGesture=%d", spotGesture);
+    LOGD("setSpots: idBits=%08x", spotIdBits.value);
     for (BitSet32 idBits(spotIdBits); !idBits.isEmpty(); ) {
         uint32_t id = idBits.firstMarkedBit();
         idBits.clearBit(id);
         const PointerCoords& c = spotCoords[spotIdToIndex[id]];
-        LOGD("  spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
+        LOGD(" spot %d: position=(%0.3f, %0.3f), pressure=%0.3f", id,
                 c.getAxisValue(AMOTION_EVENT_AXIS_X),
                 c.getAxisValue(AMOTION_EVENT_AXIS_Y),
                 c.getAxisValue(AMOTION_EVENT_AXIS_PRESSURE));
diff --git a/services/input/PointerController.h b/services/input/PointerController.h
index 1c21db1..700ef72 100644
--- a/services/input/PointerController.h
+++ b/services/input/PointerController.h
@@ -90,38 +90,6 @@
     /* Sets the mode of the pointer controller. */
     virtual void setPresentation(Presentation presentation) = 0;
 
-    // Describes the current gesture.
-    enum SpotGesture {
-        // No gesture.
-        // Do not display any spots.
-        SPOT_GESTURE_NEUTRAL,
-        // Tap at current location.
-        // Briefly display one spot at the tapped location.
-        SPOT_GESTURE_TAP,
-        // Drag at current location.
-        // Display spot at pressed location.
-        SPOT_GESTURE_DRAG,
-        // Button pressed but no finger is down.
-        // Display spot at pressed location.
-        SPOT_GESTURE_BUTTON_CLICK,
-        // Button pressed and a finger is down.
-        // Display spot at pressed location.
-        SPOT_GESTURE_BUTTON_DRAG,
-        // One finger down and hovering.
-        // Display spot at the hovered location.
-        SPOT_GESTURE_HOVER,
-        // Two fingers down but not sure in which direction they are moving so we consider
-        // it a press at the pointer location.
-        // Display two spots near the pointer location.
-        SPOT_GESTURE_PRESS,
-        // Two fingers down and moving in same direction.
-        // Display two spots near the pointer location.
-        SPOT_GESTURE_SWIPE,
-        // Two or more fingers down and moving in arbitrary directions.
-        // Display two or more spots near the pointer location, one for each finger.
-        SPOT_GESTURE_FREEFORM,
-    };
-
     /* Sets the spots for the current gesture.
      * The spots are not subject to the inactivity timeout like the pointer
      * itself it since they are expected to remain visible for so long as
@@ -131,8 +99,7 @@
      * For spotCoords, pressure != 0 indicates that the spot's location is being
      * pressed (not hovering).
      */
-    virtual void setSpots(SpotGesture spotGesture,
-            const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
+    virtual void setSpots(const PointerCoords* spotCoords, const uint32_t* spotIdToIndex,
             BitSet32 spotIdBits) = 0;
 
     /* Removes all spots. */
@@ -198,8 +165,8 @@
     virtual void unfade(Transition transition);
 
     virtual void setPresentation(Presentation presentation);
-    virtual void setSpots(SpotGesture spotGesture,
-            const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
+    virtual void setSpots(const PointerCoords* spotCoords,
+            const uint32_t* spotIdToIndex, BitSet32 spotIdBits);
     virtual void clearSpots();
 
     void setDisplaySize(int32_t width, int32_t height);
diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp
index 00b4222..d04c9e7 100644
--- a/services/input/tests/InputReader_test.cpp
+++ b/services/input/tests/InputReader_test.cpp
@@ -101,8 +101,8 @@
     virtual void setPresentation(Presentation presentation) {
     }
 
-    virtual void setSpots(SpotGesture spotGesture,
-            const PointerCoords* spotCoords, const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
+    virtual void setSpots(const PointerCoords* spotCoords,
+            const uint32_t* spotIdToIndex, BitSet32 spotIdBits) {
     }
 
     virtual void clearSpots() {
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 7c6d3c1..3aa1239 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -134,6 +134,7 @@
     // Timeout intervals for agent backup & restore operations
     static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
     static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
+    static final long TIMEOUT_SHARED_BACKUP_INTERVAL = 30 * 60 * 1000;
     static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
 
     // User confirmation timeout for a full backup/restore operation
@@ -1691,7 +1692,7 @@
         public void run() {
             final List<PackageInfo> packagesToBackup;
 
-            Slog.i(TAG, "--- Performing full-dataset restore ---");
+            Slog.i(TAG, "--- Performing full-dataset backup ---");
             sendStartBackup();
 
             // doAllApps supersedes the package set if any
@@ -1720,64 +1721,23 @@
                 }
             }
 
-            // Now back up the app data via the agent mechanism
             PackageInfo pkg = null;
             try {
+                // Now back up the app data via the agent mechanism
                 int N = packagesToBackup.size();
                 for (int i = 0; i < N; i++) {
                     pkg = packagesToBackup.get(i);
+                    backupOnePackage(pkg);
+                }
 
-                    Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
-
-                    IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
-                            IApplicationThread.BACKUP_MODE_FULL);
-                    if (agent != null) {
-                        try {
-                            ApplicationInfo app = pkg.applicationInfo;
-                            boolean sendApk = mIncludeApks
-                                    && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
-                                    && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
-                                        (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
-
-                            sendOnBackupPackage(pkg.packageName);
-
-                            {
-                                BackupDataOutput output = new BackupDataOutput(
-                                        mOutputFile.getFileDescriptor());
-
-                                if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
-                                writeAppManifest(pkg, mManifestFile, sendApk);
-                                FullBackup.backupToTar(pkg.packageName, null, null,
-                                        mFilesDir.getAbsolutePath(),
-                                        mManifestFile.getAbsolutePath(),
-                                        output);
-                            }
-
-                            if (DEBUG) Slog.d(TAG, "Calling doBackup()");
-                            final int token = generateToken();
-                            prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
-                            agent.doBackup(null, mOutputFile, null, sendApk,
-                                    token, mBackupManagerBinder);
-                            boolean success = waitUntilOperationComplete(token);
-                            if (!success) {
-                                Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
-                            } else {
-                                if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
-                            }
-                        } catch (IOException e) {
-                            Slog.e(TAG, "Error backing up " + pkg.packageName, e);
-                        }
-                    } else {
-                        Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
-                    }
-                    tearDown(pkg);
+                // Finally, shared storage if requested
+                if (mIncludeShared) {
+                    backupSharedStorage();
                 }
             } catch (RemoteException e) {
                 Slog.e(TAG, "App died during full backup");
             } finally {
-                if (pkg != null) {
-                    tearDown(pkg);
-                }
+                tearDown(pkg);
                 try {
                     mOutputFile.close();
                 } catch (IOException e) {
@@ -1796,6 +1756,79 @@
             }
         }
 
+        private void backupOnePackage(PackageInfo pkg) throws RemoteException {
+            Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
+
+            IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+                    IApplicationThread.BACKUP_MODE_FULL);
+            if (agent != null) {
+                try {
+                    ApplicationInfo app = pkg.applicationInfo;
+                    boolean sendApk = mIncludeApks
+                            && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
+                            && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+                                (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+
+                    sendOnBackupPackage(pkg.packageName);
+
+                    {
+                        BackupDataOutput output = new BackupDataOutput(
+                                mOutputFile.getFileDescriptor());
+
+                        if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
+                        writeAppManifest(pkg, mManifestFile, sendApk);
+                        FullBackup.backupToTar(pkg.packageName, null, null,
+                                mFilesDir.getAbsolutePath(),
+                                mManifestFile.getAbsolutePath(),
+                                output);
+                    }
+
+                    if (DEBUG) Slog.d(TAG, "Calling doBackup()");
+                    final int token = generateToken();
+                    prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
+                    agent.doBackup(null, mOutputFile, null, sendApk,
+                            token, mBackupManagerBinder);
+                    if (!waitUntilOperationComplete(token)) {
+                        Slog.e(TAG, "Full backup failed on package " + pkg.packageName);
+                    } else {
+                        if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
+                    }
+                } catch (IOException e) {
+                    Slog.e(TAG, "Error backing up " + pkg.packageName, e);
+                }
+            } else {
+                Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
+            }
+            tearDown(pkg);
+        }
+
+        private void backupSharedStorage() throws RemoteException {
+            PackageInfo pkg = null;
+            try {
+                pkg = mPackageManager.getPackageInfo("com.android.sharedstoragebackup", 0);
+                IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+                        IApplicationThread.BACKUP_MODE_FULL);
+                if (agent != null) {
+                    sendOnBackupPackage("Shared storage");
+
+                    final int token = generateToken();
+                    prepareOperationTimeout(token, TIMEOUT_SHARED_BACKUP_INTERVAL);
+                    agent.doBackup(null, mOutputFile, null, false, token, mBackupManagerBinder);
+                    if (!waitUntilOperationComplete(token)) {
+                        Slog.e(TAG, "Full backup failed on shared storage");
+                    } else {
+                        if (DEBUG) Slog.d(TAG, "Full shared storage backup success");
+                    }
+                } else {
+                    Slog.e(TAG, "Could not bind to shared storage backup agent");
+                }
+            } catch (NameNotFoundException e) {
+                Slog.e(TAG, "Shared storage backup package not found");
+            } finally {
+                tearDown(pkg);
+            }
+        }
+
         private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
                 throws IOException {
             // Manifest format. All data are strings ending in LF:
@@ -1836,23 +1869,24 @@
         }
 
         private void tearDown(PackageInfo pkg) {
-            final ApplicationInfo app = pkg.applicationInfo;
-            try {
-                // unbind and tidy up even on timeout or failure, just in case
-                mActivityManager.unbindBackupAgent(app);
+            if (pkg != null) {
+                final ApplicationInfo app = pkg.applicationInfo;
+                if (app != null) {
+                    try {
+                        // unbind and tidy up even on timeout or failure, just in case
+                        mActivityManager.unbindBackupAgent(app);
 
-                // The agent was running with a stub Application object, so shut it down.
-                // !!! We hardcode the confirmation UI's package name here rather than use a
-                //     manifest flag!  TODO something less direct.
-                if (app.uid != Process.SYSTEM_UID
-                        && !pkg.packageName.equals("com.android.backupconfirm")) {
-                    if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
-                    mActivityManager.killApplicationProcess(app.processName, app.uid);
-                } else {
-                    if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+                        // The agent was running with a stub Application object, so shut it down.
+                        if (app.uid != Process.SYSTEM_UID) {
+                            if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
+                            mActivityManager.killApplicationProcess(app.processName, app.uid);
+                        } else {
+                            if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+                        }
+                    } catch (RemoteException e) {
+                        Slog.d(TAG, "Lost app trying to shut down");
+                    }
                 }
-            } catch (RemoteException e) {
-                Slog.d(TAG, "Lost app trying to shut down");
             }
         }
 
@@ -1905,6 +1939,18 @@
         long mode;                      // e.g. 0666 (actually int)
         long mtime;                     // last mod time, UTC time_t (actually int)
         long size;                      // bytes of content
+
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(128);
+            sb.append("FileMetadata{");
+            sb.append(packageName); sb.append(',');
+            sb.append(type); sb.append(',');
+            sb.append(domain); sb.append(':'); sb.append(path); sb.append(',');
+            sb.append(size);
+            sb.append('}');
+            return sb.toString();
+        }
     }
 
     enum RestorePolicy {
@@ -1922,6 +1968,8 @@
         ApplicationInfo mTargetApp;
         ParcelFileDescriptor[] mPipes = null;
 
+        long mBytes;
+
         // possible handling states for a given package in the restore dataset
         final HashMap<String, RestorePolicy> mPackagePolicies
                 = new HashMap<String, RestorePolicy>();
@@ -1949,6 +1997,7 @@
             // with a whitelist of packages known to be unclearable.
             mClearedPackages.add("android");
             mClearedPackages.add("com.android.providers.settings");
+
         }
 
         class RestoreFileRunnable implements Runnable {
@@ -1988,7 +2037,13 @@
             Slog.i(TAG, "--- Performing full-dataset restore ---");
             sendStartRestore();
 
+            // Are we able to restore shared-storage data?
+            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                mPackagePolicies.put("com.android.sharedstoragebackup", RestorePolicy.ACCEPT);
+            }
+
             try {
+                mBytes = 0;
                 byte[] buffer = new byte[32 * 1024];
                 FileInputStream instream = new FileInputStream(mInputFile.getFileDescriptor());
 
@@ -1997,7 +2052,7 @@
                     didRestore = restoreOneFile(instream, buffer);
                 } while (didRestore);
 
-                if (DEBUG) Slog.v(TAG, "Done consuming input tarfile");
+                if (DEBUG) Slog.v(TAG, "Done consuming input tarfile, total bytes=" + mBytes);
             } finally {
                 tearDownPipes();
                 tearDownAgent(mTargetApp);
@@ -2005,6 +2060,7 @@
                 try {
                     mInputFile.close();
                 } catch (IOException e) {
+                    Slog.w(TAG, "Close of restore data pipe threw", e);
                     /* nothing we can do about this */
                 }
                 synchronized (mCurrentOpLock) {
@@ -2218,6 +2274,7 @@
                                     int toRead = (toCopy > buffer.length)
                                     ? buffer.length : (int)toCopy;
                                     int nRead = instream.read(buffer, 0, toRead);
+                                    if (nRead >= 0) mBytes += nRead;
                                     if (nRead <= 0) break;
                                     toCopy -= nRead;
 
@@ -2227,8 +2284,7 @@
                                         try {
                                             pipe.write(buffer, 0, nRead);
                                         } catch (IOException e) {
-                                            Slog.e(TAG,
-                                                    "Failed to write to restore pipe", e);
+                                            Slog.e(TAG, "Failed to write to restore pipe", e);
                                             pipeOkay = false;
                                         }
                                     }
@@ -2264,6 +2320,7 @@
                                 int toRead = (bytesToConsume > buffer.length)
                                 ? buffer.length : (int)bytesToConsume;
                                 long nRead = instream.read(buffer, 0, toRead);
+                                if (nRead >= 0) mBytes += nRead;
                                 if (nRead <= 0) break;
                                 bytesToConsume -= nRead;
                             }
@@ -2285,15 +2342,13 @@
 
         void tearDownPipes() {
             if (mPipes != null) {
-                if (mPipes[0] != null) {
-                    try {
-                        mPipes[0].close();
-                        mPipes[0] = null;
-                        mPipes[1].close();
-                        mPipes[1] = null;
-                    } catch (IOException e) {
-                        Slog.w(TAG, "Couldn't close agent pipes", e);
-                    }
+                try {
+                    mPipes[0].close();
+                    mPipes[0] = null;
+                    mPipes[1].close();
+                    mPipes[1] = null;
+                } catch (IOException e) {
+                    Slog.w(TAG, "Couldn't close agent pipes", e);
                 }
                 mPipes = null;
             }
@@ -2407,6 +2462,7 @@
                 while (size > 0) {
                     long toRead = (buffer.length < size) ? buffer.length : size;
                     int didRead = instream.read(buffer, 0, (int)toRead);
+                    if (didRead >= 0) mBytes += didRead;
                     apkStream.write(buffer, 0, didRead);
                     size -= didRead;
                 }
@@ -2489,7 +2545,8 @@
             long partial = (size + 512) % 512;
             if (partial > 0) {
                 byte[] buffer = new byte[512];
-                instream.read(buffer, 0, 512 - (int)partial);
+                int nRead = instream.read(buffer, 0, 512 - (int)partial);
+                if (nRead >= 0) mBytes += nRead;
             }
         }
 
@@ -2505,6 +2562,7 @@
             while (nRead < info.size) {
                 nRead += instream.read(buffer, nRead, (int)info.size - nRead);
             }
+            if (nRead >= 0) mBytes += nRead;
 
             RestorePolicy policy = RestorePolicy.IGNORE;
             String[] str = new String[1];
@@ -2689,9 +2747,17 @@
 
                 switch (typeChar) {
                     case '0': info.type = FullBackup.TYPE_FILE; break;
-                    case '5': info.type = FullBackup.TYPE_DIRECTORY; break;
+                    case '5': {
+                        info.type = FullBackup.TYPE_DIRECTORY;
+                        if (info.size != 0) {
+                            Slog.w(TAG, "Directory entry with nonzero size in header");
+                            info.size = 0;
+                        }
+                        break;
+                    }
                     case 0: {
                         // presume EOF
+                        if (DEBUG) Slog.w(TAG, "Saw type=0 in tar header block, info=" + info);
                         return null;
                     }
                     default: {
@@ -2707,7 +2773,9 @@
                         info.path, 0, FullBackup.SHARED_PREFIX.length())) {
                     // File in shared storage.  !!! TODO: implement this.
                     info.path = info.path.substring(FullBackup.SHARED_PREFIX.length());
+                    info.packageName = "com.android.sharedstoragebackup";
                     info.domain = FullBackup.SHARED_STORAGE_TOKEN;
+                    if (DEBUG) Slog.i(TAG, "File in shared storage: " + info.path);
                 } else if (FullBackup.APPS_PREFIX.regionMatches(0,
                         info.path, 0, FullBackup.APPS_PREFIX.length())) {
                     // App content!  Parse out the package name and domain
@@ -2746,6 +2814,7 @@
 
         boolean readTarHeader(InputStream instream, byte[] block) throws IOException {
             int nRead = instream.read(block, 0, 512);
+            if (nRead >= 0) mBytes += nRead;
             if (nRead > 0 && nRead != 512) {
                 // if we read only a partial block, then things are
                 // clearly screwed up.  terminate the restore.
@@ -2768,6 +2837,7 @@
             int numBlocks = (int)((info.size + 511) >> 9);
             byte[] data = new byte[numBlocks * 512];
             int nRead = instream.read(data);
+            if (nRead >= 0) mBytes += nRead;
             if (nRead != data.length) {
                 return false;
             }
@@ -2817,6 +2887,7 @@
             final int end = offset + maxChars;
             for (int i = offset; i < end; i++) {
                 final byte b = data[i];
+                // Numeric fields in tar can terminate with either NUL or SPC
                 if (b == 0 || b == ' ') break;
                 if (b < '0' || b > ('0' + radix - 1)) {
                     throw new IOException("Invalid number in header");
@@ -2829,8 +2900,8 @@
         String extractString(byte[] data, int offset, int maxChars) throws IOException {
             final int end = offset + maxChars;
             int eos = offset;
-            // tar string fields can end with either NUL or SPC
-            while (eos < end && data[eos] != 0 && data[eos] != ' ') eos++;
+            // tar string fields terminate early with a NUL
+            while (eos < end && data[eos] != 0) eos++;
             return new String(data, offset, eos-offset, "US-ASCII");
         }
 
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index dd76eb8..c6f4c20 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -16,6 +16,7 @@
 
 package com.android.server;
 
+import static android.Manifest.permission.READ_PHONE_STATE;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
 import static android.net.ConnectivityManager.isNetworkTypeValid;
 import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL;
@@ -38,6 +39,7 @@
 import android.net.NetworkConfig;
 import android.net.NetworkInfo;
 import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkState;
 import android.net.NetworkStateTracker;
 import android.net.NetworkUtils;
 import android.net.Proxy;
@@ -46,6 +48,7 @@
 import android.net.vpn.VpnManager;
 import android.net.wifi.WifiStateTracker;
 import android.os.Binder;
+import android.os.Bundle;
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.HandlerThread;
@@ -53,6 +56,7 @@
 import android.os.INetworkManagementService;
 import android.os.Looper;
 import android.os.Message;
+import android.os.ParcelFileDescriptor;
 import android.os.PowerManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
@@ -65,6 +69,9 @@
 
 import com.android.internal.telephony.Phone;
 import com.android.server.connectivity.Tethering;
+import com.android.server.connectivity.Vpn;
+
+import com.google.android.collect.Lists;
 
 import java.io.FileDescriptor;
 import java.io.IOException;
@@ -100,6 +107,8 @@
     private Tethering mTethering;
     private boolean mTetheringConfigValid = false;
 
+    private Vpn mVpn;
+
     /** Currently active network rules by UID. */
     private SparseIntArray mUidRules = new SparseIntArray();
 
@@ -458,6 +467,15 @@
                                   mTethering.getTetherableBluetoothRegexs().length != 0) &&
                                  mTethering.getUpstreamIfaceRegexs().length != 0);
 
+        mVpn = new Vpn(mContext, new VpnCallback());
+
+        try {
+            nmService.registerObserver(mTethering);
+            nmService.registerObserver(mVpn);
+        } catch (RemoteException e) {
+            loge("Error registering observer :" + e);
+        }
+
         if (DBG) {
             mInetLog = new ArrayList();
         }
@@ -557,25 +575,32 @@
      */
     private boolean isNetworkBlocked(NetworkInfo info, int uid) {
         synchronized (mUidRules) {
-            return isNetworkBlockedLocked(info, uid);
+            // TODO: expand definition of "paid" network to cover tethered or
+            // paid hotspot use cases.
+            final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
+            final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
+
+            if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
+                return true;
+            }
+
+            // no restrictive rules; network is visible
+            return false;
         }
     }
 
     /**
-     * Check if UID is blocked from using the given {@link NetworkInfo}.
+     * Return a filtered version of the given {@link NetworkInfo}, potentially
+     * marked {@link DetailedState#BLOCKED} based on
+     * {@link #isNetworkBlocked(NetworkInfo, int)}.
      */
-    private boolean isNetworkBlockedLocked(NetworkInfo info, int uid) {
-        // TODO: expand definition of "paid" network to cover tethered or paid
-        // hotspot use cases.
-        final boolean networkIsPaid = info.getType() != ConnectivityManager.TYPE_WIFI;
-        final int uidRules = mUidRules.get(uid, RULE_ALLOW_ALL);
-
-        if (networkIsPaid && (uidRules & RULE_REJECT_PAID) != 0) {
-            return true;
+    private NetworkInfo filterNetworkInfo(NetworkInfo info, int uid) {
+        if (isNetworkBlocked(info, uid)) {
+            // network is blocked; clone and override state
+            info = new NetworkInfo(info);
+            info.setDetailedState(DetailedState.BLOCKED, null, null);
         }
-
-        // no restrictive rules; network is visible
-        return false;
+        return info;
     }
 
     /**
@@ -610,12 +635,7 @@
         if (isNetworkTypeValid(networkType)) {
             final NetworkStateTracker tracker = mNetTrackers[networkType];
             if (tracker != null) {
-                info = tracker.getNetworkInfo();
-                if (isNetworkBlocked(info, uid)) {
-                    // network is blocked; clone and override state
-                    info = new NetworkInfo(info);
-                    info.setDetailedState(DetailedState.BLOCKED, null, null);
-                }
+                info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
             }
         }
         return info;
@@ -625,22 +645,15 @@
     public NetworkInfo[] getAllNetworkInfo() {
         enforceAccessPermission();
         final int uid = Binder.getCallingUid();
-        final NetworkInfo[] result = new NetworkInfo[mNetworksDefined];
-        int i = 0;
+        final ArrayList<NetworkInfo> result = Lists.newArrayList();
         synchronized (mUidRules) {
             for (NetworkStateTracker tracker : mNetTrackers) {
                 if (tracker != null) {
-                    NetworkInfo info = tracker.getNetworkInfo();
-                    if (isNetworkBlockedLocked(info, uid)) {
-                        // network is blocked; clone and override state
-                        info = new NetworkInfo(info);
-                        info.setDetailedState(DetailedState.BLOCKED, null, null);
-                    }
-                    result[i++] = info;
+                    result.add(filterNetworkInfo(tracker.getNetworkInfo(), uid));
                 }
             }
         }
-        return result;
+        return result.toArray(new NetworkInfo[result.size()]);
     }
 
     /**
@@ -668,6 +681,23 @@
         return null;
     }
 
+    @Override
+    public NetworkState[] getAllNetworkState() {
+        enforceAccessPermission();
+        final int uid = Binder.getCallingUid();
+        final ArrayList<NetworkState> result = Lists.newArrayList();
+        synchronized (mUidRules) {
+            for (NetworkStateTracker tracker : mNetTrackers) {
+                if (tracker != null) {
+                    final NetworkInfo info = filterNetworkInfo(tracker.getNetworkInfo(), uid);
+                    result.add(new NetworkState(
+                            info, tracker.getLinkProperties(), tracker.getLinkCapabilities()));
+                }
+            }
+        }
+        return result.toArray(new NetworkState[result.size()]);
+    }
+
     public boolean setRadios(boolean turnOn) {
         boolean result = true;
         enforceChangePermission();
@@ -2337,6 +2367,7 @@
     private void loge(String s) {
         Slog.e(TAG, s);
     }
+
     int convertFeatureToNetworkType(String feature){
         int networkType = -1;
         if (TextUtils.equals(feature, Phone.FEATURE_ENABLE_MMS)) {
@@ -2364,4 +2395,62 @@
         }
         return value;
     }
+
+    // @see ConnectivityManager#protectVpn(ParcelFileDescriptor)
+    // Permission checks are done in Vpn class.
+    @Override
+    public void protectVpn(ParcelFileDescriptor socket) {
+        mVpn.protect(socket, getDefaultInterface());
+    }
+
+    // @see ConnectivityManager#prepareVpn(String)
+    // Permission checks are done in Vpn class.
+    @Override
+    public String prepareVpn(String packageName) {
+        return mVpn.prepare(packageName);
+    }
+
+    // @see ConnectivityManager#establishVpn(Bundle)
+    // Permission checks are done in Vpn class.
+    @Override
+    public ParcelFileDescriptor establishVpn(Bundle config) {
+        return mVpn.establish(config);
+    }
+
+    private String getDefaultInterface() {
+        if (ConnectivityManager.isNetworkTypeValid(mActiveDefaultNetwork)) {
+            NetworkStateTracker tracker = mNetTrackers[mActiveDefaultNetwork];
+            if (tracker != null) {
+                LinkProperties properties = tracker.getLinkProperties();
+                if (properties != null) {
+                    return properties.getInterfaceName();
+                }
+            }
+        }
+        throw new IllegalStateException("No default interface");
+    }
+
+    /**
+     * Callback for VPN subsystem. Currently VPN is not adapted to the service
+     * through NetworkStateTracker since it works differently. For example, it
+     * needs to override DNS servers but never takes the default routes. It
+     * relies on another data network, and it could keep existing connections
+     * alive after reconnecting, switching between networks, or even resuming
+     * from deep sleep. Calls from applications should be done synchronously
+     * to avoid race conditions. As these are all hidden APIs, refactoring can
+     * be done whenever a better abstraction is developed.
+     */
+    public class VpnCallback {
+
+        private VpnCallback() {
+        }
+
+        public synchronized void override(String[] dnsServers) {
+            // TODO: override DNS servers and http proxy.
+        }
+
+        public synchronized void restore() {
+            // TODO: restore VPN changes.
+        }
+    }
 }
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index f78dca9..c86f962 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -1121,16 +1121,20 @@
                             com.android.internal.R.styleable.Storage_emulated, false);
                     int mtpReserve = a.getInt(
                             com.android.internal.R.styleable.Storage_mtpReserve, 0);
+                    boolean allowMassStorage = a.getBoolean(
+                            com.android.internal.R.styleable.Storage_allowMassStorage, false);
 
                     Slog.d(TAG, "got storage path: " + path + " description: " + description +
                             " primary: " + primary + " removable: " + removable +
-                            " emulated: " + emulated +  " mtpReserve: " + mtpReserve);
+                            " emulated: " + emulated +  " mtpReserve: " + mtpReserve +
+                            " allowMassStorage: " + allowMassStorage);
                     if (path == null || description == null) {
                         Slog.e(TAG, "path or description is null in readStorageList");
                     } else {
                         String pathString = path.toString();
                         StorageVolume volume = new StorageVolume(pathString,
-                                description.toString(), removable, emulated, mtpReserve);
+                                description.toString(), removable, emulated,
+                                mtpReserve, allowMassStorage);
                         if (primary) {
                             if (mPrimaryVolume == null) {
                                 mPrimaryVolume = volume;
diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java
index e738145..367c802 100755
--- a/services/java/com/android/server/NotificationManagerService.java
+++ b/services/java/com/android/server/NotificationManagerService.java
@@ -333,8 +333,7 @@
             if (action.equals(UsbManager.ACTION_USB_STATE)) {
                 Bundle extras = intent.getExtras();
                 boolean usbConnected = extras.getBoolean(UsbManager.USB_CONNECTED);
-                boolean adbEnabled = (UsbManager.USB_FUNCTION_ENABLED.equals(
-                                    extras.getString(UsbManager.USB_FUNCTION_ADB)));
+                boolean adbEnabled = extras.getBoolean(UsbManager.USB_FUNCTION_ADB);
                 updateAdbNotification(usbConnected && adbEnabled);
             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
                     || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
diff --git a/services/java/com/android/server/SystemBackupAgent.java b/services/java/com/android/server/SystemBackupAgent.java
index 99c8af6..08c6699 100644
--- a/services/java/com/android/server/SystemBackupAgent.java
+++ b/services/java/com/android/server/SystemBackupAgent.java
@@ -138,7 +138,7 @@
             if (outFile == null) {
                 Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]");
             }
-            FullBackup.restoreToFile(data, size, type, mode, mtime, outFile);
+            FullBackup.restoreToFile(data, size, type, mode, mtime, outFile, true);
 
             if (restoredWallpaper) {
                 WallpaperManagerService wallpaper =
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 596cbac..fd03e5f89 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -305,6 +305,7 @@
                 Slog.i(TAG, "Connectivity Service");
                 connectivity = new ConnectivityService(context, networkManagement, networkPolicy);
                 ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity);
+                networkStats.bindConnectivityManager(connectivity);
             } catch (Throwable e) {
                 Slog.e(TAG, "Failure starting Connectivity Service", e);
             }
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index 41ec63a..4e2501b 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -321,6 +321,13 @@
                     }
                     break;
                 }
+                case AsyncChannel.CMD_CHANNEL_DISCONNECTED: {
+                    Slog.e(TAG, "WifiStateMachine channel lost, msg.arg1 =" + msg.arg1);
+                    mWifiStateMachineChannel = null;
+                    //Re-establish connection to state machine
+                    mWsmChannel.connect(mContext, this, mWifiStateMachine.getHandler());
+                    break;
+                }
                 default: {
                     Slog.d(TAG, "WifiStateMachineHandler.handleMessage ignoring msg=" + msg);
                     break;
@@ -593,7 +600,12 @@
      */
     public WifiConfiguration getWifiApConfiguration() {
         enforceAccessPermission();
-        return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel);
+        if (mWifiStateMachineChannel != null) {
+            return mWifiStateMachine.syncGetWifiApConfiguration(mWifiStateMachineChannel);
+        } else {
+            Slog.e(TAG, "mWifiStateMachineChannel is not initialized");
+            return null;
+        }
     }
 
     /**
diff --git a/services/java/com/android/server/WifiWatchdogService.java b/services/java/com/android/server/WifiWatchdogService.java
index 6ef6963..6c7895e 100644
--- a/services/java/com/android/server/WifiWatchdogService.java
+++ b/services/java/com/android/server/WifiWatchdogService.java
@@ -36,16 +36,21 @@
 import android.text.TextUtils;
 import android.util.Slog;
 
+import java.io.BufferedInputStream;
+import java.io.InputStream;
 import java.io.IOException;
 import java.net.DatagramPacket;
 import java.net.DatagramSocket;
+import java.net.HttpURLConnection;
 import java.net.InetAddress;
 import java.net.SocketException;
 import java.net.SocketTimeoutException;
 import java.net.UnknownHostException;
+import java.net.URL;
 import java.util.Collection;
 import java.util.List;
 import java.util.Random;
+import java.util.Scanner;
 
 /**
  * {@link WifiWatchdogService} monitors the initial connection to a Wi-Fi
@@ -195,6 +200,34 @@
         return Settings.Secure.getInt(mContentResolver,
             Settings.Secure.WIFI_WATCHDOG_PING_DELAY_MS, 250);
     }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED
+     */
+     private Boolean isWalledGardenTestEnabled() {
+        return Settings.Secure.getInt(mContentResolver,
+                 Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_TEST_ENABLED, 1) == 1;
+     }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_URL
+     */
+     private String getWalledGardenUrl() {
+        String url = Settings.Secure.getString(mContentResolver,
+                 Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_URL);
+        if (TextUtils.isEmpty(url)) return "http://www.google.com/";
+        return url;
+     }
+
+    /**
+     * @see android.provider.Settings.Secure#WIFI_WATCHDOG_WALLED_GARDEN_PATTERN
+     */
+     private String getWalledGardenPattern() {
+        String pattern = Settings.Secure.getString(mContentResolver,
+                 Settings.Secure.WIFI_WATCHDOG_WALLED_GARDEN_PATTERN);
+        if (TextUtils.isEmpty(pattern)) return "<title>.*Google.*</title>";
+        return pattern;
+     }
     
     /**
      * @see android.provider.Settings.Secure#WIFI_WATCHDOG_ACCEPTABLE_PACKET_LOSS_PERCENTAGE
@@ -509,7 +542,7 @@
             // This access point does not require a watchdog, so queue idle on the main thread
             mHandler.idle();
         }
-        mHandler.checkWalledGarden(ssid);
+        if (isWalledGardenTestEnabled()) mHandler.checkWalledGarden(ssid);
     }
     
     /**
@@ -1118,19 +1151,35 @@
             }
         }
 
+        /**
+         * DNS based detection techniques do not work at all hotspots. The one sure way to check
+         * a walled garden is to see if a URL fetch on a known address fetches the data we
+         * expect
+         */
         private boolean isWalledGardenConnection() {
-            //One way to detect a walled garden is to see if multiple DNS queries
-            //resolve to the same IP address
+            InputStream in = null;
+            HttpURLConnection  urlConnection = null;
             try {
-                String host1 = "www.google.com";
-                String host2 = "www.android.com";
-                String address1 = InetAddress.getByName(host1).getHostAddress();
-                String address2 = InetAddress.getByName(host2).getHostAddress();
-                if (address1.equals(address2)) return true;
-            } catch (UnknownHostException e) {
+                URL url = new URL(getWalledGardenUrl());
+                urlConnection = (HttpURLConnection) url.openConnection();
+                in = new BufferedInputStream(urlConnection.getInputStream());
+                Scanner scanner = new Scanner(in);
+                if (scanner.findInLine(getWalledGardenPattern()) != null) {
+                    return false;
+                } else {
+                    return true;
+                }
+            } catch (IOException e) {
                 return false;
+            } finally {
+                if (in != null) {
+                    try {
+                        in.close();
+                    } catch (IOException e) {
+                    }
+                }
+                if (urlConnection != null) urlConnection.disconnect();
             }
-            return false;
         }
 
         private void handleWalledGardenCheck(String ssid) {
diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
index fba293c..86671d6 100644
--- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -16,13 +16,7 @@
 
 package com.android.server.accessibility;
 
-import com.android.internal.content.PackageMonitor;
-import com.android.internal.os.HandlerCaller;
-import com.android.internal.os.HandlerCaller.SomeArgs;
-import com.android.server.wm.WindowManagerService;
-
-import org.xmlpull.v1.XmlPullParserException;
-
+import android.Manifest;
 import android.accessibilityservice.AccessibilityService;
 import android.accessibilityservice.AccessibilityServiceInfo;
 import android.accessibilityservice.IAccessibilityServiceConnection;
@@ -37,7 +31,6 @@
 import android.content.ServiceConnection;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
 import android.database.ContentObserver;
 import android.net.Uri;
 import android.os.Binder;
@@ -46,15 +39,27 @@
 import android.os.Message;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.SystemClock;
 import android.provider.Settings;
 import android.text.TextUtils;
 import android.text.TextUtils.SimpleStringSplitter;
 import android.util.Slog;
 import android.util.SparseArray;
+import android.view.IWindow;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.accessibility.IAccessibilityInteractionConnection;
+import android.view.accessibility.IAccessibilityInteractionConnectionCallback;
 import android.view.accessibility.IAccessibilityManager;
 import android.view.accessibility.IAccessibilityManagerClient;
 
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.os.HandlerCaller.SomeArgs;
+import com.android.server.wm.WindowManagerService;
+
+import org.xmlpull.v1.XmlPullParserException;
+
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -64,6 +69,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * This class is instantiated by the system as a system level service and can be
@@ -80,12 +86,17 @@
 
     private static final String LOG_TAG = "AccessibilityManagerService";
 
+    private static final String FUNCTION_REGISTER_EVENT_LISTENER =
+        "registerEventListener";
+
     private static int sIdCounter = 0;
 
     private static final int OWN_PROCESS_ID = android.os.Process.myPid();
 
     private static final int DO_SET_SERVICE_INFO = 10;
 
+    private static int sNextWindowId;
+
     final HandlerCaller mCaller;
 
     final Context mContext;
@@ -103,6 +114,11 @@
 
     private final Set<ComponentName> mEnabledServices = new HashSet<ComponentName>();
 
+    private final SparseArray<IAccessibilityInteractionConnection> mWindowIdToInteractionConnectionMap =
+        new SparseArray<IAccessibilityInteractionConnection>();
+
+    private final SparseArray<IBinder> mWindowIdToWindowTokenMap = new SparseArray<IBinder>();
+
     private final SimpleStringSplitter mStringColonSplitter = new SimpleStringSplitter(':');
 
     private PackageManager mPackageManager;
@@ -117,6 +133,10 @@
 
     private boolean mHasInputFilter;
 
+    private final WindowManagerService mWindowManagerService;
+
+    private final SecurityPolicy mSecurityPolicy;
+
     /**
      * Handler for delayed event dispatch.
      */
@@ -145,6 +165,9 @@
         mContext = context;
         mPackageManager = mContext.getPackageManager();
         mCaller = new HandlerCaller(context, this);
+        mWindowManagerService = (WindowManagerService) ServiceManager.getService(
+                Context.WINDOW_SERVICE);
+        mSecurityPolicy = new SecurityPolicy();
 
         registerPackageChangeAndBootCompletedBroadcastReceiver();
         registerSettingsContentObservers();
@@ -208,7 +231,6 @@
                 if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) {
                     synchronized (mLock) {
                         populateAccessibilityServiceListLocked();
-
                         // get the accessibility enabled setting on boot
                         mIsEnabled = Settings.Secure.getInt(mContext.getContentResolver(),
                                 Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1;
@@ -223,7 +245,7 @@
                     
                     return;
                 }
-                
+
                 super.onReceive(context, intent);
             }
         };
@@ -297,6 +319,7 @@
 
     public boolean sendAccessibilityEvent(AccessibilityEvent event) {
         synchronized (mLock) {
+            mSecurityPolicy.updateRetrievalAllowingWindowAndEventSourceLocked(event);
             notifyAccessibilityServicesDelayedLocked(event, false);
             notifyAccessibilityServicesDelayedLocked(event, true);
         }
@@ -352,7 +375,7 @@
 
     public void executeMessage(Message message) {
         switch (message.what) {
-            case DO_SET_SERVICE_INFO:
+            case DO_SET_SERVICE_INFO: {
                 SomeArgs arguments = ((SomeArgs) message.obj);
 
                 AccessibilityServiceInfo info = (AccessibilityServiceInfo) arguments.arg1;
@@ -365,17 +388,73 @@
                     AccessibilityServiceInfo oldInfo = service.mAccessibilityServiceInfo;
                     if (oldInfo != null) {
                         oldInfo.updateDynamicallyConfigurableProperties(info);
-                        service.setAccessibilityServiceInfo(oldInfo);
+                        service.setDynamicallyConfigurableProperties(oldInfo);
                     } else {
-                        service.setAccessibilityServiceInfo(info);
+                        service.setDynamicallyConfigurableProperties(info);
                     }
                 }
-                return;
+            } return;
             default:
                 Slog.w(LOG_TAG, "Unknown message type: " + message.what);
         }
     }
 
+    public int addAccessibilityInteractionConnection(IWindow windowToken,
+            IAccessibilityInteractionConnection connection) throws RemoteException {
+        synchronized (mLock) {
+            final IWindow addedWindowToken = windowToken;
+            final int windowId = sNextWindowId++;
+            connection.asBinder().linkToDeath(new DeathRecipient() {
+                public void binderDied() {
+                    synchronized (mLock) {
+                        removeAccessibilityInteractionConnection(addedWindowToken);
+                    }
+                }
+            }, 0);
+            mWindowIdToWindowTokenMap.put(windowId, addedWindowToken.asBinder());
+            mWindowIdToInteractionConnectionMap.put(windowId, connection);
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Adding interaction connection to windowId: " + windowId);
+            }
+            return windowId;
+        }
+    }
+
+    public void removeAccessibilityInteractionConnection(IWindow windowToken) {
+        synchronized (mLock) {
+            final int count = mWindowIdToWindowTokenMap.size();
+            for (int i = 0; i < count; i++) {
+                if (mWindowIdToWindowTokenMap.valueAt(i) == windowToken.asBinder()) {
+                    final int windowId = mWindowIdToWindowTokenMap.keyAt(i);
+                    mWindowIdToWindowTokenMap.remove(windowId);
+                    mWindowIdToInteractionConnectionMap.remove(windowId);
+                    if (DEBUG) {
+                        Slog.i(LOG_TAG, "Removing interaction connection to windowId: " + windowId);
+                    }
+                    return;
+                }
+            }
+        }
+    }
+
+    public IAccessibilityServiceConnection registerEventListener(IEventListener listener) {
+        mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT,
+                FUNCTION_REGISTER_EVENT_LISTENER);
+        ComponentName componentName = new ComponentName("foo.bar", "FakeAccessibilityService");
+        synchronized (mLock) {
+            Service oldService = mComponentNameToServiceMap.get(componentName);
+            if (oldService != null) {
+                tryRemoveServiceLocked(oldService);
+            }
+        }
+        AccessibilityServiceInfo accessibilityServiceInfo = new AccessibilityServiceInfo();
+        accessibilityServiceInfo.eventTypes = AccessibilityEvent.TYPES_ALL_MASK;
+        accessibilityServiceInfo.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
+        Service service = new Service(componentName, accessibilityServiceInfo, true);
+        service.onServiceConnected(componentName, listener.asBinder());
+        return service;
+    }
+
     /**
      * Populates the cached list of installed {@link AccessibilityService}s.
      */
@@ -488,6 +567,12 @@
         AccessibilityEvent event = service.mPendingEvents.get(eventType);
 
         try {
+            if (mSecurityPolicy.canRetrieveWindowContent(service)) {
+                event.setConnection(service);
+            } else {
+                event.setSource(null);
+            }
+            event.setSealed(true);
             listener.onAccessibilityEvent(event);
             if (DEBUG) {
                 Slog.i(LOG_TAG, "Event " + event + " sent to " + listener);
@@ -646,7 +731,7 @@
             if (isEnabled) {
                 if (enabledServices.contains(componentName)) {
                     if (service == null) {
-                        service = new Service(installedService);
+                        service = new Service(componentName, installedService, false);
                     }
                     service.bind();
                 } else if (!enabledServices.contains(componentName)) {
@@ -683,8 +768,6 @@
      * spoken feedback.
      */
     private void updateInputFilterLocked() {
-        WindowManagerService wm = (WindowManagerService)ServiceManager.getService(
-                Context.WINDOW_SERVICE);
         if (mIsEnabled) {
             final boolean hasSpokenFeedbackServices = !getEnabledAccessibilityServiceList(
                     AccessibilityServiceInfo.FEEDBACK_SPOKEN).isEmpty();
@@ -695,19 +778,14 @@
                 if (mInputFilter == null) {
                     mInputFilter = new AccessibilityInputFilter(mContext);
                 }
-                wm.setInputFilter(mInputFilter);
+                mWindowManagerService.setInputFilter(mInputFilter);
                 mHasInputFilter = true;
-            } else {
-                if (mHasInputFilter) {
-                    wm.setInputFilter(null);
-                    mHasInputFilter = false;
-                }
+                return;
             }
-        } else {
-            if (mHasInputFilter) {
-                wm.setInputFilter(null);
-                mHasInputFilter = false;
-            }
+        }
+        if (mHasInputFilter) {
+            mWindowManagerService.setInputFilter(null);
+            mHasInputFilter = false;
         }
     }
 
@@ -743,24 +821,38 @@
 
         Intent mIntent;
 
+        boolean mCanRetrieveScreenContent;
+
+        boolean mIsFake;
+
+        final Callback mCallback = new Callback();
+
+        final AtomicInteger mInteractionIdCounter = new AtomicInteger();
+
         // the events pending events to be dispatched to this service
         final SparseArray<AccessibilityEvent> mPendingEvents =
             new SparseArray<AccessibilityEvent>();
 
-        Service(AccessibilityServiceInfo accessibilityServiceInfo) {
+        public Service(ComponentName componentName,
+                AccessibilityServiceInfo accessibilityServiceInfo, boolean isFake) {
             mId = sIdCounter++;
-            setAccessibilityServiceInfo(accessibilityServiceInfo);
-            mIntent = new Intent().setComponent(mComponentName);
-            mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
-                    com.android.internal.R.string.accessibility_binding_label);
-            mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
-                    mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
+            mComponentName = componentName;
+            mAccessibilityServiceInfo = accessibilityServiceInfo;
+            mIsFake = isFake;
+            if (!isFake) {
+                mCanRetrieveScreenContent = accessibilityServiceInfo.getCanRetrieveWindowContent();
+                mIntent = new Intent().setComponent(mComponentName);
+                mIntent.putExtra(Intent.EXTRA_CLIENT_LABEL,
+                        com.android.internal.R.string.accessibility_binding_label);
+                mIntent.putExtra(Intent.EXTRA_CLIENT_INTENT, PendingIntent.getActivity(
+                        mContext, 0, new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS), 0));
+            } else {
+                mCanRetrieveScreenContent = true;
+            }
+            setDynamicallyConfigurableProperties(accessibilityServiceInfo);
         }
 
-        public void setAccessibilityServiceInfo(AccessibilityServiceInfo info) {
-            mAccessibilityServiceInfo = info;
-            ServiceInfo serviceInfo = info.getResolveInfo().serviceInfo;
-            mComponentName = new ComponentName(serviceInfo.packageName, serviceInfo.name);
+        public void setDynamicallyConfigurableProperties(AccessibilityServiceInfo info) {
             mEventTypes = info.eventTypes;
             mFeedbackType = info.feedbackType;
             String[] packageNames = info.packageNames;
@@ -781,7 +873,7 @@
          * @return True if binding is successful.
          */
         public boolean bind() {
-            if (mService == null) {
+            if (!mIsFake && mService == null) {
                 return mContext.bindService(mIntent, this, Context.BIND_AUTO_CREATE);
             }
             return false;
@@ -798,7 +890,9 @@
                 synchronized (mLock) {
                     tryRemoveServiceLocked(this);
                 }
-                mContext.unbindService(this);
+                if (!mIsFake) {
+                    mContext.unbindService(this);
+                }
                 mService = null;
                 return true;
             }
@@ -832,6 +926,154 @@
             }
         }
 
+        public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int viewId) {
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+                if (permissionGranted) {
+                    connection = getConnectionToRetrievalAllowingWindowLocked();
+                }
+            }
+            if (connection == null) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "No interaction connection to a retrieve allowing window.");
+                }
+                return null;
+            }
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                connection.findAccessibilityNodeInfoByViewId(viewId, interactionId, mCallback);
+                AccessibilityNodeInfo info = mCallback.getFindAccessibilityNodeInfoResultAndClear(
+                        interactionId);
+                if (info != null) {
+                    info.setConnection(this);
+                }
+                info.setSealed(true);
+                return info;
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error finding node.");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return null;
+        }
+
+        public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewText(String text) {
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                final boolean permissionGranted = mSecurityPolicy.canRetrieveWindowContent(this);
+                if (permissionGranted) {
+                    connection = getConnectionToRetrievalAllowingWindowLocked();
+                }
+            }
+            if (connection == null) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "No interaction connection to focused window.");
+                }
+                return null;
+            }
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                connection.findAccessibilityNodeInfosByViewText(text, interactionId, mCallback);
+                List<AccessibilityNodeInfo> infos =
+                    mCallback.getFindAccessibilityNodeInfosResultAndClear(interactionId);
+                if (infos != null) {
+                    final int infoCount = infos.size();
+                    for (int i = 0; i < infoCount; i++) {
+                        AccessibilityNodeInfo info = infos.get(i);
+                        info.setConnection(this);
+                        info.setSealed(true);
+                    }
+                }
+                return infos;
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error finding node.");
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return null;
+        }
+
+        public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
+                int accessibilityWindowId, int accessibilityViewId) {
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                final boolean permissionGranted =
+                    mSecurityPolicy.canGetAccessibilityNodeInfoLocked(this, accessibilityWindowId);
+                if (permissionGranted) {
+                    connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
+                }
+            }
+            if (connection == null) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "No interaction connection to window: "
+                            + accessibilityWindowId);
+                }
+                return null;
+            }
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                connection.findAccessibilityNodeInfoByAccessibilityId(accessibilityViewId,
+                        interactionId, mCallback);
+                AccessibilityNodeInfo info =
+                     mCallback.getFindAccessibilityNodeInfoResultAndClear(interactionId);
+                if (info != null) {
+                    info.setConnection(this);
+                    info.setSealed(true);
+                }
+                return info;
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
+                            + accessibilityViewId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return null;
+        }
+
+        public boolean performAccessibilityAction(int accessibilityWindowId,
+                int accessibilityViewId, int action) {
+            IAccessibilityInteractionConnection connection = null;
+            synchronized (mLock) {
+                final boolean permissionGranted = mSecurityPolicy.canPerformActionLocked(this,
+                        accessibilityWindowId, action);
+                if (permissionGranted) {
+                    connection = mWindowIdToInteractionConnectionMap.get(accessibilityWindowId);
+                }
+            }
+            if (connection == null) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "No interaction connection to window: "
+                            + accessibilityWindowId);
+                }
+                return false;
+            }
+            final long identityToken = Binder.clearCallingIdentity();
+            try {
+                final int interactionId = mInteractionIdCounter.getAndIncrement();
+                connection.performAccessibilityAction(accessibilityViewId, action, interactionId,
+                        mCallback);
+                return mCallback.getPerformAccessibilityActionResult(interactionId);
+            } catch (RemoteException re) {
+                if (DEBUG) {
+                    Slog.e(LOG_TAG, "Error requesting node with accessibilityViewId: "
+                            + accessibilityViewId);
+                }
+            } finally {
+                Binder.restoreCallingIdentity(identityToken);
+            }
+            return false;
+        }
+
         public void onServiceDisconnected(ComponentName componentName) {
             /* do nothing - #binderDied takes care */
         }
@@ -849,5 +1091,171 @@
                 tryRemoveServiceLocked(this);
             }
         }
+
+        private IAccessibilityInteractionConnection getConnectionToRetrievalAllowingWindowLocked() {
+            final int windowId = mSecurityPolicy.getRetrievalAllowingWindowLocked();
+            if (DEBUG) {
+                Slog.i(LOG_TAG, "Trying to get interaction connection to windowId: " + windowId);
+            }
+            return mWindowIdToInteractionConnectionMap.get(windowId);
+        }
+    }
+
+    final class SecurityPolicy {
+        private static final int VALID_ACTIONS = AccessibilityNodeInfo.ACTION_FOCUS
+            | AccessibilityNodeInfo.ACTION_CLEAR_FOCUS | AccessibilityNodeInfo.ACTION_SELECT
+            | AccessibilityNodeInfo.ACTION_CLEAR_SELECTION;
+
+        private static final int RETRIEVAL_ALLOWING_EVENT_TYPES =
+            AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_END
+            | AccessibilityEvent.TYPE_TOUCH_EXPLORATION_GESTURE_START
+            | AccessibilityEvent.TYPE_VIEW_CLICKED | AccessibilityEvent.TYPE_VIEW_FOCUSED
+            | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT
+            | AccessibilityEvent.TYPE_VIEW_LONG_CLICKED | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
+            | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
+
+        private int mRetrievalAlowingWindowId;
+
+        public void updateRetrievalAllowingWindowAndEventSourceLocked(AccessibilityEvent event) {
+            final int windowId = event.getSourceAccessibilityWindowId();
+            final int eventType = event.getEventType();
+            if ((eventType & RETRIEVAL_ALLOWING_EVENT_TYPES) != 0) {
+                mRetrievalAlowingWindowId = windowId;
+            } else {
+                event.setSource(null);
+            }
+        }
+
+        public int getRetrievalAllowingWindowLocked() {
+            return mRetrievalAlowingWindowId;
+        }
+
+        public boolean canGetAccessibilityNodeInfoLocked(Service service, int windowId) {
+            return canRetrieveWindowContent(service) && isRetrievalAllowingWindow(windowId);
+        }
+
+        public boolean canPerformActionLocked(Service service, int windowId, int action) {
+            return canRetrieveWindowContent(service)
+                && isRetrievalAllowingWindow(windowId)
+                && isActionPermitted(action);
+        }
+
+        public boolean canRetrieveWindowContent(Service service) {
+            return service.mCanRetrieveScreenContent;
+        }
+
+        private boolean isRetrievalAllowingWindow(int windowId) {
+            return (mRetrievalAlowingWindowId == windowId);
+        }
+
+        private boolean isActionPermitted(int action) {
+             return (VALID_ACTIONS & action) != 0;
+        }
+
+        private void enforceCallingPermission(String permission, String function) {
+            if (OWN_PROCESS_ID == Binder.getCallingPid()) {
+                return;
+            }
+            final int permissionStatus = mContext.checkCallingPermission(permission);
+            if (permissionStatus != PackageManager.PERMISSION_GRANTED) {
+                throw new SecurityException("You do not have " + permission
+                        + " required to call " + function);
+            }
+        }
+    }
+
+    final class Callback extends IAccessibilityInteractionConnectionCallback.Stub {
+        private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
+
+        private int mInteractionId = -1;
+        private AccessibilityNodeInfo mFindAccessibilityNodeInfoResult;
+        private List<AccessibilityNodeInfo> mFindAccessibilityNodeInfosResult;
+        private boolean mPerformAccessibilityActionResult;
+
+        public void setFindAccessibilityNodeInfoResult(AccessibilityNodeInfo info,
+                int interactionId) {
+            synchronized (mLock) {
+                if (interactionId > mInteractionId) {
+                    mFindAccessibilityNodeInfoResult = info;
+                    mInteractionId = interactionId;
+                }
+                mLock.notifyAll();
+            }
+        }
+
+        public AccessibilityNodeInfo getFindAccessibilityNodeInfoResultAndClear(int interactionId) {
+            synchronized (mLock) {
+                waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
+                AccessibilityNodeInfo result = mFindAccessibilityNodeInfoResult;
+                clearLocked();
+                return result;
+            }
+        }
+
+        public void setFindAccessibilityNodeInfosResult(List<AccessibilityNodeInfo> infos,
+                int interactionId) {
+            synchronized (mLock) {
+                if (interactionId > mInteractionId) {
+                    mFindAccessibilityNodeInfosResult = infos;
+                    mInteractionId = interactionId;
+                }
+                mLock.notifyAll();
+            }
+        }
+
+        public List<AccessibilityNodeInfo> getFindAccessibilityNodeInfosResultAndClear(
+                int interactionId) {
+            synchronized (mLock) {
+                waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
+                List<AccessibilityNodeInfo> result = mFindAccessibilityNodeInfosResult;
+                clearLocked();
+                return result;
+            }
+        }
+
+        public void setPerformAccessibilityActionResult(boolean succeeded, int interactionId) {
+            synchronized (mLock) {
+                if (interactionId > mInteractionId) {
+                    mPerformAccessibilityActionResult = succeeded;
+                    mInteractionId = interactionId;
+                }
+                mLock.notifyAll();
+            }
+        }
+
+        public boolean getPerformAccessibilityActionResult(int interactionId) {
+            synchronized (mLock) {
+                waitForResultTimedLocked(TIMEOUT_INTERACTION_MILLIS, interactionId);
+                final boolean result = mPerformAccessibilityActionResult;
+                clearLocked();
+                return result;
+            }
+        }
+
+        public void clearLocked() {
+            mInteractionId = -1;
+            mFindAccessibilityNodeInfoResult = null;
+            mFindAccessibilityNodeInfosResult = null;
+            mPerformAccessibilityActionResult = false;
+        }
+
+        private void waitForResultTimedLocked(long waitTimeMillis, int interactionId) {
+            final long startTimeMillis = SystemClock.uptimeMillis();
+            while (true) {
+                try {
+                    if (mInteractionId == interactionId) {
+                        return;
+                    }
+                    final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
+                    waitTimeMillis = TIMEOUT_INTERACTION_MILLIS - elapsedTimeMillis;
+                    if (waitTimeMillis <= 0) {
+                        return;
+                    }
+                    mLock.wait(waitTimeMillis);
+                } catch (InterruptedException ie) {
+                    /* ignore */
+                }
+            }
+        }
     }
 }
diff --git a/services/java/com/android/server/accessibility/TouchExplorer.java b/services/java/com/android/server/accessibility/TouchExplorer.java
index 4c7f595..aab189a6 100644
--- a/services/java/com/android/server/accessibility/TouchExplorer.java
+++ b/services/java/com/android/server/accessibility/TouchExplorer.java
@@ -226,8 +226,9 @@
                 // Send a hover for every finger down so the user gets feedback
                 // where she is currently touching.
                 mSendHoverDelayed.forceSendAndRemove();
-                mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, 1, policyFlags,
-                        DELAY_SEND_HOVER_MOVE);
+                final int pointerIdBits = (1 << event.getActionIndex());
+                mSendHoverDelayed.post(event, MotionEvent.ACTION_HOVER_ENTER, pointerIdBits,
+                        policyFlags, DELAY_SEND_HOVER_MOVE);
             } break;
             case MotionEvent.ACTION_POINTER_DOWN: {
                 switch (activePointerCount) {
@@ -350,11 +351,12 @@
                         if (isDraggingGesture(event)) {
                             // Two pointers moving in the same direction within
                             // a given distance perform a drag.
-                            mCurrentState = STATE_DRAGGING;      
+                            mCurrentState = STATE_DRAGGING;
                             if (mTouchExploreGestureInProgress) {
                                 sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
                                 mTouchExploreGestureInProgress = false;
                             }
+                            mLastTouchExploreEvent = null;
                             mDraggingPointerId = pointerId;
                             sendMotionEvent(event, MotionEvent.ACTION_DOWN, pointerIdBits,
                                     policyFlags);
@@ -365,6 +367,7 @@
                                 sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
                                 mTouchExploreGestureInProgress = false;
                             }
+                            mLastTouchExploreEvent = null;
                             sendDownForAllActiveNotInjectedPointers(event, policyFlags);
                         }
                     } break;
@@ -382,6 +385,7 @@
                             sendAccessibilityEvent(TYPE_TOUCH_EXPLORATION_GESTURE_END);
                             mTouchExploreGestureInProgress = false;
                         }
+                        mLastTouchExploreEvent = null;
                         sendDownForAllActiveNotInjectedPointers(event, policyFlags);
                     }
                 }
@@ -728,7 +732,7 @@
     /**
      * Sends an event.
      *
-     * @param event The event to send.
+     * @param prototype The prototype from which to create the injected events.
      * @param action The action of the event.
      * @param pointerIdBits The bits of the pointers to send.
      * @param policyFlags The policy flags associated with the event.
@@ -979,7 +983,7 @@
                 case MotionEvent.ACTION_DOWN: {
                     // New gesture so restart tracking injected down pointers.
                     mInjectedPointersDown = 0;
-                    handleReceivedPointerDown(0, event);
+                    handleReceivedPointerDown(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_POINTER_DOWN: {
                     handleReceivedPointerDown(event.getActionIndex(), event);
@@ -988,7 +992,7 @@
                     handleReceivedPointerMove(event);
                 } break;
                 case MotionEvent.ACTION_UP: {
-                    handleReceivedPointerUp(0, event);
+                    handleReceivedPointerUp(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_POINTER_UP: {
                     handleReceivedPointerUp(event.getActionIndex(), event);
@@ -1008,13 +1012,13 @@
             final int action = event.getActionMasked();
             switch (action) {
                 case MotionEvent.ACTION_DOWN: {
-                    handleInjectedPointerDown(0, event);
+                    handleInjectedPointerDown(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_POINTER_DOWN: {
                     handleInjectedPointerDown(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_UP: {
-                    handleInjectedPointerUp(0, event);
+                    handleInjectedPointerUp(event.getActionIndex(), event);
                 } break;
                 case MotionEvent.ACTION_POINTER_UP: {
                     handleInjectedPointerUp(event.getActionIndex(), event);
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c2a8a1d..4f0265c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -92,7 +92,6 @@
 import android.os.FileUtils;
 import android.os.Handler;
 import android.os.IBinder;
-import android.os.IInterface;
 import android.os.IPermissionController;
 import android.os.Looper;
 import android.os.Message;
@@ -1282,7 +1281,7 @@
                         mCompatModeDialog.dismiss();
                         mCompatModeDialog = null;
                     }
-                    if (ar != null) {
+                    if (ar != null && false) {
                         if (mCompatModePackages.getPackageAskCompatModeLocked(
                                 ar.packageName)) {
                             int mode = mCompatModePackages.computeCompatModeLocked(
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
index e8155b4..5fa26ef 100644
--- a/services/java/com/android/server/connectivity/Tethering.java
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -129,13 +129,6 @@
         mNMService = nmService;
         mLooper = looper;
 
-        // register for notifications from NetworkManagement Service
-        try {
-            mNMService.registerObserver(this);
-        } catch (RemoteException e) {
-            Log.e(TAG, "Error registering observer :" + e);
-        }
-
         mIfaces = new HashMap<String, TetherInterfaceSM>();
 
         // make our own thread so we don't anr the system
@@ -1212,8 +1205,8 @@
                     return null;
                 }
 
-                for (String iface : ifaces) {
-                    for (String regex : mUpstreamIfaceRegexs) {
+                for (String regex : mUpstreamIfaceRegexs) {
+                    for (String iface : ifaces) {
                         if (iface.matches(regex)) {
                             // verify it is active
                             InterfaceConfiguration ifcg = null;
diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java
new file mode 100644
index 0000000..b754dba
--- /dev/null
+++ b/services/java/com/android/server/connectivity/Vpn.java
@@ -0,0 +1,258 @@
+/*
+ * 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 com.android.server.connectivity;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.Drawable;
+import android.net.INetworkManagementEventObserver;
+import android.os.Binder;
+import android.os.Bundle;
+import android.os.ParcelFileDescriptor;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.R;
+import com.android.server.ConnectivityService.VpnCallback;
+
+/**
+ * @hide
+ */
+public class Vpn extends INetworkManagementEventObserver.Stub {
+
+    private final static String TAG = "Vpn";
+    private final static String VPN = android.Manifest.permission.VPN;
+
+    private final Context mContext;
+    private final VpnCallback mCallback;
+
+    private String mPackageName;
+    private String mInterfaceName;
+    private String mDnsPropertyPrefix;
+
+    public Vpn(Context context, VpnCallback callback) {
+        mContext = context;
+        mCallback = callback;
+    }
+
+    /**
+     * Prepare for a VPN application.
+     *
+     * @param packageName The package name of the new VPN application.
+     * @return The name of the current prepared package.
+     */
+    public synchronized String prepare(String packageName) {
+
+        // TODO: Check if the caller is VpnDialogs.
+
+        if (packageName == null) {
+            return mPackageName;
+        }
+
+        // Check the permission of the given application.
+        PackageManager pm = mContext.getPackageManager();
+        if (pm.checkPermission(VPN, packageName) != PackageManager.PERMISSION_GRANTED) {
+            throw new SecurityException(packageName + " does not have " + VPN);
+        }
+
+        // Reset the interface and hide the notification.
+        if (mInterfaceName != null) {
+            nativeReset(mInterfaceName);
+            mInterfaceName = null;
+            hideNotification();
+            // TODO: Send out a broadcast.
+        }
+
+        mPackageName = packageName;
+        Log.i(TAG, "Prepared for " + packageName);
+        return mPackageName;
+    }
+
+    /**
+     * Protect a socket from routing changes by binding it to the given
+     * interface. The socket is NOT closed by this method.
+     *
+     * @param socket The socket to be bound.
+     * @param name The name of the interface.
+     */
+    public void protect(ParcelFileDescriptor socket, String name) {
+        mContext.enforceCallingPermission(VPN, "protect");
+        nativeProtect(socket.getFd(), name);
+    }
+
+    /**
+     * Configure a TUN interface and return its file descriptor.
+     *
+     * @param configuration The parameters to configure the interface.
+     * @return The file descriptor of the interface.
+     */
+    public synchronized ParcelFileDescriptor establish(Bundle config) {
+        // Check the permission of the caller.
+        mContext.enforceCallingPermission(VPN, "establish");
+
+        // Check if the caller is already prepared.
+        PackageManager pm = mContext.getPackageManager();
+        ApplicationInfo app = null;
+        try {
+            app = pm.getApplicationInfo(mPackageName, 0);
+        } catch (Exception e) {
+            throw new SecurityException("Not prepared");
+        }
+        if (Binder.getCallingUid() != app.uid) {
+            throw new SecurityException("Not prepared");
+        }
+
+        // Unpack the config.
+        // TODO: move constants into VpnBuilder.
+        String session = config.getString("session");
+        String addresses = config.getString("addresses");
+        String routes = config.getString("routes");
+        String dnsServers = config.getString("dnsServers");
+
+        // Create interface and configure addresses and routes.
+        ParcelFileDescriptor descriptor = nativeConfigure(addresses, routes);
+
+        // Replace the interface and abort if it fails.
+        try {
+            String interfaceName = nativeGetName(descriptor.getFd());
+
+            if (mInterfaceName != null && !mInterfaceName.equals(interfaceName)) {
+                nativeReset(mInterfaceName);
+            }
+            mInterfaceName = interfaceName;
+        } catch (RuntimeException e) {
+            try {
+                descriptor.close();
+            } catch (Exception ex) {
+                // ignore
+            }
+            throw e;
+        }
+
+        dnsServers = (dnsServers == null) ? "" : dnsServers.trim();
+        mCallback.override(dnsServers.isEmpty() ? null : dnsServers.split(" "));
+
+        showNotification(pm, app, session);
+        return descriptor;
+    }
+
+    public synchronized boolean onInterfaceRemoved(String name) {
+        if (name.equals(mInterfaceName) && nativeCheck(name) == 0) {
+            hideNotification();
+            mInterfaceName = null;
+            return true;
+        }
+        return false;
+    }
+
+    // INetworkManagementEventObserver.Stub
+    public void interfaceLinkStatusChanged(String name, boolean up) {
+    }
+
+    // INetworkManagementEventObserver.Stub
+    public void interfaceAdded(String name) {
+    }
+
+    // INetworkManagementEventObserver.Stub
+    public synchronized void interfaceRemoved(String name) {
+        if (name.equals(mInterfaceName) && nativeCheck(name) == 0) {
+            hideNotification();
+            mInterfaceName = null;
+            mCallback.restore();
+        }
+    }
+
+    private void showNotification(PackageManager pm, ApplicationInfo app, String session) {
+        NotificationManager nm = (NotificationManager)
+                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+        if (nm != null) {
+            // Load the icon and convert it into a bitmap.
+            Drawable icon = app.loadIcon(pm);
+            Bitmap bitmap = null;
+            if (icon.getIntrinsicWidth() > 0 && icon.getIntrinsicHeight() > 0) {
+                int width = mContext.getResources().getDimensionPixelSize(
+                        android.R.dimen.notification_large_icon_width);
+                int height = mContext.getResources().getDimensionPixelSize(
+                        android.R.dimen.notification_large_icon_height);
+                icon.setBounds(0, 0, width, height);
+                bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+                icon.draw(new Canvas(bitmap));
+            }
+
+            // Load the label.
+            String label = app.loadLabel(pm).toString();
+
+            // If session is null, use the application name instead.
+            if (session == null) {
+                session = label;
+            }
+
+            // Build the intent.
+            // TODO: move these into VpnBuilder.
+            Intent intent = new Intent();
+            intent.setClassName("com.android.vpndialogs",
+                    "com.android.vpndialogs.ManageDialog");
+            intent.putExtra("packageName", mPackageName);
+            intent.putExtra("interfaceName", mInterfaceName);
+            intent.putExtra("session", session);
+            intent.putExtra("startTime", android.os.SystemClock.elapsedRealtime());
+            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+
+            // Build the notification.
+            long identity = Binder.clearCallingIdentity();
+            Notification notification = new Notification.Builder(mContext)
+                    .setSmallIcon(R.drawable.vpn_connected)
+                    .setLargeIcon(bitmap)
+                    .setTicker(mContext.getString(R.string.vpn_ticker, label))
+                    .setContentTitle(mContext.getString(R.string.vpn_title, label))
+                    .setContentText(mContext.getString(R.string.vpn_text, session))
+                    .setContentIntent(PendingIntent.getActivity(mContext, 0, intent, 0))
+                    .setDefaults(Notification.DEFAULT_ALL)
+                    .setOngoing(true)
+                    .getNotification();
+
+            nm.notify(R.drawable.vpn_connected, notification);
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private void hideNotification() {
+        NotificationManager nm = (NotificationManager)
+                mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+
+        if (nm != null) {
+            long identity = Binder.clearCallingIdentity();
+            nm.cancel(R.drawable.vpn_connected);
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    private native ParcelFileDescriptor nativeConfigure(String addresses, String routes);
+    private native String nativeGetName(int fd);
+    private native void nativeReset(String name);
+    private native int nativeCheck(String name);
+    private native void nativeProtect(int fd, String name);
+}
diff --git a/services/java/com/android/server/net/InterfaceIdentity.java b/services/java/com/android/server/net/InterfaceIdentity.java
new file mode 100644
index 0000000..ff86581
--- /dev/null
+++ b/services/java/com/android/server/net/InterfaceIdentity.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 com.android.server.net;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+import java.util.HashSet;
+
+/**
+ * Identity of a {@code iface}, defined by the set of {@link NetworkIdentity}
+ * active on that interface.
+ *
+ * @hide
+ */
+public class InterfaceIdentity extends HashSet<NetworkIdentity> {
+    private static final int VERSION_CURRENT = 1;
+
+    public InterfaceIdentity() {
+    }
+
+    public InterfaceIdentity(DataInputStream in) throws IOException {
+        final int version = in.readInt();
+        switch (version) {
+            case VERSION_CURRENT: {
+                final int size = in.readInt();
+                for (int i = 0; i < size; i++) {
+                    add(new NetworkIdentity(in));
+                }
+                break;
+            }
+            default: {
+                throw new ProtocolException("unexpected version: " + version);
+            }
+        }
+    }
+
+    public void writeToStream(DataOutputStream out) throws IOException {
+        out.writeInt(VERSION_CURRENT);
+        out.writeInt(size());
+        for (NetworkIdentity ident : this) {
+            ident.writeToStream(out);
+        }
+    }
+
+    /**
+     * Test if any {@link NetworkIdentity} on this interface matches the given
+     * template and IMEI.
+     */
+    public boolean matchesTemplate(int networkTemplate, String subscriberId) {
+        for (NetworkIdentity ident : this) {
+            if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
diff --git a/services/java/com/android/server/net/NetworkIdentity.java b/services/java/com/android/server/net/NetworkIdentity.java
new file mode 100644
index 0000000..f7a7c49
--- /dev/null
+++ b/services/java/com/android/server/net/NetworkIdentity.java
@@ -0,0 +1,225 @@
+/*
+ * 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 com.android.server.net;
+
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.TYPE_WIMAX;
+import static android.net.ConnectivityManager.isNetworkTypeMobile;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_4G;
+import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL;
+import static android.net.TrafficStats.TEMPLATE_WIFI;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_2_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_3_G;
+import static android.telephony.TelephonyManager.NETWORK_CLASS_4_G;
+import static android.telephony.TelephonyManager.getNetworkClass;
+
+import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
+import android.telephony.TelephonyManager;
+
+import com.android.internal.util.Objects;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.ProtocolException;
+
+/**
+ * Identity of a {@link NetworkInfo}, defined by network type and billing
+ * relationship (such as IMSI).
+ *
+ * @hide
+ */
+public class NetworkIdentity {
+    private static final int VERSION_CURRENT = 1;
+
+    public final int type;
+    public final int subType;
+    public final String subscriberId;
+
+    public NetworkIdentity(int type, int subType, String subscriberId) {
+        this.type = type;
+        this.subType = subType;
+        this.subscriberId = subscriberId;
+    }
+
+    public NetworkIdentity(DataInputStream in) throws IOException {
+        final int version = in.readInt();
+        switch (version) {
+            case VERSION_CURRENT: {
+                type = in.readInt();
+                subType = in.readInt();
+                subscriberId = readOptionalString(in);
+                break;
+            }
+            default: {
+                throw new ProtocolException("unexpected version: " + version);
+            }
+        }
+    }
+
+    public void writeToStream(DataOutputStream out) throws IOException {
+        out.writeInt(VERSION_CURRENT);
+        out.writeInt(type);
+        out.writeInt(subType);
+        writeOptionalString(out, subscriberId);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(type, subType, subscriberId);
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof NetworkIdentity) {
+            final NetworkIdentity ident = (NetworkIdentity) obj;
+            return type == ident.type && subType == ident.subType
+                    && Objects.equal(subscriberId, ident.subscriberId);
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        final String typeName = ConnectivityManager.getNetworkTypeName(type);
+        final String subTypeName;
+        if (ConnectivityManager.isNetworkTypeMobile(type)) {
+            subTypeName = TelephonyManager.getNetworkTypeName(subType);
+        } else {
+            subTypeName = Integer.toString(subType);
+        }
+
+        return "[type=" + typeName + ", subType=" + subTypeName + ", subId=" + subscriberId + "]";
+    }
+
+    /**
+     * Test if this network matches the given template and IMEI.
+     */
+    public boolean matchesTemplate(int networkTemplate, String subscriberId) {
+        switch (networkTemplate) {
+            case TEMPLATE_MOBILE_ALL:
+                return matchesMobile(subscriberId);
+            case TEMPLATE_MOBILE_3G_LOWER:
+                return matchesMobile3gLower(subscriberId);
+            case TEMPLATE_MOBILE_4G:
+                return matchesMobile4g(subscriberId);
+            case TEMPLATE_WIFI:
+                return matchesWifi();
+            default:
+                throw new IllegalArgumentException("unknown network template");
+        }
+    }
+
+    /**
+     * Check if mobile network with matching IMEI. Also matches
+     * {@link #TYPE_WIMAX}.
+     */
+    private boolean matchesMobile(String subscriberId) {
+        if (isNetworkTypeMobile(type) && Objects.equal(this.subscriberId, subscriberId)) {
+            return true;
+        } else if (type == TYPE_WIMAX) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if mobile network classified 3G or lower with matching IMEI.
+     */
+    private boolean matchesMobile3gLower(String subscriberId) {
+        if (isNetworkTypeMobile(type)
+                && Objects.equal(this.subscriberId, subscriberId)) {
+            switch (getNetworkClass(subType)) {
+                case NETWORK_CLASS_2_G:
+                case NETWORK_CLASS_3_G:
+                    return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if mobile network classified 4G with matching IMEI. Also matches
+     * {@link #TYPE_WIMAX}.
+     */
+    private boolean matchesMobile4g(String subscriberId) {
+        if (isNetworkTypeMobile(type)
+                && Objects.equal(this.subscriberId, subscriberId)) {
+            switch (getNetworkClass(subType)) {
+                case NETWORK_CLASS_4_G:
+                    return true;
+            }
+        } else if (type == TYPE_WIMAX) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Check if matches Wi-Fi network template.
+     */
+    private boolean matchesWifi() {
+        if (type == TYPE_WIFI) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Build a {@link NetworkIdentity} from the given {@link NetworkState},
+     * assuming that any mobile networks are using the current IMSI.
+     */
+    public static NetworkIdentity buildNetworkIdentity(Context context, NetworkState state) {
+        final int type = state.networkInfo.getType();
+        final int subType = state.networkInfo.getSubtype();
+
+        // TODO: consider moving subscriberId over to LinkCapabilities, so it
+        // comes from an authoritative source.
+
+        final String subscriberId;
+        if (isNetworkTypeMobile(type)) {
+            final TelephonyManager telephony = (TelephonyManager) context.getSystemService(
+                    Context.TELEPHONY_SERVICE);
+            subscriberId = telephony.getSubscriberId();
+        } else {
+            subscriberId = null;
+        }
+        return new NetworkIdentity(type, subType, subscriberId);
+    }
+
+    private static void writeOptionalString(DataOutputStream out, String value) throws IOException {
+        if (value != null) {
+            out.writeByte(1);
+            out.writeUTF(value);
+        } else {
+            out.writeByte(0);
+        }
+    }
+
+    private static String readOptionalString(DataInputStream in) throws IOException {
+        if (in.readByte() != 0) {
+            return in.readUTF();
+        } else {
+            return null;
+        }
+    }
+
+}
diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java
index 17c7161..1a90a92 100644
--- a/services/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -89,6 +89,8 @@
     // TODO: keep record of billing cycle details, and limit rules
     // TODO: keep map of interfaces-to-billing-relationship
 
+    // TODO: dispatch callbacks through handler when locked
+
     public NetworkPolicyManagerService(Context context, IActivityManager activityManager,
             IPowerManager powerManager, INetworkStatsService networkStats) {
         mContext = checkNotNull(context, "missing context");
diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java
index d9c1f25..8db2839 100644
--- a/services/java/com/android/server/net/NetworkStatsService.java
+++ b/services/java/com/android/server/net/NetworkStatsService.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008 The Android Open Source Project
+ * 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.
@@ -16,12 +16,23 @@
 
 package com.android.server.net;
 
+import static android.Manifest.permission.CONNECTIVITY_INTERNAL;
 import static android.Manifest.permission.DUMP;
 import static android.Manifest.permission.SHUTDOWN;
 import static android.Manifest.permission.UPDATE_DEVICE_STATS;
-import static android.net.ConnectivityManager.TYPE_MOBILE;
-import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.NetworkStats.IFACE_ALL;
 import static android.net.NetworkStats.UID_ALL;
+import static android.provider.Settings.Secure.NETSTATS_DETAIL_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_DETAIL_MAX_HISTORY;
+import static android.provider.Settings.Secure.NETSTATS_PERSIST_THRESHOLD;
+import static android.provider.Settings.Secure.NETSTATS_POLL_INTERVAL;
+import static android.provider.Settings.Secure.NETSTATS_SUMMARY_BUCKET_DURATION;
+import static android.provider.Settings.Secure.NETSTATS_SUMMARY_MAX_HISTORY;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
+import static com.android.internal.util.Preconditions.checkNotNull;
 
 import android.app.AlarmManager;
 import android.app.IAlarmManager;
@@ -30,29 +41,45 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.net.IConnectivityManager;
 import android.net.INetworkStatsService;
-import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkState;
 import android.net.NetworkStats;
 import android.net.NetworkStatsHistory;
-import android.net.wifi.WifiManager;
+import android.os.Environment;
 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
 import android.os.SystemClock;
+import android.provider.Settings;
 import android.telephony.TelephonyManager;
-import android.text.format.DateUtils;
 import android.util.NtpTrustedTime;
 import android.util.Slog;
+import android.util.SparseArray;
 import android.util.TrustedTime;
 
-import com.android.internal.telephony.Phone;
-import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.os.AtomicFile;
+import com.google.android.collect.Lists;
 import com.google.android.collect.Maps;
 
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.File;
 import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
 import java.io.PrintWriter;
+import java.net.ProtocolException;
+import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
+
+import libcore.io.IoUtils;
 
 /**
  * Collect and persist detailed network statistics, and provide this data to
@@ -62,48 +89,66 @@
     private static final String TAG = "NetworkStatsService";
     private static final boolean LOGD = true;
 
+    /** File header magic number: "ANET" */
+    private static final int FILE_MAGIC = 0x414E4554;
+    private static final int VERSION_CURRENT = 1;
+
     private final Context mContext;
     private final INetworkManagementService mNetworkManager;
     private final IAlarmManager mAlarmManager;
     private final TrustedTime mTime;
 
-    private static final String ACTION_NETWORK_STATS_POLL =
+    private IConnectivityManager mConnManager;
+
+    // @VisibleForTesting
+    public static final String ACTION_NETWORK_STATS_POLL =
             "com.android.server.action.NETWORK_STATS_POLL";
 
     private PendingIntent mPollIntent;
 
-    // TODO: move tweakable params to Settings.Secure
     // TODO: listen for kernel push events through netd instead of polling
 
     private static final long KB_IN_BYTES = 1024;
+    private static final long MB_IN_BYTES = 1024 * KB_IN_BYTES;
+    private static final long GB_IN_BYTES = 1024 * MB_IN_BYTES;
 
-    private static final long POLL_INTERVAL = AlarmManager.INTERVAL_FIFTEEN_MINUTES;
-    private static final long SUMMARY_BUCKET_DURATION = 6 * DateUtils.HOUR_IN_MILLIS;
-    private static final long SUMMARY_MAX_HISTORY = 90 * DateUtils.DAY_IN_MILLIS;
+    private LongSecureSetting mPollInterval = new LongSecureSetting(
+            NETSTATS_POLL_INTERVAL, 15 * MINUTE_IN_MILLIS);
+    private LongSecureSetting mPersistThreshold = new LongSecureSetting(
+            NETSTATS_PERSIST_THRESHOLD, 16 * KB_IN_BYTES);
 
-    // TODO: remove these high-frequency testing values
-//    private static final long POLL_INTERVAL = 5 * DateUtils.SECOND_IN_MILLIS;
-//    private static final long SUMMARY_BUCKET_DURATION = 10 * DateUtils.SECOND_IN_MILLIS;
-//    private static final long SUMMARY_MAX_HISTORY = 2 * DateUtils.MINUTE_IN_MILLIS;
+    // TODO: adjust these timings for production builds
+    private LongSecureSetting mSummaryBucketDuration = new LongSecureSetting(
+            NETSTATS_SUMMARY_BUCKET_DURATION, 1 * HOUR_IN_MILLIS);
+    private LongSecureSetting mSummaryMaxHistory = new LongSecureSetting(
+            NETSTATS_SUMMARY_MAX_HISTORY, 90 * DAY_IN_MILLIS);
+    private LongSecureSetting mDetailBucketDuration = new LongSecureSetting(
+            NETSTATS_DETAIL_BUCKET_DURATION, 2 * HOUR_IN_MILLIS);
+    private LongSecureSetting mDetailMaxHistory = new LongSecureSetting(
+            NETSTATS_DETAIL_MAX_HISTORY, 90 * DAY_IN_MILLIS);
 
-    /** Minimum delta required to persist to disk. */
-    private static final long SUMMARY_PERSIST_THRESHOLD = 64 * KB_IN_BYTES;
-
-    private static final long TIME_CACHE_MAX_AGE = DateUtils.DAY_IN_MILLIS;
+    private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS;
 
     private final Object mStatsLock = new Object();
 
     /** Set of active ifaces during this boot. */
-    private HashMap<String, InterfaceInfo> mActiveIface = Maps.newHashMap();
-    /** Set of historical stats for known ifaces. */
-    private HashMap<InterfaceInfo, NetworkStatsHistory> mIfaceStats = Maps.newHashMap();
+    private HashMap<String, InterfaceIdentity> mActiveIface = Maps.newHashMap();
 
-    private NetworkStats mLastPollStats;
-    private NetworkStats mLastPersistStats;
+    /** Set of historical stats for known ifaces. */
+    private HashMap<InterfaceIdentity, NetworkStatsHistory> mSummaryStats = Maps.newHashMap();
+    /** Set of historical stats for known UIDs. */
+    private SparseArray<NetworkStatsHistory> mDetailStats = new SparseArray<NetworkStatsHistory>();
+
+    private NetworkStats mLastSummaryPoll;
+    private NetworkStats mLastSummaryPersist;
+
+    private NetworkStats mLastDetailPoll;
 
     private final HandlerThread mHandlerThread;
     private final Handler mHandler;
 
+    private final AtomicFile mSummaryFile;
+
     // TODO: collect detailed uid stats, storing tag-granularity data until next
     // dropbox, and uid summary for a specific bucket count.
 
@@ -112,11 +157,15 @@
     public NetworkStatsService(
             Context context, INetworkManagementService networkManager, IAlarmManager alarmManager) {
         // TODO: move to using cached NtpTrustedTime
-        this(context, networkManager, alarmManager, new NtpTrustedTime());
+        this(context, networkManager, alarmManager, new NtpTrustedTime(), getSystemDir());
+    }
+
+    private static File getSystemDir() {
+        return new File(Environment.getDataDirectory(), "system");
     }
 
     public NetworkStatsService(Context context, INetworkManagementService networkManager,
-            IAlarmManager alarmManager, TrustedTime time) {
+            IAlarmManager alarmManager, TrustedTime time, File systemDir) {
         mContext = checkNotNull(context, "missing Context");
         mNetworkManager = checkNotNull(networkManager, "missing INetworkManagementService");
         mAlarmManager = checkNotNull(alarmManager, "missing IAlarmManager");
@@ -125,20 +174,20 @@
         mHandlerThread = new HandlerThread(TAG);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper());
+
+        mSummaryFile = new AtomicFile(new File(systemDir, "netstats.bin"));
     }
 
     public void systemReady() {
-        // read historical stats from disk
-        readStatsLocked();
+        synchronized (mStatsLock) {
+            // read historical stats from disk
+            readStatsLocked();
+        }
 
         // watch other system services that claim interfaces
-        // TODO: protect incoming broadcast with permissions check.
-        // TODO: consider migrating this to ConnectivityService, but it might
-        // cause a circular dependency.
-        final IntentFilter interfaceFilter = new IntentFilter();
-        interfaceFilter.addAction(TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED);
-        interfaceFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
-        mContext.registerReceiver(mInterfaceReceiver, interfaceFilter);
+        final IntentFilter ifaceFilter = new IntentFilter();
+        ifaceFilter.addAction(CONNECTIVITY_ACTION);
+        mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler);
 
         // listen for periodic polling events
         final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL);
@@ -155,9 +204,23 @@
         }
     }
 
+    public void bindConnectivityManager(IConnectivityManager connManager) {
+        mConnManager = checkNotNull(connManager, "missing IConnectivityManager");
+    }
+
+    private void shutdownLocked() {
+        mContext.unregisterReceiver(mIfaceReceiver);
+        mContext.unregisterReceiver(mPollReceiver);
+        mContext.unregisterReceiver(mShutdownReceiver);
+
+        writeStatsLocked();
+        mSummaryStats.clear();
+        mDetailStats.clear();
+    }
+
     /**
      * Clear any existing {@link #ACTION_NETWORK_STATS_POLL} alarms, and
-     * reschedule based on current {@link #POLL_INTERVAL} value.
+     * reschedule based on current {@link #mPollInterval} value.
      */
     private void registerPollAlarmLocked() throws RemoteException {
         if (mPollIntent != null) {
@@ -169,49 +232,72 @@
 
         final long currentRealtime = SystemClock.elapsedRealtime();
         mAlarmManager.setInexactRepeating(
-                AlarmManager.ELAPSED_REALTIME, currentRealtime, POLL_INTERVAL, mPollIntent);
+                AlarmManager.ELAPSED_REALTIME, currentRealtime, mPollInterval.get(), mPollIntent);
     }
 
     @Override
-    public NetworkStatsHistory[] getNetworkStatsSummary(int networkType) {
-        // TODO: return history for requested types
-        return null;
+    public NetworkStatsHistory getHistoryForNetwork(int networkTemplate) {
+        // TODO: create relaxed permission for reading stats
+        mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+        synchronized (mStatsLock) {
+            // combine all interfaces that match template
+            final String subscriberId = getActiveSubscriberId();
+            final NetworkStatsHistory combined = new NetworkStatsHistory(
+                    mSummaryBucketDuration.get());
+            for (InterfaceIdentity ident : mSummaryStats.keySet()) {
+                final NetworkStatsHistory history = mSummaryStats.get(ident);
+                if (ident.matchesTemplate(networkTemplate, subscriberId)) {
+                    combined.recordEntireHistory(history);
+                }
+            }
+            return combined;
+        }
     }
 
     @Override
-    public NetworkStatsHistory getNetworkStatsUid(int uid) {
+    public NetworkStatsHistory getHistoryForUid(int uid, int networkTemplate) {
+        // TODO: create relaxed permission for reading stats
+        mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
         // TODO: return history for requested uid
         return null;
     }
 
+    @Override
+    public NetworkStats getSummaryForAllUid(long start, long end, int networkTemplate) {
+        // TODO: create relaxed permission for reading stats
+        mContext.enforceCallingOrSelfPermission(UPDATE_DEVICE_STATS, TAG);
+
+        // TODO: apply networktemplate once granular uid stats are stored.
+
+        synchronized (mStatsLock) {
+            final int size = mDetailStats.size();
+            final NetworkStats.Builder stats = new NetworkStats.Builder(end - start, size);
+
+            final long[] total = new long[2];
+            for (int i = 0; i < size; i++) {
+                final int uid = mDetailStats.keyAt(i);
+                final NetworkStatsHistory history = mDetailStats.valueAt(i);
+                history.getTotalData(start, end, total);
+                stats.addEntry(IFACE_ALL, uid, total[0], total[1]);
+            }
+            return stats.build();
+        }
+    }
+
     /**
-     * Receiver that watches for other system components that claim network
+     * Receiver that watches for {@link IConnectivityManager} to claim network
      * interfaces. Used to associate {@link TelephonyManager#getSubscriberId()}
      * with mobile interfaces.
      */
-    private BroadcastReceiver mInterfaceReceiver = new BroadcastReceiver() {
+    private BroadcastReceiver mIfaceReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            final String action = intent.getAction();
-            if (TelephonyIntents.ACTION_ANY_DATA_CONNECTION_STATE_CHANGED.equals(action)) {
-                final LinkProperties prop = intent.getParcelableExtra(
-                        Phone.DATA_LINK_PROPERTIES_KEY);
-                final String iface = prop != null ? prop.getInterfaceName() : null;
-                if (iface != null) {
-                    final TelephonyManager teleManager = (TelephonyManager) context
-                            .getSystemService(Context.TELEPHONY_SERVICE);
-                    final InterfaceInfo info = new InterfaceInfo(
-                            iface, TYPE_MOBILE, teleManager.getSubscriberId());
-                    reportActiveInterface(info);
-                }
-            } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
-                final LinkProperties prop = intent.getParcelableExtra(
-                        WifiManager.EXTRA_LINK_PROPERTIES);
-                final String iface = prop != null ? prop.getInterfaceName() : null;
-                if (iface != null) {
-                    final InterfaceInfo info = new InterfaceInfo(iface, TYPE_WIFI, null);
-                    reportActiveInterface(info);
-                }
+            // on background handler thread, and verified CONNECTIVITY_INTERNAL
+            // permission above.
+            synchronized (mStatsLock) {
+                updateIfacesLocked();
             }
         }
     };
@@ -219,8 +305,8 @@
     private BroadcastReceiver mPollReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // already running on background handler, network/io is safe, and
-            // caller verified to have UPDATE_DEVICE_STATS permission above.
+            // on background handler thread, and verified UPDATE_DEVICE_STATS
+            // permission above.
             synchronized (mStatsLock) {
                 // TODO: acquire wakelock while performing poll
                 performPollLocked();
@@ -231,13 +317,49 @@
     private BroadcastReceiver mShutdownReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            // persist stats during clean shutdown
+            // verified SHUTDOWN permission above.
             synchronized (mStatsLock) {
-                writeStatsLocked();
+                shutdownLocked();
             }
         }
     };
 
+    /**
+     * Inspect all current {@link NetworkState} to derive mapping from {@code
+     * iface} to {@link NetworkStatsHistory}. When multiple {@link NetworkInfo}
+     * are active on a single {@code iface}, they are combined under a single
+     * {@link InterfaceIdentity}.
+     */
+    private void updateIfacesLocked() {
+        if (LOGD) Slog.v(TAG, "updateIfacesLocked()");
+
+        // take one last stats snapshot before updating iface mapping. this
+        // isn't perfect, since the kernel may already be counting traffic from
+        // the updated network.
+        // TODO: verify that we only poll summary stats, not uid details
+        performPollLocked();
+
+        final NetworkState[] states;
+        try {
+            states = mConnManager.getAllNetworkState();
+        } catch (RemoteException e) {
+            Slog.w(TAG, "problem reading network state");
+            return;
+        }
+
+        // rebuild active interfaces based on connected networks
+        mActiveIface.clear();
+
+        for (NetworkState state : states) {
+            if (state.networkInfo.isConnected()) {
+                // collect networks under their parent interfaces
+                final String iface = state.linkProperties.getInterfaceName();
+                final InterfaceIdentity ident = findOrCreateInterfaceLocked(iface);
+                ident.add(NetworkIdentity.buildNetworkIdentity(mContext, state));
+            }
+        }
+    }
+
     private void performPollLocked() {
         if (LOGD) Slog.v(TAG, "performPollLocked()");
 
@@ -250,130 +372,275 @@
         final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis()
                 : System.currentTimeMillis();
 
-        final NetworkStats current;
+        final NetworkStats summary;
+        final NetworkStats detail;
         try {
-            current = mNetworkManager.getNetworkStatsSummary();
+            summary = mNetworkManager.getNetworkStatsSummary();
+            detail = mNetworkManager.getNetworkStatsDetail();
         } catch (RemoteException e) {
             Slog.w(TAG, "problem reading network stats");
             return;
         }
 
-        // update historical usage with delta since last poll
-        final NetworkStats pollDelta = computeStatsDelta(mLastPollStats, current);
-        final long timeStart = currentTime - pollDelta.elapsedRealtime;
-        for (String iface : pollDelta.getKnownIfaces()) {
-            final InterfaceInfo info = mActiveIface.get(iface);
-            if (info == null) {
-                if (LOGD) Slog.w(TAG, "unknown interface " + iface + ", ignoring stats");
-                continue;
-            }
-
-            final int index = pollDelta.findIndex(iface, UID_ALL);
-            final long rx = pollDelta.rx[index];
-            final long tx = pollDelta.tx[index];
-
-            final NetworkStatsHistory history = findOrCreateHistoryLocked(info);
-            history.recordData(timeStart, currentTime, rx, tx);
-            history.removeBucketsBefore(currentTime - SUMMARY_MAX_HISTORY);
-        }
-
-        mLastPollStats = current;
+        performSummaryPollLocked(summary, currentTime);
+        performDetailPollLocked(detail, currentTime);
 
         // decide if enough has changed to trigger persist
-        final NetworkStats persistDelta = computeStatsDelta(mLastPersistStats, current);
-        for (String iface : persistDelta.getKnownIfaces()) {
+        final NetworkStats persistDelta = computeStatsDelta(mLastSummaryPersist, summary);
+        final long persistThreshold = mPersistThreshold.get();
+        for (String iface : persistDelta.getUniqueIfaces()) {
             final int index = persistDelta.findIndex(iface, UID_ALL);
-            if (persistDelta.rx[index] > SUMMARY_PERSIST_THRESHOLD
-                    || persistDelta.tx[index] > SUMMARY_PERSIST_THRESHOLD) {
+            if (persistDelta.rx[index] > persistThreshold
+                    || persistDelta.tx[index] > persistThreshold) {
                 writeStatsLocked();
-                mLastPersistStats = current;
+                mLastSummaryPersist = summary;
                 break;
             }
         }
     }
 
-    private NetworkStatsHistory findOrCreateHistoryLocked(InterfaceInfo info) {
-        NetworkStatsHistory stats = mIfaceStats.get(info);
+    /**
+     * Update {@link #mSummaryStats} historical usage.
+     */
+    private void performSummaryPollLocked(NetworkStats summary, long currentTime) {
+        final ArrayList<String> unknownIface = Lists.newArrayList();
+
+        final NetworkStats delta = computeStatsDelta(mLastSummaryPoll, summary);
+        final long timeStart = currentTime - delta.elapsedRealtime;
+        final long maxHistory = mSummaryMaxHistory.get();
+        for (String iface : delta.getUniqueIfaces()) {
+            final InterfaceIdentity ident = mActiveIface.get(iface);
+            if (ident == null) {
+                unknownIface.add(iface);
+                continue;
+            }
+
+            final int index = delta.findIndex(iface, UID_ALL);
+            final long rx = delta.rx[index];
+            final long tx = delta.tx[index];
+
+            final NetworkStatsHistory history = findOrCreateSummaryLocked(ident);
+            history.recordData(timeStart, currentTime, rx, tx);
+            history.removeBucketsBefore(currentTime - maxHistory);
+        }
+        mLastSummaryPoll = summary;
+
+        if (LOGD && unknownIface.size() > 0) {
+            Slog.w(TAG, "unknown interfaces " + unknownIface.toString() + ", ignoring those stats");
+        }
+    }
+
+    /**
+     * Update {@link #mDetailStats} historical usage.
+     */
+    private void performDetailPollLocked(NetworkStats detail, long currentTime) {
+        final NetworkStats delta = computeStatsDelta(mLastDetailPoll, detail);
+        final long timeStart = currentTime - delta.elapsedRealtime;
+        final long maxHistory = mDetailMaxHistory.get();
+        for (int uid : delta.getUniqueUids()) {
+            final int index = delta.findIndex(IFACE_ALL, uid);
+            final long rx = delta.rx[index];
+            final long tx = delta.tx[index];
+
+            final NetworkStatsHistory history = findOrCreateDetailLocked(uid);
+            history.recordData(timeStart, currentTime, rx, tx);
+            history.removeBucketsBefore(currentTime - maxHistory);
+        }
+        mLastDetailPoll = detail;
+    }
+
+    private NetworkStatsHistory findOrCreateSummaryLocked(InterfaceIdentity ident) {
+        NetworkStatsHistory stats = mSummaryStats.get(ident);
         if (stats == null) {
-            stats = new NetworkStatsHistory(
-                    info.networkType, info.identity, UID_ALL, SUMMARY_BUCKET_DURATION);
-            mIfaceStats.put(info, stats);
+            stats = new NetworkStatsHistory(mSummaryBucketDuration.get());
+            mSummaryStats.put(ident, stats);
         }
         return stats;
     }
 
+    private NetworkStatsHistory findOrCreateDetailLocked(int uid) {
+        NetworkStatsHistory stats = mDetailStats.get(uid);
+        if (stats == null) {
+            stats = new NetworkStatsHistory(mDetailBucketDuration.get());
+            mDetailStats.put(uid, stats);
+        }
+        return stats;
+    }
+
+    private InterfaceIdentity findOrCreateInterfaceLocked(String iface) {
+        InterfaceIdentity ident = mActiveIface.get(iface);
+        if (ident == null) {
+            ident = new InterfaceIdentity();
+            mActiveIface.put(iface, ident);
+        }
+        return ident;
+    }
+
     private void readStatsLocked() {
         if (LOGD) Slog.v(TAG, "readStatsLocked()");
-        // TODO: read historical stats from disk using AtomicFile
+
+        // clear any existing stats and read from disk
+        mSummaryStats.clear();
+
+        FileInputStream fis = null;
+        try {
+            fis = mSummaryFile.openRead();
+            final DataInputStream in = new DataInputStream(fis);
+
+            // verify file magic header intact
+            final int magic = in.readInt();
+            if (magic != FILE_MAGIC) {
+                throw new ProtocolException("unexpected magic: " + magic);
+            }
+
+            final int version = in.readInt();
+            switch (version) {
+                case VERSION_CURRENT: {
+                    // file format is pairs of interfaces and stats:
+                    // summary := size *(InterfaceIdentity NetworkStatsHistory)
+
+                    final int size = in.readInt();
+                    for (int i = 0; i < size; i++) {
+                        final InterfaceIdentity ident = new InterfaceIdentity(in);
+                        final NetworkStatsHistory history = new NetworkStatsHistory(in);
+                        mSummaryStats.put(ident, history);
+                    }
+                    break;
+                }
+                default: {
+                    throw new ProtocolException("unexpected version: " + version);
+                }
+            }
+        } catch (FileNotFoundException e) {
+            // missing stats is okay, probably first boot
+        } catch (IOException e) {
+            Slog.e(TAG, "problem reading network stats", e);
+        } finally {
+            IoUtils.closeQuietly(fis);
+        }
     }
 
     private void writeStatsLocked() {
         if (LOGD) Slog.v(TAG, "writeStatsLocked()");
-        // TODO: persist historical stats to disk using AtomicFile
+
+        // TODO: consider duplicating stats and releasing lock while writing
+
+        FileOutputStream fos = null;
+        try {
+            fos = mSummaryFile.startWrite();
+            final DataOutputStream out = new DataOutputStream(fos);
+
+            out.writeInt(FILE_MAGIC);
+            out.writeInt(VERSION_CURRENT);
+
+            out.writeInt(mSummaryStats.size());
+            for (InterfaceIdentity ident : mSummaryStats.keySet()) {
+                final NetworkStatsHistory history = mSummaryStats.get(ident);
+                ident.writeToStream(out);
+                history.writeToStream(out);
+            }
+
+            mSummaryFile.finishWrite(fos);
+        } catch (IOException e) {
+            if (fos != null) {
+                mSummaryFile.failWrite(fos);
+            }
+        }
     }
 
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         mContext.enforceCallingOrSelfPermission(DUMP, TAG);
 
-        pw.println("Active interfaces:");
-        for (InterfaceInfo info : mActiveIface.values()) {
-            info.dump("  ", pw);
+        final HashSet<String> argSet = new HashSet<String>();
+        for (String arg : args) {
+            argSet.add(arg);
         }
 
-        pw.println("Known historical stats:");
-        for (NetworkStatsHistory stats : mIfaceStats.values()) {
-            stats.dump("  ", pw);
+        synchronized (mStatsLock) {
+            // TODO: remove this testing code, since it corrupts stats
+            if (argSet.contains("generate")) {
+                generateRandomLocked();
+                pw.println("Generated stub stats");
+                return;
+            }
+
+            if (argSet.contains("poll")) {
+                performPollLocked();
+                pw.println("Forced poll");
+                return;
+            }
+
+            pw.println("Active interfaces:");
+            for (String iface : mActiveIface.keySet()) {
+                final InterfaceIdentity ident = mActiveIface.get(iface);
+                pw.print("  iface="); pw.print(iface);
+                pw.print(" ident="); pw.println(ident.toString());
+            }
+
+            pw.println("Known historical stats:");
+            for (InterfaceIdentity ident : mSummaryStats.keySet()) {
+                final NetworkStatsHistory stats = mSummaryStats.get(ident);
+                pw.print("  ident="); pw.println(ident.toString());
+                stats.dump("    ", pw);
+            }
+
+            if (argSet.contains("detail")) {
+                pw.println("Known detail stats:");
+                for (int i = 0; i < mDetailStats.size(); i++) {
+                    final int uid = mDetailStats.keyAt(i);
+                    final NetworkStatsHistory stats = mDetailStats.valueAt(i);
+                    pw.print("  UID="); pw.println(uid);
+                    stats.dump("    ", pw);
+                }
+            }
         }
     }
 
     /**
-     * Details for a well-known network interface, including its name, network
-     * type, and billing relationship identity (such as IMSI).
+     * @deprecated only for temporary testing
      */
-    private static class InterfaceInfo {
-        public final String iface;
-        public final int networkType;
-        public final String identity;
+    @Deprecated
+    private void generateRandomLocked() {
+        long end = System.currentTimeMillis();
+        long start = end - mSummaryMaxHistory.get();
+        long rx = 3 * GB_IN_BYTES;
+        long tx = 2 * GB_IN_BYTES;
 
-        public InterfaceInfo(String iface, int networkType, String identity) {
-            this.iface = iface;
-            this.networkType = networkType;
-            this.identity = identity;
+        mSummaryStats.clear();
+        for (InterfaceIdentity ident : mActiveIface.values()) {
+            final NetworkStatsHistory stats = findOrCreateSummaryLocked(ident);
+            stats.generateRandom(start, end, rx, tx);
         }
 
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((identity == null) ? 0 : identity.hashCode());
-            result = prime * result + ((iface == null) ? 0 : iface.hashCode());
-            result = prime * result + networkType;
-            return result;
-        }
+        end = System.currentTimeMillis();
+        start = end - mDetailMaxHistory.get();
+        rx = 500 * MB_IN_BYTES;
+        tx = 100 * MB_IN_BYTES;
 
-        @Override
-        public boolean equals(Object obj) {
-            if (obj instanceof InterfaceInfo) {
-                final InterfaceInfo info = (InterfaceInfo) obj;
-                return equal(iface, info.iface) && networkType == info.networkType
-                        && equal(identity, info.identity);
-            }
-            return false;
-        }
-
-        public void dump(String prefix, PrintWriter pw) {
-            pw.print(prefix);
-            pw.print("InterfaceInfo: iface="); pw.print(iface);
-            pw.print(" networkType="); pw.print(networkType);
-            pw.print(" identity="); pw.println(identity);
+        mDetailStats.clear();
+        for (ApplicationInfo info : mContext.getPackageManager().getInstalledApplications(0)) {
+            final int uid = info.uid;
+            final NetworkStatsHistory stats = findOrCreateDetailLocked(uid);
+            stats.generateRandom(start, end, rx, tx);
         }
     }
 
-    private void reportActiveInterface(InterfaceInfo info) {
-        synchronized (mStatsLock) {
-            // TODO: when interface redefined, port over historical stats
-            mActiveIface.put(info.iface, info);
+    private class LongSecureSetting {
+        private String mKey;
+        private long mDefaultValue;
+
+        public LongSecureSetting(String key, long defaultValue) {
+            mKey = key;
+            mDefaultValue = defaultValue;
+        }
+
+        public long get() {
+            if (mContext != null) {
+                return Settings.Secure.getLong(mContext.getContentResolver(), mKey, mDefaultValue);
+            } else {
+                return mDefaultValue;
+            }
         }
     }
 
@@ -383,21 +650,16 @@
      */
     private static NetworkStats computeStatsDelta(NetworkStats before, NetworkStats current) {
         if (before != null) {
-            return current.subtract(before, false);
+            return current.subtractClamped(before);
         } else {
             return current;
         }
     }
 
-    private static boolean equal(Object a, Object b) {
-        return a == b || (a != null && a.equals(b));
-    }
-
-    private static <T> T checkNotNull(T value, String message) {
-        if (value == null) {
-            throw new NullPointerException(message);
-        }
-        return value;
+    private String getActiveSubscriberId() {
+        final TelephonyManager telephony = (TelephonyManager) mContext.getSystemService(
+                Context.TELEPHONY_SERVICE);
+        return telephony.getSubscriberId();
     }
 
 }
diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java
index 3791cc4..95008e5 100644
--- a/services/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/java/com/android/server/usb/UsbDeviceManager.java
@@ -84,7 +84,6 @@
 
     // lists of enabled and disabled USB functions
     private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
-    private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
 
     private boolean mSystemReady;
 
@@ -120,15 +119,11 @@
             if (!mEnabledFunctions.contains(function)) {
                 mEnabledFunctions.add(function);
             }
-            mDisabledFunctions.remove(function);
 
             if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) {
                 readCurrentAccessoryLocked();
             }
         } else {
-            if (!mDisabledFunctions.contains(function)) {
-                mDisabledFunctions.add(function);
-            }
             mEnabledFunctions.remove(function);
         }
     }
@@ -260,8 +255,6 @@
                         // so don't treat it as a default function.
                         mDefaultFunctions.add(functionName);
                     }
-                } else {
-                    mDisabledFunctions.add(functionName);
                 }
             }
         } catch (FileNotFoundException e) {
@@ -278,9 +271,9 @@
             // FIXME - if we booted in accessory mode, then we have no way to figure out
             // which functions are enabled by default.
             // For now, assume that MTP or mass storage are the only possibilities
-            if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
+            if (!mEnabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) {
                 mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP);
-            } else if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
+            } else if (!mEnabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) {
                 mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE);
             }
         }
@@ -335,15 +328,6 @@
      * This handler is for deferred handling of events related to device mode and accessories.
      */
     private final Handler mHandler = new Handler() {
-        private void addEnabledFunctionsLocked(Intent intent) {
-            // include state of all USB functions in our extras
-            for (int i = 0; i < mEnabledFunctions.size(); i++) {
-                intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
-            }
-            for (int i = 0; i < mDisabledFunctions.size(); i++) {
-                intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
-            }
-        }
 
         @Override
         public void handleMessage(Message msg) {
@@ -390,7 +374,9 @@
                             intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
                             intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
                             intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
-                            addEnabledFunctionsLocked(intent);
+                            for (int i = 0; i < mEnabledFunctions.size(); i++) {
+                                intent.putExtra(mEnabledFunctions.get(i), true);
+                            }
                             mContext.sendStickyBroadcast(intent);
                         }
                         break;
@@ -411,11 +397,6 @@
                 pw.print(mEnabledFunctions.get(i) + " ");
             }
             pw.println("");
-            pw.print("    Disabled Functions: ");
-            for (int i = 0; i < mDisabledFunctions.size(); i++) {
-                pw.print(mDisabledFunctions.get(i) + " ");
-            }
-            pw.println("");
             pw.print("    Default Functions: ");
             for (int i = 0; i < mDefaultFunctions.size(); i++) {
                 pw.print(mDefaultFunctions.get(i) + " ");
diff --git a/services/java/com/android/server/wm/InputManager.java b/services/java/com/android/server/wm/InputManager.java
index 3095c37..ee69311 100644
--- a/services/java/com/android/server/wm/InputManager.java
+++ b/services/java/com/android/server/wm/InputManager.java
@@ -617,8 +617,13 @@
         }
 
         @SuppressWarnings("unused")
-        public int getTapTimeout() {
-            return ViewConfiguration.getTapTimeout();
+        public int getHoverTapTimeout() {
+            return ViewConfiguration.getHoverTapTimeout();
+        }
+
+        @SuppressWarnings("unused")
+        public int getHoverTapSlop() {
+            return ViewConfiguration.getHoverTapSlop();
         }
 
         @SuppressWarnings("unused")
@@ -632,11 +637,6 @@
         }
 
         @SuppressWarnings("unused")
-        public int getTouchSlop() {
-            return ViewConfiguration.get(mContext).getScaledTouchSlop();
-        }
-
-        @SuppressWarnings("unused")
         public int getMaxEventsPerSecond() {
             int result = 0;
             try {
diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java
index d95d4c5..819c16e 100644
--- a/services/java/com/android/server/wm/WindowManagerService.java
+++ b/services/java/com/android/server/wm/WindowManagerService.java
@@ -153,6 +153,7 @@
     static final boolean DEBUG_WINDOW_MOVEMENT = false;
     static final boolean DEBUG_TOKEN_MOVEMENT = false;
     static final boolean DEBUG_ORIENTATION = false;
+    static final boolean DEBUG_APP_ORIENTATION = false;
     static final boolean DEBUG_CONFIGURATION = false;
     static final boolean DEBUG_APP_TRANSITIONS = false;
     static final boolean DEBUG_STARTING_WINDOW = false;
@@ -427,6 +428,7 @@
     boolean mWindowsFreezingScreen = false;
     long mFreezeGcPending = 0;
     int mAppsFreezingScreen = 0;
+    int mLastWindowForcedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
 
     int mLayoutSeq = 0;
     
@@ -3187,6 +3189,15 @@
     }
 
     public int getOrientationFromWindowsLocked() {
+        if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
+            // If the display is frozen, some activities may be in the middle
+            // of restarting, and thus have removed their old window.  If the
+            // window has the flag to hide the lock screen, then the lock screen
+            // can re-appear and inflict its own orientation on us.  Keep the
+            // orientation stable until this all settles down.
+            return mLastWindowForcedOrientation;
+        }
+
         int pos = mWindows.size() - 1;
         while (pos >= 0) {
             WindowState wtoken = mWindows.get(pos);
@@ -3194,7 +3205,7 @@
             if (wtoken.mAppToken != null) {
                 // We hit an application window. so the orientation will be determined by the
                 // app window. No point in continuing further.
-                return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+                return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
             }
             if (!wtoken.isVisibleLw() || !wtoken.mPolicyVisibilityAfterAnim) {
                 continue;
@@ -3204,10 +3215,10 @@
                     (req == ActivityInfo.SCREEN_ORIENTATION_BEHIND)){
                 continue;
             } else {
-                return req;
+                return (mLastWindowForcedOrientation=req);
             }
         }
-        return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        return (mLastWindowForcedOrientation=ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
     }
 
     public int getOrientationFromAppTokensLocked() {
@@ -3220,16 +3231,23 @@
         while (pos >= 0) {
             AppWindowToken wtoken = mAppTokens.get(pos);
             pos--;
+
+            if (DEBUG_APP_ORIENTATION) Slog.v(TAG, "Checking app orientation: " + wtoken);
+
             // if we're about to tear down this window and not seek for
             // the behind activity, don't use it for orientation
             if (!findingBehind
                     && (!wtoken.hidden && wtoken.hiddenRequested)) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+                        + " -- going to hide");
                 continue;
             }
 
             if (!haveGroup) {
                 // We ignore any hidden applications on the top.
                 if (wtoken.hiddenRequested || wtoken.willBeHidden) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Skipping " + wtoken
+                            + " -- hidden on top");
                     continue;
                 }
                 haveGroup = true;
@@ -3243,6 +3261,8 @@
                 // user's orientation.
                 if (lastOrientation != ActivityInfo.SCREEN_ORIENTATION_BEHIND
                         && lastFullscreen) {
+                    if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+                            + " -- end of group, return " + lastOrientation);
                     return lastOrientation;
                 }
             }
@@ -3253,16 +3273,21 @@
             lastFullscreen = wtoken.appFullscreen;
             if (lastFullscreen
                     && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+                        + " -- full screen, return " + or);
                 return or;
             }
             // If this application has requested an explicit orientation,
             // then use it.
             if (or != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
                     && or != ActivityInfo.SCREEN_ORIENTATION_BEHIND) {
+                if (DEBUG_ORIENTATION) Slog.v(TAG, "Done at " + wtoken
+                        + " -- explicitly set, return " + or);
                 return or;
             }
             findingBehind |= (or == ActivityInfo.SCREEN_ORIENTATION_BEHIND);
         }
+        if (DEBUG_ORIENTATION) Slog.v(TAG, "No app is requesting an orientation");
         return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
     }
 
@@ -3335,15 +3360,6 @@
      * android.os.IBinder)
      */
     boolean updateOrientationFromAppTokensLocked(boolean inTransaction) {
-        if (mDisplayFrozen || mOpeningApps.size() > 0 || mClosingApps.size() > 0) {
-            // If the display is frozen, some activities may be in the middle
-            // of restarting, and thus have removed their old window.  If the
-            // window has the flag to hide the lock screen, then the lock screen
-            // can re-appear and inflict its own orientation on us.  Keep the
-            // orientation stable until this all settles down.
-            return false;
-        }
-
         boolean changed = false;
         long ident = Binder.clearCallingIdentity();
         try {
@@ -8939,9 +8955,10 @@
                     pw.print(" mAppsFreezingScreen="); pw.print(mAppsFreezingScreen);
                     pw.print(" mWaitingForConfig="); pw.println(mWaitingForConfig);
             pw.print("  mRotation="); pw.print(mRotation);
-                    pw.print(" mForcedAppOrientation="); pw.print(mForcedAppOrientation);
                     pw.print(" mRequestedRotation="); pw.print(mRequestedRotation);
                     pw.print(" mAltOrientation="); pw.println(mAltOrientation);
+            pw.print("  mLastWindowForcedOrientation"); pw.print(mLastWindowForcedOrientation);
+                    pw.print(" mForcedAppOrientation="); pw.println(mForcedAppOrientation);
             pw.print("  mDeferredRotation="); pw.print(mDeferredRotation);
                     pw.print(", mDeferredRotationAnimFlags="); pw.println(mDeferredRotationAnimFlags);
             pw.print("  mAnimationPending="); pw.print(mAnimationPending);
diff --git a/services/jni/Android.mk b/services/jni/Android.mk
index a1c3283..f33920d 100644
--- a/services/jni/Android.mk
+++ b/services/jni/Android.mk
@@ -16,6 +16,7 @@
     com_android_server_UsbHostManager.cpp \
     com_android_server_VibratorService.cpp \
     com_android_server_location_GpsLocationProvider.cpp \
+    com_android_server_connectivity_Vpn.cpp \
     onload.cpp
 
 LOCAL_C_INCLUDES += \
diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp
index 881882f..7c5084f 100644
--- a/services/jni/com_android_server_InputManager.cpp
+++ b/services/jni/com_android_server_InputManager.cpp
@@ -56,7 +56,7 @@
 // The exponent used to calculate the pointer speed scaling factor.
 // The scaling factor is calculated as 2 ^ (speed * exponent),
 // where the speed ranges from -7 to + 7 and is supplied by the user.
-static const float POINTER_SPEED_EXPONENT = 1.0f / 3;
+static const float POINTER_SPEED_EXPONENT = 1.0f / 4;
 
 static struct {
     jmethodID notifyConfigurationChanged;
@@ -76,10 +76,10 @@
     jmethodID getKeyRepeatTimeout;
     jmethodID getKeyRepeatDelay;
     jmethodID getMaxEventsPerSecond;
-    jmethodID getTapTimeout;
+    jmethodID getHoverTapTimeout;
+    jmethodID getHoverTapSlop;
     jmethodID getDoubleTapTimeout;
     jmethodID getLongPressTimeout;
-    jmethodID getTouchSlop;
     jmethodID getPointerLayer;
     jmethodID getPointerIcon;
 } gCallbacksClassInfo;
@@ -410,32 +410,32 @@
         env->DeleteLocalRef(excludedDeviceNames);
     }
 
-    jint tapTimeout = env->CallIntMethod(mCallbacksObj,
-            gCallbacksClassInfo.getTapTimeout);
-    if (!checkAndClearExceptionFromCallback(env, "getTapTimeout")) {
+    jint hoverTapTimeout = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getHoverTapTimeout);
+    if (!checkAndClearExceptionFromCallback(env, "getHoverTapTimeout")) {
         jint doubleTapTimeout = env->CallIntMethod(mCallbacksObj,
                 gCallbacksClassInfo.getDoubleTapTimeout);
         if (!checkAndClearExceptionFromCallback(env, "getDoubleTapTimeout")) {
             jint longPressTimeout = env->CallIntMethod(mCallbacksObj,
                     gCallbacksClassInfo.getLongPressTimeout);
             if (!checkAndClearExceptionFromCallback(env, "getLongPressTimeout")) {
-                outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(tapTimeout);
+                outConfig->pointerGestureTapInterval = milliseconds_to_nanoseconds(hoverTapTimeout);
 
                 // We must ensure that the tap-drag interval is significantly shorter than
                 // the long-press timeout because the tap is held down for the entire duration
                 // of the double-tap timeout.
                 jint tapDragInterval = max(min(longPressTimeout - 100,
-                        doubleTapTimeout), tapTimeout);
+                        doubleTapTimeout), hoverTapTimeout);
                 outConfig->pointerGestureTapDragInterval =
                         milliseconds_to_nanoseconds(tapDragInterval);
             }
         }
     }
 
-    jint touchSlop = env->CallIntMethod(mCallbacksObj,
-            gCallbacksClassInfo.getTouchSlop);
-    if (!checkAndClearExceptionFromCallback(env, "getTouchSlop")) {
-        outConfig->pointerGestureTapSlop = touchSlop;
+    jint hoverTapSlop = env->CallIntMethod(mCallbacksObj,
+            gCallbacksClassInfo.getHoverTapSlop);
+    if (!checkAndClearExceptionFromCallback(env, "getHoverTapSlop")) {
+        outConfig->pointerGestureTapSlop = hoverTapSlop;
     }
 
     { // acquire lock
@@ -1394,8 +1394,11 @@
     GET_METHOD_ID(gCallbacksClassInfo.getKeyRepeatDelay, clazz,
             "getKeyRepeatDelay", "()I");
 
-    GET_METHOD_ID(gCallbacksClassInfo.getTapTimeout, clazz,
-            "getTapTimeout", "()I");
+    GET_METHOD_ID(gCallbacksClassInfo.getHoverTapTimeout, clazz,
+            "getHoverTapTimeout", "()I");
+
+    GET_METHOD_ID(gCallbacksClassInfo.getHoverTapSlop, clazz,
+            "getHoverTapSlop", "()I");
 
     GET_METHOD_ID(gCallbacksClassInfo.getDoubleTapTimeout, clazz,
             "getDoubleTapTimeout", "()I");
@@ -1403,9 +1406,6 @@
     GET_METHOD_ID(gCallbacksClassInfo.getLongPressTimeout, clazz,
             "getLongPressTimeout", "()I");
 
-    GET_METHOD_ID(gCallbacksClassInfo.getTouchSlop, clazz,
-            "getTouchSlop", "()I");
-
     GET_METHOD_ID(gCallbacksClassInfo.getMaxEventsPerSecond, clazz,
             "getMaxEventsPerSecond", "()I");
 
diff --git a/services/jni/com_android_server_connectivity_Vpn.cpp b/services/jni/com_android_server_connectivity_Vpn.cpp
new file mode 100644
index 0000000..374fd3b
--- /dev/null
+++ b/services/jni/com_android_server_connectivity_Vpn.cpp
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_NDEBUG 0
+
+#define LOG_TAG "VpnJni"
+#include <cutils/log.h>
+#include <cutils/properties.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+
+#include <linux/if.h>
+#include <linux/if_tun.h>
+#include <linux/route.h>
+#include <linux/ipv6_route.h>
+
+#include "jni.h"
+#include "JNIHelp.h"
+#include "android_util_Binder.h"
+
+namespace android
+{
+
+static inline void init_sockaddr(sockaddr *sa) {
+    ((sockaddr_in *)sa)->sin_family = AF_INET;
+    ((sockaddr_in *)sa)->sin_port = 0;
+}
+
+static inline in_addr_t *as_in_addr(sockaddr *sa) {
+    return &((sockaddr_in *)sa)->sin_addr.s_addr;
+}
+
+static inline in_addr_t *as_in_addr(sockaddr_storage *ss) {
+    return &((sockaddr_in *)ss)->sin_addr.s_addr;
+}
+
+static inline in6_addr *as_in6_addr(sockaddr_storage *ss) {
+    return &((sockaddr_in6 *)&ss)->sin6_addr;
+}
+
+//------------------------------------------------------------------------------
+
+#define SYSTEM_ERROR -1
+#define BAD_ARGUMENT -2
+
+static int create_interface(char *name, int *index)
+{
+    int tun = open("/dev/tun", O_RDWR);
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+
+    ifreq ifr4;
+    memset(&ifr4, 0, sizeof(ifr4));
+
+    // Allocate interface.
+    ifr4.ifr_flags = IFF_TUN;
+    if (ioctl(tun, TUNSETIFF, &ifr4)) {
+        LOGE("Cannot allocate TUN: %s", strerror(errno));
+        goto error;
+    }
+
+    // Activate interface.
+    ifr4.ifr_flags = IFF_UP;
+    if (ioctl(inet4, SIOCSIFFLAGS, &ifr4)) {
+        LOGE("Cannot activate %s: %s", ifr4.ifr_name, strerror(errno));
+        goto error;
+    }
+
+    // Get interface index.
+    if (ioctl(inet4, SIOGIFINDEX, &ifr4)) {
+        LOGE("Cannot get index of %s: %s", ifr4.ifr_name, strerror(errno));
+        goto error;
+    }
+
+    strcpy(name, ifr4.ifr_name);
+    *index = ifr4.ifr_ifindex;
+    close(inet4);
+    return tun;
+
+error:
+    close(tun);
+    close(inet4);
+    return SYSTEM_ERROR;
+}
+
+static int set_addresses(const char *name, int index, const char *addresses)
+{
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+    int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+
+    ifreq ifr4;
+    memset(&ifr4, 0, sizeof(ifr4));
+    strcpy(ifr4.ifr_name, name);
+    init_sockaddr(&ifr4.ifr_addr);
+
+    in6_ifreq ifr6;
+    memset(&ifr6, 0, sizeof(ifr6));
+    ifr6.ifr6_ifindex = index;
+
+    char address[65];
+    int prefix;
+
+    int chars;
+    int count = 0;
+
+    while (sscanf(addresses, " %64[^/]/%d %n", address, &prefix, &chars) == 2) {
+        addresses += chars;
+
+        if (strchr(address, ':')) {
+            // Add an IPv6 address.
+            if (inet_pton(AF_INET6, address, &ifr6.ifr6_addr) != 1 ||
+                    prefix < 0 || prefix > 128) {
+                count = BAD_ARGUMENT;
+                break;
+            }
+
+            ifr6.ifr6_prefixlen = prefix;
+            if (ioctl(inet6, SIOCSIFADDR, &ifr6)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+        } else {
+            // Add an IPv4 address.
+            if (inet_pton(AF_INET, address, as_in_addr(&ifr4.ifr_addr)) != 1 ||
+                    prefix < 0 || prefix > 32) {
+                count = BAD_ARGUMENT;
+                break;
+            }
+
+            if (count) {
+                sprintf(ifr4.ifr_name, "%s:%d", name, count);
+            }
+            if (ioctl(inet4, SIOCSIFADDR, &ifr4)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+
+            in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
+            *as_in_addr(&ifr4.ifr_addr) = htonl(mask);
+            if (ioctl(inet4, SIOCSIFNETMASK, &ifr4)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+        }
+        LOGV("Address added on %s: %s/%d", name, address, prefix);
+        ++count;
+    }
+
+    if (count == BAD_ARGUMENT) {
+        LOGE("Invalid address: %s/%d", address, prefix);
+    } else if (count == SYSTEM_ERROR) {
+        LOGE("Cannot add address: %s/%d: %s", address, prefix, strerror(errno));
+    } else if (*addresses) {
+        LOGE("Invalid address: %s", addresses);
+        count = BAD_ARGUMENT;
+    }
+
+    close(inet4);
+    close(inet6);
+    return count;
+}
+
+static int set_routes(const char *name, int index, const char *routes)
+{
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+    int inet6 = socket(AF_INET6, SOCK_DGRAM, 0);
+
+    rtentry rt4;
+    memset(&rt4, 0, sizeof(rt4));
+    rt4.rt_dev = (char *)name;
+    rt4.rt_flags = RTF_UP;
+    init_sockaddr(&rt4.rt_dst);
+    init_sockaddr(&rt4.rt_genmask);
+    init_sockaddr(&rt4.rt_gateway);
+
+    in6_rtmsg rt6;
+    memset(&rt6, 0, sizeof(rt6));
+    rt6.rtmsg_ifindex = index;
+    rt6.rtmsg_flags = RTF_UP;
+
+    char address[65];
+    int prefix;
+    char gateway[65];
+
+    int chars;
+    int count = 0;
+
+    while (sscanf(routes, " %64[^/]/%d>%64[^ ] %n",
+            address, &prefix, gateway, &chars) == 3) {
+        routes += chars;
+
+        if (strchr(address, ':')) {
+            // Add an IPv6 route.
+            if (inet_pton(AF_INET6, gateway, &rt6.rtmsg_gateway) != 1 ||
+                    inet_pton(AF_INET6, address, &rt6.rtmsg_dst) != 1 ||
+                    prefix < 0 || prefix > 128) {
+                count = BAD_ARGUMENT;
+                break;
+            }
+
+            rt6.rtmsg_dst_len = prefix;
+            if (memcmp(&rt6.rtmsg_gateway, &in6addr_any, sizeof(in6addr_any))) {
+                rt6.rtmsg_flags |= RTF_GATEWAY;
+            }
+            if (ioctl(inet6, SIOCADDRT, &rt6)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+        } else {
+            // Add an IPv4 route.
+            if (inet_pton(AF_INET, gateway, as_in_addr(&rt4.rt_gateway)) != 1 ||
+                    inet_pton(AF_INET, address, as_in_addr(&rt4.rt_dst)) != 1 ||
+                    prefix < 0 || prefix > 32) {
+                count = BAD_ARGUMENT;
+                break;
+            }
+
+            in_addr_t mask = prefix ? (~0 << (32 - prefix)) : 0;
+            *as_in_addr(&rt4.rt_genmask) = htonl(mask);
+            if (*as_in_addr(&rt4.rt_gateway)) {
+                rt4.rt_flags |= RTF_GATEWAY;
+            }
+            if (ioctl(inet4, SIOCADDRT, &rt4)) {
+                count = (errno == EINVAL) ? BAD_ARGUMENT : SYSTEM_ERROR;
+                break;
+            }
+        }
+        LOGV("Route added on %s: %s/%d -> %s", name, address, prefix, gateway);
+        ++count;
+    }
+
+    if (count == BAD_ARGUMENT) {
+        LOGE("Invalid route: %s/%d -> %s", address, prefix, gateway);
+    } else if (count == SYSTEM_ERROR) {
+        LOGE("Cannot add route: %s/%d -> %s: %s",
+                address, prefix, gateway, strerror(errno));
+    } else if (*routes) {
+        LOGE("Invalid route: %s", routes);
+        count = BAD_ARGUMENT;
+    }
+
+    close(inet4);
+    close(inet6);
+    return count;
+}
+
+static int get_interface_name(char *name, int tun)
+{
+    ifreq ifr4;
+    if (ioctl(tun, TUNGETIFF, &ifr4)) {
+        LOGE("Cannot get interface name: %s", strerror(errno));
+        return SYSTEM_ERROR;
+    }
+    strcpy(name, ifr4.ifr_name);
+    return 0;
+}
+
+static int reset_interface(const char *name)
+{
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+
+    ifreq ifr4;
+    ifr4.ifr_flags = 0;
+    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+
+    if (ioctl(inet4, SIOCSIFFLAGS, &ifr4) && errno != ENODEV) {
+        LOGE("Cannot reset %s: %s", name, strerror(errno));
+        close(inet4);
+        return SYSTEM_ERROR;
+    }
+    close(inet4);
+    return 0;
+}
+
+static int check_interface(const char *name)
+{
+    int inet4 = socket(AF_INET, SOCK_DGRAM, 0);
+
+    ifreq ifr4;
+    strncpy(ifr4.ifr_name, name, IFNAMSIZ);
+
+    if (ioctl(inet4, SIOCGIFFLAGS, &ifr4) && errno != ENODEV) {
+        LOGE("Cannot check %s: %s", name, strerror(errno));
+        ifr4.ifr_flags = 0;
+    }
+    close(inet4);
+    return ifr4.ifr_flags;
+}
+
+static int bind_to_interface(int fd, const char *name)
+{
+    if (setsockopt(fd, SOL_SOCKET, SO_BINDTODEVICE, name, strlen(name))) {
+        LOGE("Cannot bind socket to %s: %s", name, strerror(errno));
+        return SYSTEM_ERROR;
+    }
+    return 0;
+}
+
+//------------------------------------------------------------------------------
+
+static void throwException(JNIEnv *env, int error, const char *message)
+{
+    if (error == SYSTEM_ERROR) {
+        jniThrowException(env, "java/lang/IllegalStateException", message);
+    } else {
+        jniThrowException(env, "java/lang/IllegalArgumentException", message);
+    }
+}
+
+static jobject configure(JNIEnv *env, jobject thiz,
+        jstring jAddresses, jstring jRoutes)
+{
+    char name[IFNAMSIZ];
+    int index;
+    int tun = create_interface(name, &index);
+    if (tun < 0) {
+        throwException(env, tun, "Cannot create interface");
+        return NULL;
+    }
+    LOGD("%s is created", name);
+
+    const char *addresses;
+    const char *routes;
+    int count;
+
+    // Addresses are required.
+    addresses = jAddresses ? env->GetStringUTFChars(jAddresses, NULL) : NULL;
+    if (!addresses) {
+        jniThrowNullPointerException(env, "address");
+        goto error;
+    }
+    count = set_addresses(name, index, addresses);
+    env->ReleaseStringUTFChars(jAddresses, addresses);
+    if (count <= 0) {
+        throwException(env, count, "Cannot set address");
+        goto error;
+    }
+    LOGD("Configured %d address(es) on %s", count, name);
+
+    // Routes are optional.
+    routes = jRoutes ? env->GetStringUTFChars(jRoutes, NULL) : NULL;
+    if (routes) {
+        count = set_routes(name, index, routes);
+        env->ReleaseStringUTFChars(jRoutes, routes);
+        if (count < 0) {
+            throwException(env, count, "Cannot set route");
+            goto error;
+        }
+        LOGD("Configured %d route(s) on %s", count, name);
+    }
+
+    return newParcelFileDescriptor(env, jniCreateFileDescriptor(env, tun));
+
+error:
+    close(tun);
+    LOGD("%s is destroyed", name);
+    return NULL;
+}
+
+static jstring getName(JNIEnv *env, jobject thiz, jint fd)
+{
+    char name[IFNAMSIZ];
+    if (get_interface_name(name, fd) < 0) {
+        throwException(env, SYSTEM_ERROR, "Cannot get interface name");
+        return NULL;
+    }
+    return env->NewStringUTF(name);
+}
+
+static void reset(JNIEnv *env, jobject thiz, jstring jName)
+{
+    const char *name = jName ?
+            env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        return;
+    }
+    if (reset_interface(name) < 0) {
+        throwException(env, SYSTEM_ERROR, "Cannot reset interface");
+    } else {
+        LOGD("%s is deactivated", name);
+    }
+    env->ReleaseStringUTFChars(jName, name);
+}
+
+static jint check(JNIEnv *env, jobject thiz, jstring jName)
+{
+    const char *name = jName ?
+            env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        return 0;
+    }
+    int flags = check_interface(name);
+    env->ReleaseStringUTFChars(jName, name);
+    return flags;
+}
+
+static void protect(JNIEnv *env, jobject thiz, jint fd, jstring jName)
+{
+    const char *name = jName ?
+            env->GetStringUTFChars(jName, NULL) : NULL;
+    if (!name) {
+        jniThrowNullPointerException(env, "name");
+        return;
+    }
+    if (bind_to_interface(fd, name) < 0) {
+        throwException(env, SYSTEM_ERROR, "Cannot protect socket");
+    }
+    env->ReleaseStringUTFChars(jName, name);
+}
+
+//------------------------------------------------------------------------------
+
+static JNINativeMethod gMethods[] = {
+    {"nativeConfigure", "(Ljava/lang/String;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;", (void *)configure},
+    {"nativeGetName", "(I)Ljava/lang/String;", (void *)getName},
+    {"nativeReset", "(Ljava/lang/String;)V", (void *)reset},
+    {"nativeCheck", "(Ljava/lang/String;)I", (void *)check},
+    {"nativeProtect", "(ILjava/lang/String;)V", (void *)protect},
+};
+
+int register_android_server_connectivity_Vpn(JNIEnv *env)
+{
+    return jniRegisterNativeMethods(env, "com/android/server/connectivity/Vpn",
+            gMethods, NELEM(gMethods));
+}
+
+};
diff --git a/services/jni/onload.cpp b/services/jni/onload.cpp
index 469e818..9dff48b 100644
--- a/services/jni/onload.cpp
+++ b/services/jni/onload.cpp
@@ -34,6 +34,7 @@
 int register_android_server_VibratorService(JNIEnv* env);
 int register_android_server_SystemServer(JNIEnv* env);
 int register_android_server_location_GpsLocationProvider(JNIEnv* env);
+int register_android_server_connectivity_Vpn(JNIEnv* env);
 };
 
 using namespace android;
@@ -63,6 +64,7 @@
     register_android_server_VibratorService(env);
     register_android_server_SystemServer(env);
     register_android_server_location_GpsLocationProvider(env);
+    register_android_server_connectivity_Vpn(env);
 
     return JNI_VERSION_1_4;
 }
diff --git a/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
new file mode 100644
index 0000000..9846372
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/NetworkStatsServiceTest.java
@@ -0,0 +1,280 @@
+/*
+ * 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 com.android.server;
+
+import static android.net.ConnectivityManager.CONNECTIVITY_ACTION;
+import static android.net.ConnectivityManager.TYPE_WIFI;
+import static android.net.NetworkStats.UID_ALL;
+import static android.net.TrafficStats.TEMPLATE_WIFI;
+import static android.text.format.DateUtils.DAY_IN_MILLIS;
+import static android.text.format.DateUtils.HOUR_IN_MILLIS;
+import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_POLL;
+import static org.easymock.EasyMock.anyLong;
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.eq;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.isA;
+
+import android.app.AlarmManager;
+import android.app.IAlarmManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.net.IConnectivityManager;
+import android.net.LinkProperties;
+import android.net.NetworkInfo;
+import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkState;
+import android.net.NetworkStats;
+import android.net.NetworkStatsHistory;
+import android.os.INetworkManagementService;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.util.TrustedTime;
+
+import com.android.server.net.NetworkStatsService;
+
+import org.easymock.EasyMock;
+
+import java.io.File;
+
+/**
+ * Tests for {@link NetworkStatsService}.
+ */
+@LargeTest
+public class NetworkStatsServiceTest extends AndroidTestCase {
+    private static final String TAG = "NetworkStatsServiceTest";
+
+    private static final String TEST_IFACE = "test0";
+    private static final long TEST_START = 1194220800000L;
+
+    private BroadcastInterceptingContext mServiceContext;
+    private File mStatsDir;
+
+    private INetworkManagementService mNetManager;
+    private IAlarmManager mAlarmManager;
+    private TrustedTime mTime;
+    private IConnectivityManager mConnManager;
+
+    private NetworkStatsService mService;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+
+        mServiceContext = new BroadcastInterceptingContext(getContext());
+        mStatsDir = getContext().getFilesDir();
+
+        mNetManager = createMock(INetworkManagementService.class);
+        mAlarmManager = createMock(IAlarmManager.class);
+        mTime = createMock(TrustedTime.class);
+        mConnManager = createMock(IConnectivityManager.class);
+
+        mService = new NetworkStatsService(
+                mServiceContext, mNetManager, mAlarmManager, mTime, mStatsDir);
+        mService.bindConnectivityManager(mConnManager);
+
+        expectSystemReady();
+
+        replay();
+        mService.systemReady();
+        verifyAndReset();
+
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        for (File file : mStatsDir.listFiles()) {
+            file.delete();
+        }
+
+        mServiceContext = null;
+        mStatsDir = null;
+
+        mNetManager = null;
+        mAlarmManager = null;
+        mTime = null;
+
+        mService = null;
+
+        super.tearDown();
+    }
+
+    private static NetworkState buildWifi() {
+        final NetworkInfo info = new NetworkInfo(TYPE_WIFI, 0, null, null);
+        info.setDetailedState(DetailedState.CONNECTED, null, null);
+        final LinkProperties prop = new LinkProperties();
+        prop.setInterfaceName(TEST_IFACE);
+        return new NetworkState(info, prop, null);
+    }
+
+    public void testHistoryForWifi() throws Exception {
+        long elapsedRealtime = 0;
+        NetworkState[] state = null;
+        NetworkStats stats = null;
+        NetworkStats detail = null;
+
+        // pretend that wifi network comes online; service should ask about full
+        // network state, and poll any existing interfaces before updating.
+        state = new NetworkState[] { buildWifi() };
+        stats = new NetworkStats.Builder(elapsedRealtime, 0).build();
+        detail = new NetworkStats.Builder(elapsedRealtime, 0).build();
+
+        expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+        expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
+        expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
+        expectTime(TEST_START + elapsedRealtime);
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        verifyAndReset();
+
+        // verify service has empty history for wifi
+        assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L);
+
+        // modify some number on wifi, and trigger poll event
+        elapsedRealtime += HOUR_IN_MILLIS;
+        stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry(
+                TEST_IFACE, UID_ALL, 1024L, 2048L).build();
+
+        expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
+        expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
+        expectTime(TEST_START + elapsedRealtime);
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+        verifyAndReset();
+
+        // verify service recorded history
+        assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L);
+
+        // and bump forward again, with counters going higher. this is
+        // important, since polling should correctly subtract last snapshot.
+        elapsedRealtime += DAY_IN_MILLIS;
+        stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry(
+                TEST_IFACE, UID_ALL, 4096L, 8192L).build();
+
+        expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
+        expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
+        expectTime(TEST_START + elapsedRealtime);
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+        verifyAndReset();
+
+        // verify service recorded history
+        assertNetworkTotal(TEMPLATE_WIFI, 4096L, 8192L);
+    }
+
+    public void testHistoryForRebootPersist() throws Exception {
+        long elapsedRealtime = 0;
+        NetworkState[] state = null;
+        NetworkStats stats = null;
+        NetworkStats detail = null;
+
+        // assert that no stats file exists
+        final File statsFile = new File(mStatsDir, "netstats.bin");
+        assertFalse(statsFile.exists());
+
+        // pretend that wifi network comes online; service should ask about full
+        // network state, and poll any existing interfaces before updating.
+        state = new NetworkState[] { buildWifi() };
+        stats = new NetworkStats.Builder(elapsedRealtime, 0).build();
+        detail = new NetworkStats.Builder(elapsedRealtime, 0).build();
+
+        expect(mConnManager.getAllNetworkState()).andReturn(state).atLeastOnce();
+        expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
+        expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
+        expectTime(TEST_START + elapsedRealtime);
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(CONNECTIVITY_ACTION));
+        verifyAndReset();
+
+        // verify service has empty history for wifi
+        assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L);
+
+        // modify some number on wifi, and trigger poll event
+        elapsedRealtime += HOUR_IN_MILLIS;
+        stats = new NetworkStats.Builder(elapsedRealtime, 1).addEntry(
+                TEST_IFACE, UID_ALL, 1024L, 2048L).build();
+
+        expect(mNetManager.getNetworkStatsSummary()).andReturn(stats).atLeastOnce();
+        expect(mNetManager.getNetworkStatsDetail()).andReturn(detail).atLeastOnce();
+        expectTime(TEST_START + elapsedRealtime);
+
+        replay();
+        mServiceContext.sendBroadcast(new Intent(ACTION_NETWORK_STATS_POLL));
+        verifyAndReset();
+
+        // verify service recorded history
+        assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L);
+
+        // graceful shutdown system, which should trigger persist of stats, and
+        // clear any values in memory.
+        mServiceContext.sendBroadcast(new Intent(Intent.ACTION_SHUTDOWN));
+
+        // talk with zombie service to assert stats have gone; and assert that
+        // we persisted them to file.
+        assertNetworkTotal(TEMPLATE_WIFI, 0L, 0L);
+        assertTrue(statsFile.exists());
+
+        // boot through serviceReady() again
+        expectSystemReady();
+
+        replay();
+        mService.systemReady();
+        verifyAndReset();
+
+        // after systemReady(), we should have historical stats loaded again
+        assertNetworkTotal(TEMPLATE_WIFI, 1024L, 2048L);
+
+    }
+
+    private void assertNetworkTotal(int template, long rx, long tx) {
+        final NetworkStatsHistory history = mService.getHistoryForNetwork(template);
+        final long[] total = history.getTotalData(Long.MIN_VALUE, Long.MAX_VALUE, null);
+        assertEquals(rx, total[0]);
+        assertEquals(tx, total[1]);
+    }
+
+    private void expectSystemReady() throws Exception {
+        mAlarmManager.remove(isA(PendingIntent.class));
+        expectLastCall().anyTimes();
+
+        mAlarmManager.setInexactRepeating(
+                eq(AlarmManager.ELAPSED_REALTIME), anyLong(), anyLong(), isA(PendingIntent.class));
+        expectLastCall().atLeastOnce();
+    }
+
+    public void expectTime(long currentTime) throws Exception {
+        expect(mTime.forceRefresh()).andReturn(false).anyTimes();
+        expect(mTime.hasCache()).andReturn(true).anyTimes();
+        expect(mTime.currentTimeMillis()).andReturn(currentTime).anyTimes();
+        expect(mTime.getCacheAge()).andReturn(0L).anyTimes();
+        expect(mTime.getCacheCertainty()).andReturn(0L).anyTimes();
+    }
+
+    private void replay() {
+        EasyMock.replay(mNetManager, mAlarmManager, mTime, mConnManager);
+    }
+
+    private void verifyAndReset() {
+        EasyMock.verify(mNetManager, mAlarmManager, mTime, mConnManager);
+        EasyMock.reset(mNetManager, mAlarmManager, mTime, mConnManager);
+    }
+}
diff --git a/telephony/java/android/telephony/SmsCbConstants.java b/telephony/java/android/telephony/SmsCbConstants.java
new file mode 100644
index 0000000..a1b4adf
--- /dev/null
+++ b/telephony/java/android/telephony/SmsCbConstants.java
@@ -0,0 +1,117 @@
+/*
+ * 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.telephony;
+
+/**
+ * Constants used in SMS Cell Broadcast messages.
+ *
+ * {@hide}
+ */
+public interface SmsCbConstants {
+    /** Start of PWS Message Identifier range (includes ETWS and CMAS). */
+    public static final int MESSAGE_ID_PWS_FIRST_IDENTIFIER = 0x1100;
+
+    /** Bitmask for messages of ETWS type (including future extensions). */
+    public static final int MESSAGE_ID_ETWS_TYPE_MASK       = 0xFFF8;
+
+    /** Value for messages of ETWS type after applying {@link #MESSAGE_ID_ETWS_TYPE_MASK}. */
+    public static final int MESSAGE_ID_ETWS_TYPE            = 0x1100;
+
+    /** ETWS Message Identifier for earthquake warning message. */
+    public static final int MESSAGE_ID_ETWS_EARTHQUAKE_WARNING      = 0x1100;
+
+    /** ETWS Message Identifier for tsunami warning message. */
+    public static final int MESSAGE_ID_ETWS_TSUNAMI_WARNING         = 0x1101;
+
+    /** ETWS Message Identifier for earthquake and tsunami combined warning message. */
+    public static final int MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING  = 0x1102;
+
+    /** ETWS Message Identifier for test message. */
+    public static final int MESSAGE_ID_ETWS_TEST_MESSAGE            = 0x1103;
+
+    /** ETWS Message Identifier for messages related to other emergency types. */
+    public static final int MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE    = 0x1104;
+
+    /** Start of CMAS Message Identifier range. */
+    public static final int MESSAGE_ID_CMAS_FIRST_IDENTIFIER                = 0x1112;
+
+    /** CMAS Message Identifier for Presidential Level alerts. */
+    public static final int MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL        = 0x1112;
+
+    /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed. */
+    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED = 0x1113;
+
+    /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely. */
+    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY  = 0x1114;
+
+    /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed. */
+    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED = 0x1115;
+
+    /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely. */
+    public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY   = 0x1116;
+
+    /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed. */
+    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED = 0x1117;
+
+    /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely. */
+    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY   = 0x1118;
+
+    /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed. */
+    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED  = 0x1119;
+
+    /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely. */
+    public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY    = 0x111A;
+
+    /** CMAS Message Identifier for Child Abduction Emergency (Amber Alert). */
+    public static final int MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY = 0x111B;
+
+    /** CMAS Message Identifier for the Required Monthly Test. */
+    public static final int MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST     = 0x111C;
+
+    /** CMAS Message Identifier for CMAS Exercise. */
+    public static final int MESSAGE_ID_CMAS_ALERT_EXERCISE                  = 0x111D;
+
+    /** CMAS Message Identifier for operator defined use. */
+    public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE      = 0x111E;
+
+    /** End of CMAS Message Identifier range (including future extensions). */
+    public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER                 = 0x112F;
+
+    /** End of PWS Message Identifier range (includes ETWS, CMAS, and future extensions). */
+    public static final int MESSAGE_ID_PWS_LAST_IDENTIFIER                  = 0x18FF;
+
+    /** ETWS message code flag to activate the popup display. */
+    public static final int MESSAGE_CODE_ETWS_ACTIVATE_POPUP                = 0x100;
+
+    /** ETWS message code flag to activate the emergency user alert. */
+    public static final int MESSAGE_CODE_ETWS_EMERGENCY_USER_ALERT          = 0x200;
+
+    /** ETWS warning type value for earthquake. */
+    public static final int ETWS_WARNING_TYPE_EARTHQUAKE                    = 0x00;
+
+    /** ETWS warning type value for tsunami. */
+    public static final int ETWS_WARNING_TYPE_TSUNAMI                       = 0x01;
+
+    /** ETWS warning type value for earthquake and tsunami. */
+    public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI        = 0x02;
+
+    /** ETWS warning type value for test broadcast. */
+    public static final int ETWS_WARNING_TYPE_TEST                          = 0x03;
+
+    /** ETWS warning type value for other notifications. */
+    public static final int ETWS_WARNING_TYPE_OTHER                         = 0x04;
+}
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 3543275..383e0f9 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -16,7 +16,11 @@
 
 package android.telephony;
 
+import android.text.format.Time;
+import android.util.Log;
+
 import com.android.internal.telephony.GsmAlphabet;
+import com.android.internal.telephony.IccUtils;
 import com.android.internal.telephony.gsm.SmsCbHeader;
 
 import java.io.UnsupportedEncodingException;
@@ -58,10 +62,13 @@
         try {
             return new SmsCbMessage(pdu);
         } catch (IllegalArgumentException e) {
+            Log.w(LOG_TAG, "Failed parsing SMS-CB pdu", e);
             return null;
         }
     }
 
+    private static final String LOG_TAG = "SMSCB";
+
     /**
      * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
      */
@@ -80,15 +87,34 @@
 
     private static final char CARRIAGE_RETURN = 0x0d;
 
+    private static final int PDU_BODY_PAGE_LENGTH = 82;
+
     private SmsCbHeader mHeader;
 
     private String mLanguage;
 
     private String mBody;
 
+    /** Timestamp of ETWS primary notification with security. */
+    private long mPrimaryNotificationTimestamp;
+
+    /** 43 byte digital signature of ETWS primary notification with security. */
+    private byte[] mPrimaryNotificationDigitalSignature;
+
     private SmsCbMessage(byte[] pdu) throws IllegalArgumentException {
         mHeader = new SmsCbHeader(pdu);
-        parseBody(pdu);
+        if (mHeader.format == SmsCbHeader.FORMAT_ETWS_PRIMARY) {
+            mBody = "ETWS";
+            // ETWS primary notification with security is 56 octets in length
+            if (pdu.length >= SmsCbHeader.PDU_LENGTH_ETWS) {
+                mPrimaryNotificationTimestamp = getTimestampMillis(pdu);
+                mPrimaryNotificationDigitalSignature = new byte[43];
+                // digital signature starts after 6 byte header and 7 byte timestamp
+                System.arraycopy(pdu, 13, mPrimaryNotificationDigitalSignature, 0, 43);
+            }
+        } else {
+            parseBody(pdu);
+        }
     }
 
     /**
@@ -149,6 +175,62 @@
         return mHeader.updateNumber;
     }
 
+    /**
+     * Get the format of this message.
+     * @return {@link SmsCbHeader#FORMAT_GSM}, {@link SmsCbHeader#FORMAT_UMTS}, or
+     *         {@link SmsCbHeader#FORMAT_ETWS_PRIMARY}
+     */
+    public int getMessageFormat() {
+        return mHeader.format;
+    }
+
+    /**
+     * For ETWS primary notifications, return the emergency user alert flag.
+     * @return true to notify terminal to activate emergency user alert; false otherwise
+     */
+    public boolean getEtwsEmergencyUserAlert() {
+        return mHeader.etwsEmergencyUserAlert;
+    }
+
+    /**
+     * For ETWS primary notifications, return the popup flag.
+     * @return true to notify terminal to activate display popup; false otherwise
+     */
+    public boolean getEtwsPopup() {
+        return mHeader.etwsPopup;
+    }
+
+    /**
+     * For ETWS primary notifications, return the warning type.
+     * @return a value such as {@link SmsCbConstants#ETWS_WARNING_TYPE_EARTHQUAKE}
+     */
+    public int getEtwsWarningType() {
+        return mHeader.etwsWarningType;
+    }
+
+    /**
+     * For ETWS primary notifications, return the Warning-Security-Information timestamp.
+     * @return a timestamp in System.currentTimeMillis() format.
+     */
+    public long getEtwsSecurityTimestamp() {
+        return mPrimaryNotificationTimestamp;
+    }
+
+    /**
+     * For ETWS primary notifications, return the 43 byte digital signature.
+     * @return a byte array containing a copy of the digital signature
+     */
+    public byte[] getEtwsSecuritySignature() {
+        return mPrimaryNotificationDigitalSignature.clone();
+    }
+
+    /**
+     * Parse and unpack the body text according to the encoding in the DCS.
+     * After completing successfully this method will have assigned the body
+     * text into mBody, and optionally the language code into mLanguage
+     *
+     * @param pdu The pdu
+     */
     private void parseBody(byte[] pdu) {
         int encoding;
         boolean hasLanguageIndicator = false;
@@ -221,28 +303,81 @@
                 break;
         }
 
+        if (mHeader.format == SmsCbHeader.FORMAT_UMTS) {
+            // Payload may contain multiple pages
+            int nrPages = pdu[SmsCbHeader.PDU_HEADER_LENGTH];
+
+            if (pdu.length < SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1)
+                    * nrPages) {
+                throw new IllegalArgumentException("Pdu length " + pdu.length + " does not match "
+                        + nrPages + " pages");
+            }
+
+            StringBuilder sb = new StringBuilder();
+
+            for (int i = 0; i < nrPages; i++) {
+                // Each page is 82 bytes followed by a length octet indicating
+                // the number of useful octets within those 82
+                int offset = SmsCbHeader.PDU_HEADER_LENGTH + 1 + (PDU_BODY_PAGE_LENGTH + 1) * i;
+                int length = pdu[offset + PDU_BODY_PAGE_LENGTH];
+
+                if (length > PDU_BODY_PAGE_LENGTH) {
+                    throw new IllegalArgumentException("Page length " + length
+                            + " exceeds maximum value " + PDU_BODY_PAGE_LENGTH);
+                }
+
+                sb.append(unpackBody(pdu, encoding, offset, length, hasLanguageIndicator));
+            }
+            mBody = sb.toString();
+        } else {
+            // Payload is one single page
+            int offset = SmsCbHeader.PDU_HEADER_LENGTH;
+            int length = pdu.length - offset;
+
+            mBody = unpackBody(pdu, encoding, offset, length, hasLanguageIndicator);
+        }
+    }
+
+    /**
+     * Unpack body text from the pdu using the given encoding, position and
+     * length within the pdu
+     *
+     * @param pdu The pdu
+     * @param encoding The encoding, as derived from the DCS
+     * @param offset Position of the first byte to unpack
+     * @param length Number of bytes to unpack
+     * @param hasLanguageIndicator true if the body text is preceded by a
+     *            language indicator. If so, this method will as a side-effect
+     *            assign the extracted language code into mLanguage
+     * @return Body text
+     */
+    private String unpackBody(byte[] pdu, int encoding, int offset, int length,
+            boolean hasLanguageIndicator) {
+        String body = null;
+
         switch (encoding) {
             case SmsMessage.ENCODING_7BIT:
-                mBody = GsmAlphabet.gsm7BitPackedToString(pdu, SmsCbHeader.PDU_HEADER_LENGTH,
-                        (pdu.length - SmsCbHeader.PDU_HEADER_LENGTH) * 8 / 7);
+                body = GsmAlphabet.gsm7BitPackedToString(pdu, offset, length * 8 / 7);
 
-                if (hasLanguageIndicator && mBody != null && mBody.length() > 2) {
-                    mLanguage = mBody.substring(0, 2);
-                    mBody = mBody.substring(3);
+                if (hasLanguageIndicator && body != null && body.length() > 2) {
+                    // Language is two GSM characters followed by a CR.
+                    // The actual body text is offset by 3 characters.
+                    mLanguage = body.substring(0, 2);
+                    body = body.substring(3);
                 }
                 break;
 
             case SmsMessage.ENCODING_16BIT:
-                int offset = SmsCbHeader.PDU_HEADER_LENGTH;
-
-                if (hasLanguageIndicator && pdu.length >= SmsCbHeader.PDU_HEADER_LENGTH + 2) {
-                    mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu,
-                            SmsCbHeader.PDU_HEADER_LENGTH, 2);
+                if (hasLanguageIndicator && pdu.length >= offset + 2) {
+                    // Language is two GSM characters.
+                    // The actual body text is offset by 2 bytes.
+                    mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu, offset, 2);
                     offset += 2;
+                    length -= 2;
                 }
 
                 try {
-                    mBody = new String(pdu, offset, (pdu.length & 0xfffe) - offset, "utf-16");
+                    body = new String(pdu, offset, (length & 0xfffe), "utf-16");
                 } catch (UnsupportedEncodingException e) {
                     // Eeeek
                 }
@@ -252,16 +387,73 @@
                 break;
         }
 
-        if (mBody != null) {
+        if (body != null) {
             // Remove trailing carriage return
-            for (int i = mBody.length() - 1; i >= 0; i--) {
-                if (mBody.charAt(i) != CARRIAGE_RETURN) {
-                    mBody = mBody.substring(0, i + 1);
+            for (int i = body.length() - 1; i >= 0; i--) {
+                if (body.charAt(i) != CARRIAGE_RETURN) {
+                    body = body.substring(0, i + 1);
                     break;
                 }
             }
         } else {
-            mBody = "";
+            body = "";
         }
+
+        return body;
+    }
+
+    /**
+     * Parses an ETWS primary notification timestamp and returns a currentTimeMillis()-style
+     * timestamp. Copied from com.android.internal.telephony.gsm.SmsMessage.
+     * @param pdu the ETWS primary notification PDU to decode
+     * @return the UTC timestamp from the Warning-Security-Information parameter
+     */
+    private long getTimestampMillis(byte[] pdu) {
+        // Timestamp starts after CB header, in pdu[6]
+        int year = IccUtils.gsmBcdByteToInt(pdu[6]);
+        int month = IccUtils.gsmBcdByteToInt(pdu[7]);
+        int day = IccUtils.gsmBcdByteToInt(pdu[8]);
+        int hour = IccUtils.gsmBcdByteToInt(pdu[9]);
+        int minute = IccUtils.gsmBcdByteToInt(pdu[10]);
+        int second = IccUtils.gsmBcdByteToInt(pdu[11]);
+
+        // For the timezone, the most significant bit of the
+        // least significant nibble is the sign byte
+        // (meaning the max range of this field is 79 quarter-hours,
+        // which is more than enough)
+
+        byte tzByte = pdu[12];
+
+        // Mask out sign bit.
+        int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08)));
+
+        timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset;
+
+        Time time = new Time(Time.TIMEZONE_UTC);
+
+        // It's 2006.  Should I really support years < 2000?
+        time.year = year >= 90 ? year + 1900 : year + 2000;
+        time.month = month - 1;
+        time.monthDay = day;
+        time.hour = hour;
+        time.minute = minute;
+        time.second = second;
+
+        // Timezone offset is in quarter hours.
+        return time.toMillis(true) - (timezoneOffset * 15 * 60 * 1000);
+    }
+
+    /**
+     * Append text to the message body. This is used to concatenate multi-page GSM broadcasts.
+     * @param body the text to append to this message
+     */
+    public void appendToBody(String body) {
+        mBody = mBody + body;
+    }
+
+    @Override
+    public String toString() {
+        return "SmsCbMessage{" + mHeader.toString() + ", language=" + mLanguage +
+                ", body=\"" + mBody + "\"}";
     }
 }
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 6a346af..5bdc146 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -345,7 +345,7 @@
      * message identifier. Note that if two different clients enable the same
      * message identifier, they must both disable it for the device to stop
      * receiving those messages. All received messages will be broadcast in an
-     * intent with the action "android.provider.telephony.SMS_CB_RECEIVED".
+     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
      * Note: This call is blocking, callers may want to avoid calling it from
      * the main thread of an application.
      *
@@ -401,6 +401,68 @@
     }
 
     /**
+     * Enable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier range. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages. All received messages will be broadcast in an
+     * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * @param startMessageId first message identifier as specified in TS 23.041
+     * @param endMessageId last message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     * @see #disableCellBroadcastRange(int, int)
+     *
+     * {@hide}
+     */
+    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.enableCellBroadcastRange(startMessageId, endMessageId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
+     * Disable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier range. Note that if two different clients enable the same
+     * message identifier, they must both disable it for the device to stop
+     * receiving those messages.
+     * Note: This call is blocking, callers may want to avoid calling it from
+     * the main thread of an application.
+     *
+     * @param startMessageId first message identifier as specified in TS 23.041
+     * @param endMessageId last message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     *
+     * @see #enableCellBroadcastRange(int, int)
+     *
+     * {@hide}
+     */
+    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
+        boolean success = false;
+
+        try {
+            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+            if (iccISms != null) {
+                success = iccISms.disableCellBroadcastRange(startMessageId, endMessageId);
+            }
+        } catch (RemoteException ex) {
+            // ignore it
+        }
+
+        return success;
+    }
+
+    /**
      * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
      * records returned by <code>getAllMessagesFromIcc()</code>
      *
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 8732e21..184d665 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -191,6 +191,10 @@
      *   {@link android.Manifest.permission#READ_PHONE_STATE READ_PHONE_STATE}
      */
     public String getDeviceId() {
+        if (!isVoiceCapable()) {
+            return null;
+        }
+
         try {
             return getSubscriberInfo().getDeviceId();
         } catch (RemoteException ex) {
@@ -426,8 +430,7 @@
     public static final int NETWORK_TYPE_LTE = 13;
     /** Current network is eHRPD */
     public static final int NETWORK_TYPE_EHRPD = 14;
-    /** Current network is HSPA+
-     * @hide */
+    /** Current network is HSPA+ */
     public static final int NETWORK_TYPE_HSPAP = 15;
 
     /**
@@ -450,6 +453,7 @@
      * @see #NETWORK_TYPE_IDEN
      * @see #NETWORK_TYPE_LTE
      * @see #NETWORK_TYPE_EHRPD
+     * @see #NETWORK_TYPE_HSPAP
      */
     public int getNetworkType() {
         try{
@@ -469,6 +473,46 @@
         }
     }
 
+    /** Unknown network class. {@hide} */
+    public static final int NETWORK_CLASS_UNKNOWN = 0;
+    /** Class of broadly defined "2G" networks. {@hide} */
+    public static final int NETWORK_CLASS_2_G = 1;
+    /** Class of broadly defined "3G" networks. {@hide} */
+    public static final int NETWORK_CLASS_3_G = 2;
+    /** Class of broadly defined "4G" networks. {@hide} */
+    public static final int NETWORK_CLASS_4_G = 3;
+
+    /**
+     * Return general class of network type, such as "3G" or "4G". In cases
+     * where classification is contentious, this method is conservative.
+     *
+     * @hide
+     */
+    public static int getNetworkClass(int networkType) {
+        switch (networkType) {
+            case NETWORK_TYPE_GPRS:
+            case NETWORK_TYPE_EDGE:
+            case NETWORK_TYPE_CDMA:
+            case NETWORK_TYPE_1xRTT:
+            case NETWORK_TYPE_IDEN:
+                return NETWORK_CLASS_2_G;
+            case NETWORK_TYPE_UMTS:
+            case NETWORK_TYPE_EVDO_0:
+            case NETWORK_TYPE_EVDO_A:
+            case NETWORK_TYPE_HSDPA:
+            case NETWORK_TYPE_HSUPA:
+            case NETWORK_TYPE_HSPA:
+            case NETWORK_TYPE_EVDO_B:
+            case NETWORK_TYPE_EHRPD:
+            case NETWORK_TYPE_HSPAP:
+                return NETWORK_CLASS_3_G;
+            case NETWORK_TYPE_LTE:
+                return NETWORK_CLASS_4_G;
+            default:
+                return NETWORK_CLASS_UNKNOWN;
+        }
+    }
+
     /**
      * Returns a string representation of the radio technology (network type)
      * currently in use on the device.
@@ -477,7 +521,12 @@
      * @hide pending API council review
      */
     public String getNetworkTypeName() {
-        switch (getNetworkType()) {
+        return getNetworkTypeName(getNetworkType());
+    }
+
+    /** {@hide} */
+    public static String getNetworkTypeName(int type) {
+        switch (type) {
             case NETWORK_TYPE_GPRS:
                 return "GPRS";
             case NETWORK_TYPE_EDGE:
@@ -506,6 +555,8 @@
                 return "CDMA - eHRPD";
             case NETWORK_TYPE_IDEN:
                 return "iDEN";
+            case NETWORK_TYPE_HSPAP:
+                return "HSPA+";
             default:
                 return "UNKNOWN";
         }
diff --git a/telephony/java/com/android/internal/telephony/BaseCommands.java b/telephony/java/com/android/internal/telephony/BaseCommands.java
index 0c4581b..8427d14 100644
--- a/telephony/java/com/android/internal/telephony/BaseCommands.java
+++ b/telephony/java/com/android/internal/telephony/BaseCommands.java
@@ -96,8 +96,10 @@
     protected Registrant mRestrictedStateRegistrant;
     protected Registrant mGsmBroadcastSmsRegistrant;
 
-    // Network Mode received from PhoneFactory
-    protected int mNetworkMode;
+    // Preferred network type received from PhoneFactory.
+    // This is used when establishing a connection to the
+    // vendor ril so it starts up in the correct mode.
+    protected int mPreferredNetworkType;
     // CDMA subscription received from PhoneFactory
     protected int mCdmaSubscription;
     // Type of Phone, GSM or CDMA. Set by CDMAPhone or GSMPhone.
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index c7c91e4..a3f4f1f 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -86,6 +86,8 @@
      * GSM_EXTENDED_ESCAPE if this character is in the extended table.
      * In this case, you must call charToGsmExtended() for the value
      * that should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string.
+     * @param c the character to convert
+     * @return the GSM 7 bit table index for the specified character
      */
     public static int
     charToGsm(char c) {
@@ -99,12 +101,15 @@
 
     /**
      * Converts a char to a GSM 7 bit table index.
+     * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table.
+     * In this case, you must call charToGsmExtended() for the value that
+     * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string.
+     *
+     * @param c the character to convert
      * @param throwException If true, throws EncodeException on invalid char.
      *   If false, returns GSM alphabet ' ' char.
-     *
-     * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table
-     * In this case, you must call charToGsmExtended() for the value that
-     * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string
+     * @throws EncodeException encode error when throwException is true
+     * @return the GSM 7 bit table index for the specified character
      */
     public static int
     charToGsm(char c, boolean throwException) throws EncodeException {
@@ -133,6 +138,8 @@
      * Converts a char to an extended GSM 7 bit table index.
      * Extended chars should be escaped with GSM_EXTENDED_ESCAPE.
      * Returns ' ' in GSM alphabet if there's no possible match.
+     * @param c the character to convert
+     * @return the GSM 7 bit extended table index for the specified character
      */
     public static int
     charToGsmExtended(char c) {
@@ -155,6 +162,9 @@
      * gsmExtendedToChar().
      *
      * If an unmappable value is passed (one greater than 127), ' ' is returned.
+     *
+     * @param gsmChar the GSM 7 bit table index to convert
+     * @return the decoded character
      */
     public static char
     gsmToChar(int gsmChar) {
@@ -174,6 +184,9 @@
      *
      * If an unmappable value is passed, the character from the GSM 7 bit
      * default table will be used (table 6.2.1.1 of TS 23.038).
+     *
+     * @param gsmChar the GSM 7 bit extended table index to convert
+     * @return the decoded character
      */
     public static char
     gsmExtendedToChar(int gsmChar) {
@@ -244,6 +257,26 @@
      * septets.
      *
      * @param data the data string to encode
+     * @return the encoded string
+     * @throws EncodeException if String is too large to encode
+     */
+    public static byte[] stringToGsm7BitPacked(String data)
+            throws EncodeException {
+        return stringToGsm7BitPacked(data, 0, true, 0, 0);
+    }
+
+    /**
+     * Converts a String into a byte array containing
+     * the 7-bit packed GSM Alphabet representation of the string.
+     *
+     * Unencodable chars are encoded as spaces
+     *
+     * Byte 0 in the returned byte array is the count of septets used
+     * The returned byte array is the minimum size required to store
+     * the packed septets. The returned array cannot contain more than 255
+     * septets.
+     *
+     * @param data the data string to encode
      * @param languageTable the 7 bit language table, or 0 for the default GSM alphabet
      * @param languageShiftTable the 7 bit single shift language table, or 0 for the default
      *     GSM extension table
@@ -449,6 +482,11 @@
      *
      * Field may be padded with trailing 0xff's. The decode stops
      * at the first 0xff encountered.
+     *
+     * @param data the byte array to decode
+     * @param offset array offset for the first character to decode
+     * @param length the number of bytes to decode
+     * @return the decoded string
      */
     public static String
     gsm8BitUnpackedToString(byte[] data, int offset, int length) {
@@ -532,6 +570,8 @@
     /**
      * Convert a string into an 8-bit unpacked GSM alphabet byte array.
      * Always uses GSM default 7-bit alphabet and extension table.
+     * @param s the string to encode
+     * @return the 8-bit GSM encoded byte array for the string
      */
     public static byte[]
     stringToGsm8BitPacked(String s) {
@@ -550,11 +590,13 @@
 
     /**
      * Write a String into a GSM 8-bit unpacked field of
-     * @param length size at @param offset in @param dest
-     *
      * Field is padded with 0xff's, string is truncated if necessary
+     *
+     * @param s the string to encode
+     * @param dest the destination byte array
+     * @param offset the starting offset for the encoded string
+     * @param length the maximum number of bytes to write
      */
-
     public static void
     stringToGsm8BitUnpackedField(String s, byte dest[], int offset, int length) {
         int outByteIndex = offset;
@@ -596,6 +638,8 @@
     /**
      * Returns the count of 7-bit GSM alphabet characters
      * needed to represent this character. Counts unencodable char as 1 septet.
+     * @param c the character to examine
+     * @return the number of septets for this character
      */
     public static int
     countGsmSeptets(char c) {
@@ -610,8 +654,11 @@
     /**
      * Returns the count of 7-bit GSM alphabet characters
      * needed to represent this character using the default 7 bit GSM alphabet.
+     * @param c the character to examine
      * @param throwsException If true, throws EncodeException if unencodable
-     * char. Otherwise, counts invalid char as 1 septet
+     * char. Otherwise, counts invalid char as 1 septet.
+     * @return the number of septets for this character
+     * @throws EncodeException the character can't be encoded and throwsException is true
      */
     public static int
     countGsmSeptets(char c, boolean throwsException) throws EncodeException {
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 90de5e1..735f986 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -170,4 +170,32 @@
      */
     boolean disableCellBroadcast(int messageIdentifier);
 
+    /**
+     * Enable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier range. Note that if two different clients enable
+     * a message identifier range, they must both disable it for the device
+     * to stop receiving those messages.
+     *
+     * @param startMessageId first message identifier as specified in TS 23.041
+     * @param endMessageId last message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     *
+     * @see #disableCellBroadcastRange(int, int)
+     */
+    boolean enableCellBroadcastRange(int startMessageId, int endMessageId);
+
+    /**
+     * Disable reception of cell broadcast (SMS-CB) messages with the given
+     * message identifier range. Note that if two different clients enable
+     * a message identifier range, they must both disable it for the device
+     * to stop receiving those messages.
+     *
+     * @param startMessageId first message identifier as specified in TS 23.041
+     * @param endMessageId last message identifier as specified in TS 23.041
+     * @return true if successful, false otherwise
+     *
+     * @see #enableCellBroadcastRange(int, int)
+     */
+    boolean disableCellBroadcastRange(int startMessageId, int endMessageId);
+
 }
diff --git a/telephony/java/com/android/internal/telephony/IccConstants.java b/telephony/java/com/android/internal/telephony/IccConstants.java
index cafc79b..1ba6dfe 100644
--- a/telephony/java/com/android/internal/telephony/IccConstants.java
+++ b/telephony/java/com/android/internal/telephony/IccConstants.java
@@ -63,6 +63,10 @@
     // 3GPP2 C.S0065
     static final int EF_CSIM_LI = 0x6F3A;
     static final int EF_CSIM_SPN =0x6F41;
+    static final int EF_CSIM_MDN = 0x6F44;
+    static final int EF_CSIM_IMSIM = 0x6F22;
+    static final int EF_CSIM_CDMAHOME = 0x6F28;
+    static final int EF_CSIM_EPRL = 0x6F5A;
 
     //ISIM access
     static final int EF_IMPU = 0x6f04;
diff --git a/telephony/java/com/android/internal/telephony/IccRecords.java b/telephony/java/com/android/internal/telephony/IccRecords.java
index 10a3b69..3a27901 100644
--- a/telephony/java/com/android/internal/telephony/IccRecords.java
+++ b/telephony/java/com/android/internal/telephony/IccRecords.java
@@ -288,6 +288,16 @@
     }
 
     /**
+     * Indicates wether SIM is in provisioned state or not.
+     * Overridden only if SIM can be dynamically provisioned via OTA.
+     *
+     * @return true if provisioned
+     */
+    public boolean isProvisioned () {
+        return true;
+    }
+
+    /**
      * Write string to log file
      *
      * @param s is the string to write
diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
index 5049249..54de508 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
@@ -76,4 +76,13 @@
         return mIccSmsInterfaceManager.disableCellBroadcast(messageIdentifier);
     }
 
+    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId)
+            throws android.os.RemoteException {
+        return mIccSmsInterfaceManager.enableCellBroadcastRange(startMessageId, endMessageId);
+    }
+
+    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId)
+            throws android.os.RemoteException {
+        return mIccSmsInterfaceManager.disableCellBroadcastRange(startMessageId, endMessageId);
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/IccUtils.java b/telephony/java/com/android/internal/telephony/IccUtils.java
index c3b0ffc..a966f76 100644
--- a/telephony/java/com/android/internal/telephony/IccUtils.java
+++ b/telephony/java/com/android/internal/telephony/IccUtils.java
@@ -63,6 +63,29 @@
         return ret.toString();
     }
 
+    /**
+     * Decode cdma byte into String.
+     */
+    public static String
+    cdmaBcdToString(byte[] data, int offset, int length) {
+        StringBuilder ret = new StringBuilder(length);
+
+        int count = 0;
+        for (int i = offset; count < length; i++) {
+            int v;
+            v = data[i] & 0xf;
+            if (v > 9)  v = 0;
+            ret.append((char)('0' + v));
+
+            if (++count == length) break;
+
+            v = (data[i] >> 4) & 0xf;
+            if (v > 9)  v = 0;
+            ret.append((char)('0' + v));
+            ++count;
+        }
+        return ret.toString();
+    }
 
     /**
      * Decodes a GSM-style BCD byte, returning an int ranging from 0-99.
diff --git a/telephony/java/com/android/internal/telephony/IntRangeManager.java b/telephony/java/com/android/internal/telephony/IntRangeManager.java
new file mode 100644
index 0000000..970bc44
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IntRangeManager.java
@@ -0,0 +1,568 @@
+/*
+ * 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 com.android.internal.telephony;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Clients can enable reception of SMS-CB messages for specific ranges of
+ * message identifiers (channels). This class keeps track of the currently
+ * enabled message identifiers and calls abstract methods to update the
+ * radio when the range of enabled message identifiers changes.
+ *
+ * An update is a call to {@link #startUpdate} followed by zero or more
+ * calls to {@link #addRange} followed by a call to {@link #finishUpdate}.
+ * Calls to {@link #enableRange} and {@link #disableRange} will perform
+ * an incremental update operation if the enabled ranges have changed.
+ * A full update operation (i.e. after a radio reset) can be performed
+ * by a call to {@link #updateRanges}.
+ *
+ * Clients are identified by String (the name associated with the User ID
+ * of the caller) so that a call to remove a range can be mapped to the
+ * client that enabled that range (or else rejected).
+ */
+public abstract class IntRangeManager {
+
+    /**
+     * Initial capacity for IntRange clients array list. There will be
+     * few cell broadcast listeners on a typical device, so this can be small.
+     */
+    private static final int INITIAL_CLIENTS_ARRAY_SIZE = 4;
+
+    /**
+     * One or more clients forming the continuous range [startId, endId].
+     * <p>When a client is added, the IntRange may merge with one or more
+     * adjacent IntRanges to form a single combined IntRange.
+     * <p>When a client is removed, the IntRange may divide into several
+     * non-contiguous IntRanges.
+     */
+    private class IntRange {
+        int startId;
+        int endId;
+        // sorted by earliest start id
+        final ArrayList<ClientRange> clients;
+
+        /**
+         * Create a new IntRange with a single client.
+         * @param startId the first id included in the range
+         * @param endId the last id included in the range
+         * @param client the client requesting the enabled range
+         */
+        IntRange(int startId, int endId, String client) {
+            this.startId = startId;
+            this.endId = endId;
+            clients = new ArrayList<ClientRange>(INITIAL_CLIENTS_ARRAY_SIZE);
+            clients.add(new ClientRange(startId, endId, client));
+        }
+
+        /**
+         * Create a new IntRange for an existing ClientRange.
+         * @param clientRange the initial ClientRange to add
+         */
+        IntRange(ClientRange clientRange) {
+            startId = clientRange.startId;
+            endId = clientRange.endId;
+            clients = new ArrayList<ClientRange>(INITIAL_CLIENTS_ARRAY_SIZE);
+            clients.add(clientRange);
+        }
+
+        /**
+         * Create a new IntRange from an existing IntRange. This is used for
+         * removing a ClientRange, because new IntRanges may need to be created
+         * for any gaps that open up after the ClientRange is removed. A copy
+         * is made of the elements of the original IntRange preceding the element
+         * that is being removed. The following elements will be added to this
+         * IntRange or to a new IntRange when a gap is found.
+         * @param intRange the original IntRange to copy elements from
+         * @param numElements the number of elements to copy from the original
+         */
+        IntRange(IntRange intRange, int numElements) {
+            this.startId = intRange.startId;
+            this.endId = intRange.endId;
+            this.clients = new ArrayList<ClientRange>(intRange.clients.size());
+            for (int i=0; i < numElements; i++) {
+                this.clients.add(intRange.clients.get(i));
+            }
+        }
+
+        /**
+         * Insert new ClientRange in order by start id.
+         * <p>If the new ClientRange is known to be sorted before or after the
+         * existing ClientRanges, or at a particular index, it can be added
+         * to the clients array list directly, instead of via this method.
+         * <p>Note that this can be changed from linear to binary search if the
+         * number of clients grows large enough that it would make a difference.
+         * @param range the new ClientRange to insert
+         */
+        void insert(ClientRange range) {
+            int len = clients.size();
+            for (int i=0; i < len; i++) {
+                ClientRange nextRange = clients.get(i);
+                if (range.startId <= nextRange.startId) {
+                    // ignore duplicate ranges from the same client
+                    if (!range.equals(nextRange)) {
+                        clients.add(i, range);
+                    }
+                    return;
+                }
+            }
+            clients.add(range);    // append to end of list
+        }
+    }
+
+    /**
+     * The message id range for a single client.
+     */
+    private class ClientRange {
+        final int startId;
+        final int endId;
+        final String client;
+
+        ClientRange(int startId, int endId, String client) {
+            this.startId = startId;
+            this.endId = endId;
+            this.client = client;
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            if (o != null && o instanceof ClientRange) {
+                ClientRange other = (ClientRange) o;
+                return startId == other.startId &&
+                        endId == other.endId &&
+                        client.equals(other.client);
+            } else {
+                return false;
+            }
+        }
+
+        @Override
+        public int hashCode() {
+            return (startId * 31 + endId) * 31 + client.hashCode();
+        }
+    }
+
+    /**
+     * List of integer ranges, one per client, sorted by start id.
+     */
+    private ArrayList<IntRange> mRanges = new ArrayList<IntRange>();
+
+    protected IntRangeManager() {}
+
+    /**
+     * Enable a range for the specified client and update ranges
+     * if necessary. If {@link #finishUpdate} returns failure,
+     * false is returned and the range is not added.
+     *
+     * @param startId the first id included in the range
+     * @param endId the last id included in the range
+     * @param client the client requesting the enabled range
+     * @return true if successful, false otherwise
+     */
+    public synchronized boolean enableRange(int startId, int endId, String client) {
+        int len = mRanges.size();
+
+        // empty range list: add the initial IntRange
+        if (len == 0) {
+            if (tryAddSingleRange(startId, endId, true)) {
+                mRanges.add(new IntRange(startId, endId, client));
+                return true;
+            } else {
+                return false;   // failed to update radio
+            }
+        }
+
+        for (int startIndex = 0; startIndex < len; startIndex++) {
+            IntRange range = mRanges.get(startIndex);
+            if (startId < range.startId) {
+                // test if new range completely precedes this range
+                // note that [1, 4] and [5, 6] coalesce to [1, 6]
+                if ((endId + 1) < range.startId) {
+                    // insert new int range before previous first range
+                    if (tryAddSingleRange(startId, endId, true)) {
+                        mRanges.add(startIndex, new IntRange(startId, endId, client));
+                        return true;
+                    } else {
+                        return false;   // failed to update radio
+                    }
+                } else if (endId <= range.endId) {
+                    // extend the start of this range
+                    if (tryAddSingleRange(startId, range.startId - 1, true)) {
+                        range.startId = startId;
+                        range.clients.add(0, new ClientRange(startId, endId, client));
+                        return true;
+                    } else {
+                        return false;   // failed to update radio
+                    }
+                } else {
+                    // find last range that can coalesce into the new combined range
+                    for (int endIndex = startIndex+1; endIndex < len; endIndex++) {
+                        IntRange endRange = mRanges.get(endIndex);
+                        if ((endId + 1) < endRange.startId) {
+                            // try to add entire new range
+                            if (tryAddSingleRange(startId, endId, true)) {
+                                range.startId = startId;
+                                range.endId = endId;
+                                // insert new ClientRange before existing ranges
+                                range.clients.add(0, new ClientRange(startId, endId, client));
+                                // coalesce range with following ranges up to endIndex-1
+                                // remove each range after adding its elements, so the index
+                                // of the next range to join is always startIndex+1.
+                                // i is the index if no elements were removed: we only care
+                                // about the number of loop iterations, not the value of i.
+                                int joinIndex = startIndex + 1;
+                                for (int i = joinIndex; i < endIndex; i++) {
+                                    IntRange joinRange = mRanges.get(joinIndex);
+                                    range.clients.addAll(joinRange.clients);
+                                    mRanges.remove(joinRange);
+                                }
+                                return true;
+                            } else {
+                                return false;   // failed to update radio
+                            }
+                        } else if (endId <= endRange.endId) {
+                            // add range from start id to start of last overlapping range,
+                            // values from endRange.startId to endId are already enabled
+                            if (tryAddSingleRange(startId, endRange.startId - 1, true)) {
+                                range.startId = startId;
+                                range.endId = endRange.endId;
+                                // insert new ClientRange before existing ranges
+                                range.clients.add(0, new ClientRange(startId, endId, client));
+                                // coalesce range with following ranges up to endIndex
+                                // remove each range after adding its elements, so the index
+                                // of the next range to join is always startIndex+1.
+                                // i is the index if no elements were removed: we only care
+                                // about the number of loop iterations, not the value of i.
+                                int joinIndex = startIndex + 1;
+                                for (int i = joinIndex; i <= endIndex; i++) {
+                                    IntRange joinRange = mRanges.get(joinIndex);
+                                    range.clients.addAll(joinRange.clients);
+                                    mRanges.remove(joinRange);
+                                }
+                                return true;
+                            } else {
+                                return false;   // failed to update radio
+                            }
+                        }
+                    }
+
+                    // endId extends past all existing IntRanges: combine them all together
+                    if (tryAddSingleRange(startId, endId, true)) {
+                        range.startId = startId;
+                        range.endId = endId;
+                        // insert new ClientRange before existing ranges
+                        range.clients.add(0, new ClientRange(startId, endId, client));
+                        // coalesce range with following ranges up to len-1
+                        // remove each range after adding its elements, so the index
+                        // of the next range to join is always startIndex+1.
+                        // i is the index if no elements were removed: we only care
+                        // about the number of loop iterations, not the value of i.
+                        int joinIndex = startIndex + 1;
+                        for (int i = joinIndex; i < len; i++) {
+                            IntRange joinRange = mRanges.get(joinIndex);
+                            range.clients.addAll(joinRange.clients);
+                            mRanges.remove(joinRange);
+                        }
+                        return true;
+                    } else {
+                        return false;   // failed to update radio
+                    }
+                }
+            } else if ((startId + 1) <= range.endId) {
+                if (endId <= range.endId) {
+                    // completely contained in existing range; no radio changes
+                    range.insert(new ClientRange(startId, endId, client));
+                    return true;
+                } else {
+                    // find last range that can coalesce into the new combined range
+                    int endIndex = startIndex;
+                    for (int testIndex = startIndex+1; testIndex < len; testIndex++) {
+                        IntRange testRange = mRanges.get(testIndex);
+                        if ((endId + 1) < testRange.startId) {
+                            break;
+                        } else {
+                            endIndex = testIndex;
+                        }
+                    }
+                    // no adjacent IntRanges to combine
+                    if (endIndex == startIndex) {
+                        // add range from range.endId+1 to endId,
+                        // values from startId to range.endId are already enabled
+                        if (tryAddSingleRange(range.endId + 1, endId, true)) {
+                            range.endId = endId;
+                            range.insert(new ClientRange(startId, endId, client));
+                            return true;
+                        } else {
+                            return false;   // failed to update radio
+                        }
+                    }
+                    // get last range to coalesce into start range
+                    IntRange endRange = mRanges.get(endIndex);
+                    // Values from startId to range.endId have already been enabled.
+                    // if endId > endRange.endId, then enable range from range.endId+1 to endId,
+                    // else enable range from range.endId+1 to endRange.startId-1, because
+                    // values from endRange.startId to endId have already been added.
+                    int newRangeEndId = (endId <= endRange.endId) ? endRange.startId - 1 : endId;
+                    if (tryAddSingleRange(range.endId + 1, newRangeEndId, true)) {
+                        range.endId = endId;
+                        // insert new ClientRange in place
+                        range.insert(new ClientRange(startId, endId, client));
+                        // coalesce range with following ranges up to endIndex-1
+                        // remove each range after adding its elements, so the index
+                        // of the next range to join is always startIndex+1 (joinIndex).
+                        // i is the index if no elements had been removed: we only care
+                        // about the number of loop iterations, not the value of i.
+                        int joinIndex = startIndex + 1;
+                        for (int i = joinIndex; i < endIndex; i++) {
+                            IntRange joinRange = mRanges.get(joinIndex);
+                            range.clients.addAll(joinRange.clients);
+                            mRanges.remove(joinRange);
+                        }
+                        return true;
+                    } else {
+                        return false;   // failed to update radio
+                    }
+                }
+            }
+        }
+
+        // append new range after existing IntRanges
+        if (tryAddSingleRange(startId, endId, true)) {
+            mRanges.add(new IntRange(startId, endId, client));
+            return true;
+        } else {
+            return false;   // failed to update radio
+        }
+    }
+
+    /**
+     * Disable a range for the specified client and update ranges
+     * if necessary. If {@link #finishUpdate} returns failure,
+     * false is returned and the range is not removed.
+     *
+     * @param startId the first id included in the range
+     * @param endId the last id included in the range
+     * @param client the client requesting to disable the range
+     * @return true if successful, false otherwise
+     */
+    public synchronized boolean disableRange(int startId, int endId, String client) {
+        int len = mRanges.size();
+
+        for (int i=0; i < len; i++) {
+            IntRange range = mRanges.get(i);
+            if (startId < range.startId) {
+                return false;   // not found
+            } else if (endId <= range.endId) {
+                // found the IntRange that encloses the client range, if any
+                // search for it in the clients list
+                ArrayList<ClientRange> clients = range.clients;
+
+                // handle common case of IntRange containing one ClientRange
+                int crLength = clients.size();
+                if (crLength == 1) {
+                    ClientRange cr = clients.get(0);
+                    if (cr.startId == startId && cr.endId == endId && cr.client.equals(client)) {
+                        // disable range in radio then remove the entire IntRange
+                        if (tryAddSingleRange(startId, endId, false)) {
+                            mRanges.remove(i);
+                            return true;
+                        } else {
+                            return false;   // failed to update radio
+                        }
+                    } else {
+                        return false;   // not found
+                    }
+                }
+
+                // several ClientRanges: remove one, potentially splitting into many IntRanges.
+                // Save the original start and end id for the original IntRange
+                // in case the radio update fails and we have to revert it. If the
+                // update succeeds, we remove the client range and insert the new IntRanges.
+                int largestEndId = Integer.MIN_VALUE;  // largest end identifier found
+                boolean updateStarted = false;
+
+                for (int crIndex=0; crIndex < crLength; crIndex++) {
+                    ClientRange cr = clients.get(crIndex);
+                    if (cr.startId == startId && cr.endId == endId && cr.client.equals(client)) {
+                        // found the ClientRange to remove, check if it's the last in the list
+                        if (crIndex == crLength - 1) {
+                            if (range.endId == largestEndId) {
+                                // no channels to remove from radio; return success
+                                clients.remove(crIndex);
+                                return true;
+                            } else {
+                                // disable the channels at the end and lower the end id
+                                if (tryAddSingleRange(largestEndId + 1, range.endId, false)) {
+                                    clients.remove(crIndex);
+                                    range.endId = largestEndId;
+                                    return true;
+                                } else {
+                                    return false;
+                                }
+                            }
+                        }
+
+                        // copy the IntRange so that we can remove elements and modify the
+                        // start and end id's in the copy, leaving the original unmodified
+                        // until after the radio update succeeds
+                        IntRange rangeCopy = new IntRange(range, crIndex);
+
+                        if (crIndex == 0) {
+                            // removing the first ClientRange, so we may need to increase
+                            // the start id of the IntRange.
+                            // We know there are at least two ClientRanges in the list,
+                            // so clients.get(1) should always succeed.
+                            int nextStartId = clients.get(1).startId;
+                            if (nextStartId != range.startId) {
+                                startUpdate();
+                                updateStarted = true;
+                                addRange(range.startId, nextStartId - 1, false);
+                                rangeCopy.startId = nextStartId;
+                            }
+                            // init largestEndId
+                            largestEndId = clients.get(1).endId;
+                        }
+
+                        // go through remaining ClientRanges, creating new IntRanges when
+                        // there is a gap in the sequence. After radio update succeeds,
+                        // remove the original IntRange and append newRanges to mRanges.
+                        // Otherwise, leave the original IntRange in mRanges and return false.
+                        ArrayList<IntRange> newRanges = new ArrayList<IntRange>();
+
+                        IntRange currentRange = rangeCopy;
+                        for (int nextIndex = crIndex + 1; nextIndex < crLength; nextIndex++) {
+                            ClientRange nextCr = clients.get(nextIndex);
+                            if (nextCr.startId > largestEndId + 1) {
+                                if (!updateStarted) {
+                                    startUpdate();
+                                    updateStarted = true;
+                                }
+                                addRange(largestEndId + 1, nextCr.startId - 1, false);
+                                currentRange.endId = largestEndId;
+                                newRanges.add(currentRange);
+                                currentRange = new IntRange(nextCr);
+                            } else {
+                                currentRange.clients.add(nextCr);
+                            }
+                            if (nextCr.endId > largestEndId) {
+                                largestEndId = nextCr.endId;
+                            }
+                        }
+
+                        // remove any channels between largestEndId and endId
+                        if (largestEndId < endId) {
+                            if (!updateStarted) {
+                                startUpdate();
+                                updateStarted = true;
+                            }
+                            addRange(largestEndId + 1, endId, false);
+                            currentRange.endId = largestEndId;
+                        }
+                        newRanges.add(currentRange);
+
+                        if (updateStarted && !finishUpdate()) {
+                            return false;   // failed to update radio
+                        }
+
+                        // replace the original IntRange with newRanges
+                        mRanges.remove(i);
+                        mRanges.addAll(i, newRanges);
+                        return true;
+                    } else {
+                        // not the ClientRange to remove; save highest end ID seen so far
+                        if (cr.endId > largestEndId) {
+                            largestEndId = cr.endId;
+                        }
+                    }
+                }
+            }
+        }
+
+        return false;   // not found
+    }
+
+    /**
+     * Perform a complete update operation (enable all ranges). Useful
+     * after a radio reset. Calls {@link #startUpdate}, followed by zero or
+     * more calls to {@link #addRange}, followed by {@link #finishUpdate}.
+     * @return true if successful, false otherwise
+     */
+    public boolean updateRanges() {
+        startUpdate();
+        Iterator<IntRange> iterator = mRanges.iterator();
+        if (iterator.hasNext()) {
+            IntRange range = iterator.next();
+            int start = range.startId;
+            int end = range.endId;
+            // accumulate ranges of [startId, endId]
+            while (iterator.hasNext()) {
+                IntRange nextNode = iterator.next();
+                // [startIdA, endIdA], [endIdA + 1, endIdB] -> [startIdA, endIdB]
+                if (nextNode.startId <= (end + 1)) {
+                    if (nextNode.endId > end) {
+                        end = nextNode.endId;
+                    }
+                } else {
+                    addRange(start, end, true);
+                    start = nextNode.startId;
+                    end = nextNode.endId;
+                }
+            }
+            // add final range
+            addRange(start, end, true);
+        }
+        return finishUpdate();
+    }
+
+    /**
+     * Enable or disable a single range of message identifiers.
+     * @param startId the first id included in the range
+     * @param endId the last id included in the range
+     * @param selected true to enable range, false to disable range
+     * @return true if successful, false otherwise
+     */
+    private boolean tryAddSingleRange(int startId, int endId, boolean selected) {
+        startUpdate();
+        addRange(startId, endId, selected);
+        return finishUpdate();
+    }
+
+    /**
+     * Called when the list of enabled ranges has changed. This will be
+     * followed by zero or more calls to {@link #addRange} followed by
+     * a call to {@link #finishUpdate}.
+     */
+    protected abstract void startUpdate();
+
+    /**
+     * Called after {@link #startUpdate} to indicate a range of enabled
+     * or disabled values.
+     *
+     * @param startId the first id included in the range
+     * @param endId the last id included in the range
+     * @param selected true to enable range, false to disable range
+     */
+    protected abstract void addRange(int startId, int endId, boolean selected);
+
+    /**
+     * Called to indicate the end of a range update started by the
+     * previous call to {@link #startUpdate}.
+     * @return true if successful, false otherwise
+     */
+    protected abstract boolean finishUpdate();
+}
diff --git a/telephony/java/com/android/internal/telephony/PhoneFactory.java b/telephony/java/com/android/internal/telephony/PhoneFactory.java
index 6a2d7c9..74bae44 100644
--- a/telephony/java/com/android/internal/telephony/PhoneFactory.java
+++ b/telephony/java/com/android/internal/telephony/PhoneFactory.java
@@ -47,8 +47,6 @@
     static private Looper sLooper;
     static private Context sContext;
 
-    static final int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
-
     static final int preferredCdmaSubscription = RILConstants.PREFERRED_CDMA_SUBSCRIPTION;
 
     //***** Class Methods
@@ -99,7 +97,11 @@
 
                 sPhoneNotifier = new DefaultPhoneNotifier();
 
-                //Get preferredNetworkMode from Settings.System
+                // Get preferred network mode
+                int preferredNetworkMode = RILConstants.PREFERRED_NETWORK_MODE;
+                if (BaseCommands.getLteOnCdmaModeStatic() == Phone.LTE_ON_CDMA_TRUE) {
+                    preferredNetworkMode = Phone.NT_MODE_GLOBAL;
+                }
                 int networkMode = Settings.Secure.getInt(context.getContentResolver(),
                         Settings.Secure.PREFERRED_NETWORK_MODE, preferredNetworkMode);
                 Log.i(LOG_TAG, "Network Mode set to " + Integer.toString(networkMode));
diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java
index 2c04b30..572bbaa 100644
--- a/telephony/java/com/android/internal/telephony/RIL.java
+++ b/telephony/java/com/android/internal/telephony/RIL.java
@@ -611,14 +611,14 @@
 
     //***** Constructors
 
-    public RIL(Context context, int networkMode, int cdmaSubscription) {
+    public RIL(Context context, int preferredNetworkType, int cdmaSubscription) {
         super(context);
         if (RILJ_LOGD) {
-            riljLog("RIL(context, networkMode=" + networkMode +
+            riljLog("RIL(context, preferredNetworkType=" + preferredNetworkType +
                     " cdmaSubscription=" + cdmaSubscription + ")");
         }
         mCdmaSubscription  = cdmaSubscription;
-        mNetworkMode = networkMode;
+        mPreferredNetworkType = preferredNetworkType;
         mPhoneType = RILConstants.NO_PHONE;
 
         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -1813,6 +1813,8 @@
         rr.mp.writeInt(1);
         rr.mp.writeInt(networkType);
 
+        mPreferredNetworkType = networkType;
+
         if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest)
                 + " : " + networkType);
 
@@ -2222,7 +2224,7 @@
             case RIL_REQUEST_STK_HANDLE_CALL_SETUP_REQUESTED_FROM_SIM: ret =  responseInts(p); break;
             case RIL_REQUEST_EXPLICIT_CALL_TRANSFER: ret =  responseVoid(p); break;
             case RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE: ret =  responseVoid(p); break;
-            case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret =  responseInts(p); break;
+            case RIL_REQUEST_GET_PREFERRED_NETWORK_TYPE: ret =  responseGetPreferredNetworkType(p); break;
             case RIL_REQUEST_GET_NEIGHBORING_CELL_IDS: ret = responseCellList(p); break;
             case RIL_REQUEST_SET_LOCATION_UPDATES: ret =  responseVoid(p); break;
             case RIL_REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE: ret =  responseVoid(p); break;
@@ -2737,7 +2739,7 @@
 
                 // Initial conditions
                 setRadioPower(false, null);
-                setPreferredNetworkType(mNetworkMode, null);
+                setPreferredNetworkType(mPreferredNetworkType, null);
                 setCdmaSubscriptionSource(mCdmaSubscription, null);
                 notifyRegistrantsRilConnectionChanged(((int[])ret)[0]);
                 break;
@@ -3161,6 +3163,18 @@
        return response;
     }
 
+    private Object responseGetPreferredNetworkType(Parcel p) {
+       int [] response = (int[]) responseInts(p);
+
+       if (response.length >= 1) {
+           // Since this is the response for getPreferredNetworkType
+           // we'll assume that it should be the value we want the
+           // vendor ril to take if we reestablish a connection to it.
+           mPreferredNetworkType = response[0];
+       }
+       return response;
+    }
+
     private Object responseGmsBroadcastConfig(Parcel p) {
         int num;
         ArrayList<SmsBroadcastConfigInfo> response;
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
old mode 100755
new mode 100644
index 8efc9aa..76e719c
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -32,11 +32,9 @@
 import android.database.SQLException;
 import android.net.Uri;
 import android.os.AsyncResult;
-import android.os.Environment;
 import android.os.Handler;
 import android.os.Message;
 import android.os.PowerManager;
-import android.os.StatFs;
 import android.os.SystemProperties;
 import android.provider.Telephony;
 import android.provider.Telephony.Sms.Intents;
@@ -908,37 +906,6 @@
     protected abstract void sendMultipartSms (SmsTracker tracker);
 
     /**
-     * Activate or deactivate cell broadcast SMS.
-     *
-     * @param activate
-     *            0 = activate, 1 = deactivate
-     * @param response
-     *            Callback message is empty on completion
-     */
-    public abstract void activateCellBroadcastSms(int activate, Message response);
-
-    /**
-     * Query the current configuration of cell broadcast SMS.
-     *
-     * @param response
-     *            Callback message contains the configuration from the modem on completion
-     *            @see #setCellBroadcastConfig
-     */
-    public abstract void getCellBroadcastSmsConfig(Message response);
-
-    /**
-     * Configure cell broadcast SMS.
-     *
-     * @param configValuesArray
-     *          The first element defines the number of triples that follow.
-     *          A triple is made up of the service category, the language identifier
-     *          and a boolean that specifies whether the category is set active.
-     * @param response
-     *            Callback message is empty on completion
-     */
-    public abstract void setCellBroadcastConfig(int[] configValuesArray, Message response);
-
-    /**
      * Send an acknowledge message.
      * @param success indicates that last message was successfully received.
      * @param result result code indicating any error
@@ -1065,14 +1032,17 @@
 
     protected abstract void handleBroadcastSms(AsyncResult ar);
 
-    protected void dispatchBroadcastPdus(byte[][] pdus) {
-        Intent intent = new Intent("android.provider.telephony.SMS_CB_RECEIVED");
-        intent.putExtra("pdus", pdus);
-
-        if (false)
+    protected void dispatchBroadcastPdus(byte[][] pdus, boolean isEmergencyMessage) {
+        if (isEmergencyMessage) {
+            Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
+            intent.putExtra("pdus", pdus);
+            Log.d(TAG, "Dispatching " + pdus.length + " emergency SMS CB pdus");
+            dispatch(intent, "android.permission.RECEIVE_EMERGENCY_BROADCAST");
+        } else {
+            Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
+            intent.putExtra("pdus", pdus);
             Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");
-
-        dispatch(intent, "android.permission.RECEIVE_SMS");
+            dispatch(intent, "android.permission.RECEIVE_SMS");
+        }
     }
-
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
index fe2fcb2..518a34a 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMALTEPhone.java
@@ -65,33 +65,13 @@
 
     @Override
     public DataState getDataConnectionState(String apnType) {
-        // TODO: Remove instanceof if possible.
-        boolean isCdmaDataConnectionTracker = false;
-        if (mDataConnectionTracker instanceof CdmaDataConnectionTracker) {
-            log("getDataConnectionState isCdmaDataConnectionTracker");
-            isCdmaDataConnectionTracker = true;
-        } else {
-            log("getDataConnectionState NOT CdmaDataConnectionTracker");
-        }
         DataState ret = DataState.DISCONNECTED;
 
-        if (!isCdmaDataConnectionTracker && (SystemProperties.get("adb.connected", "").length()
-                > 0)) {
-            // We're connected to an ADB host and we have USB networking
-            // turned on. No matter what the radio state is,
-            // we report data connected
-
-            ret = DataState.CONNECTED;
-        } else if (mSST == null) {
+        if (mSST == null) {
             // Radio Technology Change is ongoning, dispose() and
-            // removeReferences() have
-            // already been called
+            // removeReferences() have already been called
 
             ret = DataState.DISCONNECTED;
-        } else if (mSST.getCurrentDataConnectionState() != ServiceState.STATE_IN_SERVICE) {
-            // If we're out of service, open TCP sockets may still work
-            // but no data will flow
-            ret = DataState.DISCONNECTED;
         } else if (mDataConnectionTracker.isApnTypeEnabled(apnType) == false) {
             ret = DataState.DISCONNECTED;
         } else {
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index a283062..bac15a6 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -1121,7 +1121,8 @@
      * @param response Callback message is empty on completion
      */
     public void activateCellBroadcastSms(int activate, Message response) {
-        mSMS.activateCellBroadcastSms(activate, response);
+        Log.e(LOG_TAG, "[CDMAPhone] activateCellBroadcastSms() is obsolete; use SmsManager");
+        response.sendToTarget();
     }
 
     /**
@@ -1130,7 +1131,8 @@
      * @param response Callback message is empty on completion
      */
     public void getCellBroadcastSmsConfig(Message response) {
-        mSMS.getCellBroadcastSmsConfig(response);
+        Log.e(LOG_TAG, "[CDMAPhone] getCellBroadcastSmsConfig() is obsolete; use SmsManager");
+        response.sendToTarget();
     }
 
     /**
@@ -1139,7 +1141,8 @@
      * @param response Callback message is empty on completion
      */
     public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
-        mSMS.setCellBroadcastConfig(configValuesArray, response);
+        Log.e(LOG_TAG, "[CDMAPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager");
+        response.sendToTarget();
     }
 
     /**
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
index 7bc7ca2..318cf37 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java
@@ -16,7 +16,6 @@
 
 package com.android.internal.telephony.cdma;
 
-import com.android.internal.telephony.DataConnectionTracker;
 import com.android.internal.telephony.TelephonyProperties;
 import com.android.internal.telephony.MccTable;
 import com.android.internal.telephony.EventLogTags;
@@ -26,23 +25,15 @@
 import android.telephony.ServiceState;
 import android.telephony.cdma.CdmaCellLocation;
 import android.os.AsyncResult;
-import android.os.Handler;
 import android.os.Message;
-import android.os.Registrant;
-import android.os.RegistrantList;
-import android.os.AsyncResult;
-import android.os.Message;
-import android.os.SystemProperties;
+
 
 import android.util.Log;
 import android.util.EventLog;
 
-import com.android.internal.telephony.RestrictedState;
 import com.android.internal.telephony.gsm.GsmDataConnectionTracker;
 
 public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker {
-    static final String LOG_TAG = "CDMA";
-
     CDMALTEPhone mCdmaLtePhone;
 
     private ServiceState  mLteSS;  // The last LTE state from Voice Registration
@@ -56,6 +47,7 @@
         if (DBG) log("CdmaLteServiceStateTracker Constructors");
     }
 
+    @Override
     public void dispose() {
         cm.unregisterForSIMReady(this);
         super.dispose();
@@ -75,11 +67,24 @@
         case EVENT_SIM_READY:
             if (DBG) log("handleMessage EVENT_SIM_READY");
             isSubscriptionFromRuim = false;
-            cm.getCDMASubscription( obtainMessage(EVENT_POLL_STATE_CDMA_SUBSCRIPTION));
+            // Register SIM_RECORDS_LOADED dynamically.
+            // This is to avoid confilct with RUIM_READY scenario)
+            phone.mIccRecords.registerForRecordsLoaded(this, EVENT_SIM_RECORDS_LOADED, null);
             pollState();
             // Signal strength polling stops when radio is off.
             queueNextSignalStrengthPoll();
             break;
+        case EVENT_SIM_RECORDS_LOADED:
+            CdmaLteUiccRecords sim = (CdmaLteUiccRecords)phone.mIccRecords;
+            if ((sim != null) && sim.isProvisioned()) {
+                mMdn = sim.getMdn();
+                mMin = sim.getMin();
+                parseSidNid(sim.getSid(), sim.getNid());
+                mPrlVersion = sim.getPrlVersion();;
+                mIsMinInfoReady = true;
+                updateOtaspState();
+            }
+            break;
         default:
             super.handleMessage(msg);
         }
@@ -398,6 +403,7 @@
         }
     }
 
+    @Override
     protected void onSignalStrengthResult(AsyncResult ar) {
         SignalStrength oldSignalStrength = mSignalStrength;
 
@@ -439,6 +445,7 @@
         }
     }
 
+    @Override
     public boolean isConcurrentVoiceAndDataAllowed() {
         // Note: it needs to be confirmed which CDMA network types
         // can support voice and data calls concurrently.
@@ -447,6 +454,16 @@
         return false;
     }
 
+    /**
+     * Returns OTASP_NOT_NEEDED as its not needed for LTE
+     */
+    @Override
+    int getOtasp() {
+        int provisioningState = OTASP_NOT_NEEDED;
+        if (DBG) log("getOtasp: state=" + provisioningState);
+        return provisioningState;
+    }
+
     @Override
     protected void log(String s) {
         Log.d(LOG_TAG, "[CdmaLteSST] " + s);
@@ -456,8 +473,4 @@
     protected void loge(String s) {
         Log.e(LOG_TAG, "[CdmaLteSST] " + s);
     }
-
-    protected static void sloge(String s) {
-        Log.e(LOG_TAG, "[CdmaLteSST] " + s);
-    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
index 2aede29..b9d7c46 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccFileHandler.java
@@ -19,6 +19,7 @@
 import android.util.Log;
 import com.android.internal.telephony.IccConstants;
 import com.android.internal.telephony.IccFileHandler;
+import android.os.Message;
 
 /**
  * {@hide}
@@ -34,6 +35,10 @@
         switch(efid) {
         case EF_CSIM_SPN:
         case EF_CSIM_LI:
+        case EF_CSIM_MDN:
+        case EF_CSIM_IMSIM:
+        case EF_CSIM_CDMAHOME:
+        case EF_CSIM_EPRL:
             return MF_SIM + DF_CDMA;
         case EF_AD:
             return MF_SIM + DF_GSM;
@@ -41,6 +46,21 @@
         return getCommonIccEFPath(efid);
     }
 
+    @Override
+    public void loadEFTransparent(int fileid, Message onLoaded) {
+        if (fileid == EF_CSIM_EPRL) {
+            // Entire PRL could be huge. We are only interested in
+            // the first 4 bytes of the record.
+            phone.mCM.iccIO(COMMAND_READ_BINARY, fileid, getEFPath(fileid),
+                            0, 0, 4, null, null,
+                            obtainMessage(EVENT_READ_BINARY_DONE,
+                                          fileid, 0, onLoaded));
+        } else {
+            super.loadEFTransparent(fileid, onLoaded);
+        }
+    }
+
+
     protected void logd(String msg) {
         Log.d(LOG_TAG, "[CdmaLteUiccFileHandler] " + msg);
     }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
index 78879d6..ac77f9a 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java
@@ -26,7 +26,8 @@
 import android.os.Message;
 import android.os.SystemProperties;
 import android.util.Log;
-
+import java.util.Locale;
+import java.util.ArrayList;
 
 /**
  * {@hide}
@@ -36,10 +37,19 @@
     private byte[] mEFpl = null;
     private byte[] mEFli = null;
     boolean csimSpnDisplayCondition = false;
+    private String mMdn;
+    private String mMin;
+    private String mPrlVersion;
+    private String mHomeSystemId;
+    private String mHomeNetworkId;
 
     private static final int EVENT_GET_PL_DONE = CSIM_EVENT_BASE;
     private static final int EVENT_GET_CSIM_LI_DONE = CSIM_EVENT_BASE + 1;
     private static final int EVENT_GET_CSIM_SPN_DONE = CSIM_EVENT_BASE + 2;
+    private static final int EVENT_GET_CSIM_MDN_DONE = CSIM_EVENT_BASE + 3;
+    private static final int EVENT_GET_CSIM_IMSIM_DONE = CSIM_EVENT_BASE + 4;
+    private static final int EVENT_GET_CSIM_CDMAHOME_DONE = CSIM_EVENT_BASE + 5;
+    private static final int EVENT_GET_CSIM_EPRL_DONE = CSIM_EVENT_BASE + 6;
 
     public CdmaLteUiccRecords(PhoneBase p) {
         super(p);
@@ -109,6 +119,46 @@
                 }
                 onGetCSimSpnDone(ar);
                 break;
+            case EVENT_GET_CSIM_MDN_DONE:
+                if (DBG) log("EVENT_GET_CSIM_MDN_DONE");
+                isCsimRecordLoadResponse = true;
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception=" + ar.exception);
+                    break;
+                }
+                onGetCSimMdnDone(ar);
+                break;
+            case EVENT_GET_CSIM_IMSIM_DONE:
+                if (DBG) log("EVENT_GET_CSIM_IMSIM_DONE");
+                isCsimRecordLoadResponse = true;
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception=" + ar.exception);
+                    break;
+                }
+                onGetCSimImsimDone(ar);
+                break;
+            case EVENT_GET_CSIM_CDMAHOME_DONE:
+                if (DBG) log("EVENT_GET_CSIM_CDMAHOME_DONE");
+                isCsimRecordLoadResponse = true;
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception=" + ar.exception);
+                    break;
+                }
+                onGetCSimCdmaHomeDone(ar);
+                break;
+            case EVENT_GET_CSIM_EPRL_DONE:
+                if (DBG) log("EVENT_GET_CSIM_EPRL_DONE");
+                isCsimRecordLoadResponse = true;
+                ar = (AsyncResult) msg.obj;
+                if (ar.exception != null) {
+                    Log.e(LOG_TAG, "ar.exception=" + ar.exception);
+                    break;
+                }
+                onGetCSimEprlDone(ar);
+                break;
             default:
                 super.handleMessage(msg);
         }}catch (RuntimeException exc) {
@@ -156,6 +206,19 @@
 
         iccFh.loadEFTransparent(EF_CSIM_SPN, obtainMessage(EVENT_GET_CSIM_SPN_DONE));
         recordsToLoad++;
+
+        iccFh.loadEFLinearFixed(EF_CSIM_MDN, 1, obtainMessage(EVENT_GET_CSIM_MDN_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFTransparent(EF_CSIM_IMSIM, obtainMessage(EVENT_GET_CSIM_IMSIM_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFLinearFixedAll(EF_CSIM_CDMAHOME,
+                                   obtainMessage(EVENT_GET_CSIM_CDMAHOME_DONE));
+        recordsToLoad++;
+
+        iccFh.loadEFTransparent(EF_CSIM_EPRL, obtainMessage(EVENT_GET_CSIM_EPRL_DONE));
+        recordsToLoad++;
     }
 
     private void onGetCSimSpnDone(AsyncResult ar) {
@@ -205,6 +268,93 @@
         phone.setSystemProperty(PROPERTY_ICC_OPERATOR_ALPHA, spn);
     }
 
+    private void onGetCSimMdnDone(AsyncResult ar) {
+        byte[] data = (byte[]) ar.result;
+        if (DBG) log("CSIM_MDN=" + IccUtils.bytesToHexString(data));
+        int mdnDigitsNum = 0x0F & data[0];
+        mMdn = IccUtils.cdmaBcdToString(data, 1, mdnDigitsNum);
+        if (DBG) log("CSIM MDN=" + mMdn);
+    }
+
+    private void onGetCSimImsimDone(AsyncResult ar) {
+        byte[] data = (byte[]) ar.result;
+        if (DBG) log("CSIM_IMSIM=" + IccUtils.bytesToHexString(data));
+        // C.S0065 section 5.2.2 for IMSI_M encoding
+        // C.S0005 section 2.3.1 for MIN encoding in IMSI_M.
+        boolean provisioned = ((data[7] & 0x80) == 0x80);
+
+        if (provisioned) {
+            int first3digits = ((0x03 & data[2]) << 8) + (0xFF & data[1]);
+            int second3digits = (((0xFF & data[5]) << 8) | (0xFF & data[4])) >> 6;
+            int digit7 = 0x0F & (data[4] >> 2);
+            if (digit7 > 0x09) digit7 = 0;
+            int last3digits = ((0x03 & data[4]) << 8) | (0xFF & data[3]);
+            first3digits = adjstMinDigits(first3digits);
+            second3digits = adjstMinDigits(second3digits);
+            last3digits = adjstMinDigits(last3digits);
+
+            StringBuilder builder = new StringBuilder();
+            builder.append(String.format(Locale.US, "%03d", first3digits));
+            builder.append(String.format(Locale.US, "%03d", second3digits));
+            builder.append(String.format(Locale.US, "%d", digit7));
+            builder.append(String.format(Locale.US, "%03d", last3digits));
+            if (DBG) log("min present=" + builder.toString());
+
+            mMin = builder.toString();
+        } else {
+            if (DBG) log("min not present");
+        }
+    }
+
+    private int adjstMinDigits (int digits) {
+        // Per C.S0005 section 2.3.1.
+        digits += 111;
+        digits = (digits % 10 == 0)?(digits - 10):digits;
+        digits = ((digits / 10) % 10 == 0)?(digits - 100):digits;
+        digits = ((digits / 100) % 10 == 0)?(digits - 1000):digits;
+        return digits;
+    }
+
+    private void onGetCSimCdmaHomeDone(AsyncResult ar) {
+        // Per C.S0065 section 5.2.8
+        ArrayList<byte[]> dataList = (ArrayList<byte[]>) ar.result;
+        if (DBG) log("CSIM_CDMAHOME data size=" + dataList.size());
+        if (dataList.isEmpty()) {
+            return;
+        }
+        StringBuilder sidBuf = new StringBuilder();
+        StringBuilder nidBuf = new StringBuilder();
+
+        for (byte[] data : dataList) {
+            if (data.length == 5) {
+                int sid = ((data[1] & 0xFF) << 8) | (data[0] & 0xFF);
+                int nid = ((data[3] & 0xFF) << 8) | (data[2] & 0xFF);
+                sidBuf.append(sid).append(",");
+                nidBuf.append(nid).append(",");
+            }
+        }
+        // remove trailing ","
+        sidBuf.setLength(sidBuf.length()-1);
+        nidBuf.setLength(nidBuf.length()-1);
+
+        mHomeSystemId = sidBuf.toString();
+        mHomeNetworkId = nidBuf.toString();
+    }
+
+    private void onGetCSimEprlDone(AsyncResult ar) {
+        // C.S0065 section 5.2.57 for EFeprl encoding
+        // C.S0016 section 3.5.5 for PRL format.
+        byte[] data = (byte[]) ar.result;
+        if (DBG) log("CSIM_EPRL=" + IccUtils.bytesToHexString(data));
+
+        // Only need the first 4 bytes of record
+        if (data.length > 3) {
+            int prlId = ((data[2] & 0xFF) << 8) | (data[3] & 0xFF);
+            mPrlVersion = Integer.toString(prlId);
+        }
+        if (DBG) log("CSIM PRL version=" + mPrlVersion);
+    }
+
     public byte[] getPreferredLanguage() {
         return mEFpl;
     }
@@ -212,4 +362,32 @@
     public byte[] getLanguageIndication() {
         return mEFli;
     }
+
+    public String getMdn() {
+        return mMdn;
+    }
+
+    public String getMin() {
+        return mMin;
+    }
+
+    public String getSid() {
+        return mHomeSystemId;
+    }
+
+    public String getNid() {
+        return mHomeNetworkId;
+    }
+
+    public String getPrlVersion() {
+        return mPrlVersion;
+    }
+
+    @Override
+    public boolean isProvisioned() {
+        // Look for MDN and MIN field to determine if the SIM is provisioned.
+        if ((mMdn != null) && (mMin != null)) return true;
+
+        return false;
+    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index cf6b78a..29349db 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -273,7 +273,7 @@
             if (cursorCount != totalSegments - 1) {
                 // We don't have all the parts yet, store this one away
                 ContentValues values = new ContentValues();
-                values.put("date", new Long(0));
+                values.put("date", (long) 0);
                 values.put("pdu", HexDump.toHexString(pdu, index, pdu.length - index));
                 values.put("address", address);
                 values.put("reference_number", referenceNumber);
@@ -482,24 +482,6 @@
         }
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public void activateCellBroadcastSms(int activate, Message response) {
-        mCm.setCdmaBroadcastActivation((activate == 0), response);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void getCellBroadcastSmsConfig(Message response) {
-        mCm.getCdmaBroadcastConfig(response);
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void setCellBroadcastConfig(int[] configValuesArray, Message response) {
-        mCm.setCdmaBroadcastConfig(configValuesArray, response);
-    }
-
     protected void handleBroadcastSms(AsyncResult ar) {
         // Not supported
         Log.e(TAG, "Error! Not implemented for CDMA.");
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
index ead6bca..5ebdd22 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java
@@ -129,12 +129,12 @@
     /** Contains the name of the registered network in CDMA (either ONS or ERI text). */
     private String curPlmn = null;
 
-    private String mMdn;
+    protected String mMdn;
     private int mHomeSystemId[] = null;
     private int mHomeNetworkId[] = null;
-    private String mMin;
-    private String mPrlVersion;
-    private boolean mIsMinInfoReady = false;
+    protected String mMin;
+    protected String mPrlVersion;
+    protected boolean mIsMinInfoReady = false;
 
     private boolean isEriTextLoaded = false;
     protected boolean isSubscriptionFromRuim = false;
@@ -373,53 +373,15 @@
                 String cdmaSubscription[] = (String[])ar.result;
                 if (cdmaSubscription != null && cdmaSubscription.length >= 5) {
                     mMdn = cdmaSubscription[0];
-                    if (cdmaSubscription[1] != null) {
-                        String[] sid = cdmaSubscription[1].split(",");
-                        mHomeSystemId = new int[sid.length];
-                        for (int i = 0; i < sid.length; i++) {
-                            try {
-                                mHomeSystemId[i] = Integer.parseInt(sid[i]);
-                            } catch (NumberFormatException ex) {
-                                loge("error parsing system id: " + ex);
-                            }
-                        }
-                    }
-                    if (DBG) log("GET_CDMA_SUBSCRIPTION: SID=" + cdmaSubscription[1] );
+                    parseSidNid(cdmaSubscription[1], cdmaSubscription[2]);
 
-                    if (cdmaSubscription[2] != null) {
-                        String[] nid = cdmaSubscription[2].split(",");
-                        mHomeNetworkId = new int[nid.length];
-                        for (int i = 0; i < nid.length; i++) {
-                            try {
-                                mHomeNetworkId[i] = Integer.parseInt(nid[i]);
-                            } catch (NumberFormatException ex) {
-                                loge("GET_CDMA_SUBSCRIPTION: error parsing network id: " + ex);
-                            }
-                        }
-                    }
-                    if (DBG) log("GET_CDMA_SUBSCRIPTION: NID=" + cdmaSubscription[2]);
                     mMin = cdmaSubscription[3];
                     mPrlVersion = cdmaSubscription[4];
                     if (DBG) log("GET_CDMA_SUBSCRIPTION: MDN=" + mMdn);
 
                     mIsMinInfoReady = true;
 
-                    int otaspMode = getOtasp();
-                    int oldOtaspMode = mCurrentOtaspMode;
-                    mCurrentOtaspMode = otaspMode;
-
-                    // Notify apps subscription info is ready
-                    if (cdmaForSubscriptionInfoReadyRegistrants != null) {
-                        if (DBG) log("GET_CDMA_SUBSCRIPTION: call notifyRegistrants()");
-                        cdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants();
-                    }
-                    if (oldOtaspMode != mCurrentOtaspMode) {
-                        if (DBG) {
-                            log("GET_CDMA_SUBSCRIPTION: call notifyOtaspChanged old otaspMode=" +
-                                oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode);
-                        }
-                        phone.notifyOtaspChanged(mCurrentOtaspMode);
-                    }
+                    updateOtaspState();
                     phone.getIccCard().broadcastIccStateChangedIntent(IccCard.INTENT_VALUE_ICC_IMSI,
                             null);
                 } else {
@@ -1622,6 +1584,53 @@
         cm.setRadioPower(false, null);
     }
 
+    protected void parseSidNid (String sidStr, String nidStr) {
+        if (sidStr != null) {
+            String[] sid = sidStr.split(",");
+            mHomeSystemId = new int[sid.length];
+            for (int i = 0; i < sid.length; i++) {
+                try {
+                    mHomeSystemId[i] = Integer.parseInt(sid[i]);
+                } catch (NumberFormatException ex) {
+                    loge("error parsing system id: " + ex);
+                }
+            }
+        }
+        if (DBG) log("CDMA_SUBSCRIPTION: SID=" + sidStr);
+
+        if (nidStr != null) {
+            String[] nid = nidStr.split(",");
+            mHomeNetworkId = new int[nid.length];
+            for (int i = 0; i < nid.length; i++) {
+                try {
+                    mHomeNetworkId[i] = Integer.parseInt(nid[i]);
+                } catch (NumberFormatException ex) {
+                    loge("CDMA_SUBSCRIPTION: error parsing network id: " + ex);
+                }
+            }
+        }
+        if (DBG) log("CDMA_SUBSCRIPTION: NID=" + nidStr);
+    }
+
+    protected void updateOtaspState() {
+        int otaspMode = getOtasp();
+        int oldOtaspMode = mCurrentOtaspMode;
+        mCurrentOtaspMode = otaspMode;
+
+        // Notify apps subscription info is ready
+        if (cdmaForSubscriptionInfoReadyRegistrants != null) {
+            if (DBG) log("CDMA_SUBSCRIPTION: call notifyRegistrants()");
+            cdmaForSubscriptionInfoReadyRegistrants.notifyRegistrants();
+        }
+        if (oldOtaspMode != mCurrentOtaspMode) {
+            if (DBG) {
+                log("CDMA_SUBSCRIPTION: call notifyOtaspChanged old otaspMode=" +
+                    oldOtaspMode + " new otaspMode=" + mCurrentOtaspMode);
+            }
+            phone.notifyOtaspChanged(mCurrentOtaspMode);
+        }
+    }
+
     @Override
     protected void log(String s) {
         Log.d(LOG_TAG, "[CdmaSST] " + s);
@@ -1631,8 +1640,4 @@
     protected void loge(String s) {
         Log.e(LOG_TAG, "[CdmaSST] " + s);
     }
-
-    private static void slog(String s) {
-        Log.d(LOG_TAG, "[CdmaSST] " + s);
-    }
 }
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index 29f3bc1..9cd059d 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -203,6 +203,18 @@
         return false;
     }
 
+    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
+        // Not implemented
+        Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+        return false;
+    }
+
+    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
+        // Not implemented
+        Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+        return false;
+    }
+
     protected void log(String msg) {
         Log.d(LOG_TAG, "[RuimSmsInterfaceManager] " + msg);
     }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 3ccc03d..d357eac 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -301,6 +301,9 @@
             ret = DataState.DISCONNECTED;
         } else if (mDataConnectionTracker.isApnTypeEnabled(apnType) == false ||
                 mDataConnectionTracker.isApnTypeActive(apnType) == false) {
+            //TODO: isApnTypeActive() is just checking whether ApnContext holds
+            //      Dataconnection or not. Checking each ApnState below should
+            //      provide the same state. Calling isApnTypeActive() can be removed.
             ret = DataState.DISCONNECTED;
         } else { /* mSST.gprsState == ServiceState.STATE_IN_SERVICE */
             switch (mDataConnectionTracker.getState(apnType)) {
@@ -1427,16 +1430,35 @@
         return this.mIccFileHandler;
     }
 
+    /**
+     * Activate or deactivate cell broadcast SMS.
+     *
+     * @param activate 0 = activate, 1 = deactivate
+     * @param response Callback message is empty on completion
+     */
     public void activateCellBroadcastSms(int activate, Message response) {
-        Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM.");
+        Log.e(LOG_TAG, "[GSMPhone] activateCellBroadcastSms() is obsolete; use SmsManager");
+        response.sendToTarget();
     }
 
+    /**
+     * Query the current configuration of cdma cell broadcast SMS.
+     *
+     * @param response Callback message is empty on completion
+     */
     public void getCellBroadcastSmsConfig(Message response) {
-        Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM.");
+        Log.e(LOG_TAG, "[GSMPhone] getCellBroadcastSmsConfig() is obsolete; use SmsManager");
+        response.sendToTarget();
     }
 
-    public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){
-        Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM.");
+    /**
+     * Configure cdma cell broadcast SMS.
+     *
+     * @param response Callback message is empty on completion
+     */
+    public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
+        Log.e(LOG_TAG, "[GSMPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager");
+        response.sendToTarget();
     }
 
     public boolean isCspPlmnEnabled() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index db2b490..dcde71a 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -561,6 +561,7 @@
         boolean allowed =
                     (gprsState == ServiceState.STATE_IN_SERVICE || mAutoAttachOnCreation) &&
                     mPhone.mIccRecords.getRecordsLoaded() &&
+                    mPhone.mIccRecords.isProvisioned() &&
                     mPhone.getState() == Phone.State.IDLE &&
                     mInternalDataEnabled &&
                     (!mPhone.getServiceState().getRoaming() || getDataOnRoamingEnabled()) &&
@@ -572,6 +573,7 @@
                 reason += " - gprs= " + gprsState;
             }
             if (!mPhone.mIccRecords.getRecordsLoaded()) reason += " - SIM not loaded";
+            if (!mPhone.mIccRecords.isProvisioned()) reason += " - SIM not provisioned";
             if (mPhone.getState() != Phone.State.IDLE) {
                 reason += " - PhoneState= " + mPhone.getState();
             }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
old mode 100755
new mode 100644
index b021967..52ca453
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -391,30 +391,6 @@
         }
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public void activateCellBroadcastSms(int activate, Message response) {
-        // Unless CBS is implemented for GSM, this point should be unreachable.
-        Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
-        response.recycle();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public void getCellBroadcastSmsConfig(Message response){
-        // Unless CBS is implemented for GSM, this point should be unreachable.
-        Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
-        response.recycle();
-    }
-
-    /** {@inheritDoc} */
-    @Override
-    public  void setCellBroadcastConfig(int[] configValuesArray, Message response) {
-        // Unless CBS is implemented for GSM, this point should be unreachable.
-        Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
-        response.recycle();
-    }
-
     private int resultToCause(int rc) {
         switch (rc) {
             case Activity.RESULT_OK:
@@ -506,9 +482,10 @@
     }
 
     // This map holds incomplete concatenated messages waiting for assembly
-    private HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
+    private final HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
             new HashMap<SmsCbConcatInfo, byte[][]>();
 
+    @Override
     protected void handleBroadcastSms(AsyncResult ar) {
         try {
             byte[][] pdus = null;
@@ -520,9 +497,9 @@
                     for (int j = i; j < i + 8 && j < receivedPdu.length; j++) {
                         int b = receivedPdu[j] & 0xff;
                         if (b < 0x10) {
-                            sb.append("0");
+                            sb.append('0');
                         }
-                        sb.append(Integer.toHexString(b)).append(" ");
+                        sb.append(Integer.toHexString(b)).append(' ');
                     }
                     Log.d(TAG, sb.toString());
                 }
@@ -567,7 +544,8 @@
                 pdus[0] = receivedPdu;
             }
 
-            dispatchBroadcastPdus(pdus);
+            boolean isEmergencyMessage = SmsCbHeader.isEmergencyMessage(header.messageIdentifier);
+            dispatchBroadcastPdus(pdus, isEmergencyMessage);
 
             // Remove messages that are out of scope to prevent the map from
             // growing indefinitely, containing incomplete messages that were
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index 5abba6e..8d0e5d3 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -27,6 +27,7 @@
 import com.android.internal.telephony.IccConstants;
 import com.android.internal.telephony.IccSmsInterfaceManager;
 import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.IntRangeManager;
 import com.android.internal.telephony.SMSDispatcher;
 import com.android.internal.telephony.SmsRawData;
 
@@ -53,6 +54,9 @@
     private HashMap<Integer, HashSet<String>> mCellBroadcastSubscriptions =
             new HashMap<Integer, HashSet<String>>();
 
+    private CellBroadcastRangeManager mCellBroadcastRangeManager =
+            new CellBroadcastRangeManager();
+
     private static final int EVENT_LOAD_DONE = 1;
     private static final int EVENT_UPDATE_DONE = 2;
     private static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
@@ -213,7 +217,15 @@
     }
 
     public boolean enableCellBroadcast(int messageIdentifier) {
-        if (DBG) log("enableCellBroadcast");
+        return enableCellBroadcastRange(messageIdentifier, messageIdentifier);
+    }
+
+    public boolean disableCellBroadcast(int messageIdentifier) {
+        return disableCellBroadcastRange(messageIdentifier, messageIdentifier);
+    }
+
+    public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
+        if (DBG) log("enableCellBroadcastRange");
 
         Context context = mPhone.getContext();
 
@@ -223,30 +235,22 @@
 
         String client = context.getPackageManager().getNameForUid(
                 Binder.getCallingUid());
-        HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier);
 
-        if (clients == null) {
-            // This is a new message identifier
-            clients = new HashSet<String>();
-            mCellBroadcastSubscriptions.put(messageIdentifier, clients);
-
-            if (!updateCellBroadcastConfig()) {
-                mCellBroadcastSubscriptions.remove(messageIdentifier);
-                return false;
-            }
+        if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
+            log("Failed to add cell broadcast subscription for MID range " + startMessageId
+                    + " to " + endMessageId + " from client " + client);
+            return false;
         }
 
-        clients.add(client);
-
         if (DBG)
-            log("Added cell broadcast subscription for MID " + messageIdentifier
-                    + " from client " + client);
+            log("Added cell broadcast subscription for MID range " + startMessageId
+                    + " to " + endMessageId + " from client " + client);
 
         return true;
     }
 
-    public boolean disableCellBroadcast(int messageIdentifier) {
-        if (DBG) log("disableCellBroadcast");
+    public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
+        if (DBG) log("disableCellBroadcastRange");
 
         Context context = mPhone.getContext();
 
@@ -256,39 +260,56 @@
 
         String client = context.getPackageManager().getNameForUid(
                 Binder.getCallingUid());
-        HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier);
 
-        if (clients != null && clients.remove(client)) {
-            if (DBG)
-                log("Removed cell broadcast subscription for MID " + messageIdentifier
-                        + " from client " + client);
-
-            if (clients.isEmpty()) {
-                mCellBroadcastSubscriptions.remove(messageIdentifier);
-                updateCellBroadcastConfig();
-            }
-            return true;
+        if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
+            log("Failed to remove cell broadcast subscription for MID range " + startMessageId
+                    + " to " + endMessageId + " from client " + client);
+            return false;
         }
 
-        return false;
+        if (DBG)
+            log("Removed cell broadcast subscription for MID range " + startMessageId
+                    + " to " + endMessageId + " from client " + client);
+
+        return true;
     }
 
-    private boolean updateCellBroadcastConfig() {
-        Set<Integer> messageIdentifiers = mCellBroadcastSubscriptions.keySet();
+    class CellBroadcastRangeManager extends IntRangeManager {
+        private ArrayList<SmsBroadcastConfigInfo> mConfigList =
+                new ArrayList<SmsBroadcastConfigInfo>();
 
-        if (messageIdentifiers.size() > 0) {
-            SmsBroadcastConfigInfo[] configs =
-                    new SmsBroadcastConfigInfo[messageIdentifiers.size()];
-            int i = 0;
+        /**
+         * Called when the list of enabled ranges has changed. This will be
+         * followed by zero or more calls to {@link #addRange} followed by
+         * a call to {@link #finishUpdate}.
+         */
+        protected void startUpdate() {
+            mConfigList.clear();
+        }
 
-            for (int messageIdentifier : messageIdentifiers) {
-                configs[i++] = new SmsBroadcastConfigInfo(messageIdentifier, messageIdentifier,
-                        SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, true);
+        /**
+         * Called after {@link #startUpdate} to indicate a range of enabled
+         * values.
+         * @param startId the first id included in the range
+         * @param endId the last id included in the range
+         */
+        protected void addRange(int startId, int endId, boolean selected) {
+            mConfigList.add(new SmsBroadcastConfigInfo(startId, endId,
+                        SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected));
+        }
+
+        /**
+         * Called to indicate the end of a range update started by the
+         * previous call to {@link #startUpdate}.
+         */
+        protected boolean finishUpdate() {
+            if (mConfigList.isEmpty()) {
+                return setCellBroadcastActivation(false);
+            } else {
+                SmsBroadcastConfigInfo[] configs =
+                        mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]);
+                return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true);
             }
-
-            return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true);
-        } else {
-            return setCellBroadcastActivation(false);
         }
     }
 
@@ -314,7 +335,7 @@
 
     private boolean setCellBroadcastActivation(boolean activate) {
         if (DBG)
-            log("Calling setCellBroadcastActivation(" + activate + ")");
+            log("Calling setCellBroadcastActivation(" + activate + ')');
 
         synchronized (mLock) {
             Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java b/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
index 45f50bc..66e7ce0 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
@@ -30,11 +30,11 @@
  * and 9.4.4.2.3 for UMTS.
  * All other values can be treated as empty CBM data coding scheme.
  *
- * selected false means message types specified in <fromServiceId, toServiceId>
- * and <fromCodeScheme, toCodeScheme>are not accepted, while true means accepted.
+ * selected false means message types specified in {@code <fromServiceId, toServiceId>}
+ * and {@code <fromCodeScheme, toCodeScheme>} are not accepted, while true means accepted.
  *
  */
-public class SmsBroadcastConfigInfo {
+public final class SmsBroadcastConfigInfo {
     private int fromServiceId;
     private int toServiceId;
     private int fromCodeScheme;
@@ -46,11 +46,11 @@
      */
     public SmsBroadcastConfigInfo(int fromId, int toId, int fromScheme,
             int toScheme, boolean selected) {
-        setFromServiceId(fromId);
-        setToServiceId(toId);
-        setFromCodeScheme(fromScheme);
-        setToCodeScheme(toScheme);
-        this.setSelected(selected);
+        fromServiceId = fromId;
+        toServiceId = toId;
+        fromCodeScheme = fromScheme;
+        toCodeScheme = toScheme;
+        this.selected = selected;
     }
 
     /**
@@ -126,8 +126,8 @@
     @Override
     public String toString() {
         return "SmsBroadcastConfigInfo: Id [" +
-            getFromServiceId() + "," + getToServiceId() + "] Code [" +
-            getFromCodeScheme() + "," + getToCodeScheme() + "] " +
-            (isSelected() ? "ENABLED" : "DISABLED");
+                fromServiceId + ',' + toServiceId + "] Code [" +
+                fromCodeScheme + ',' + toCodeScheme + "] " +
+            (selected ? "ENABLED" : "DISABLED");
     }
-}
\ No newline at end of file
+}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 5f27cfc..8e6b79b 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -16,9 +16,44 @@
 
 package com.android.internal.telephony.gsm;
 
-public class SmsCbHeader {
+import android.telephony.SmsCbConstants;
+
+public class SmsCbHeader implements SmsCbConstants {
+    /**
+     * Length of SMS-CB header
+     */
     public static final int PDU_HEADER_LENGTH = 6;
 
+    /**
+     * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1
+     */
+    public static final int FORMAT_GSM = 1;
+
+    /**
+     * UMTS pdu format, as defined in 3gpp TS 23.041, section 9.4.2
+     */
+    public static final int FORMAT_UMTS = 2;
+
+    /**
+     * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3
+     */
+    public static final int FORMAT_ETWS_PRIMARY = 3;
+
+    /**
+     * Message type value as defined in 3gpp TS 25.324, section 11.1.
+     */
+    private static final int MESSAGE_TYPE_CBS_MESSAGE = 1;
+
+    /**
+     * Length of GSM pdus
+     */
+    public static final int PDU_LENGTH_GSM = 88;
+
+    /**
+     * Maximum length of ETWS primary message GSM pdus
+     */
+    public static final int PDU_LENGTH_ETWS = 56;
+
     public final int geographicalScope;
 
     public final int messageCode;
@@ -33,27 +68,147 @@
 
     public final int nrOfPages;
 
+    public final int format;
+
+    public final boolean etwsEmergencyUserAlert;
+
+    public final boolean etwsPopup;
+
+    public final int etwsWarningType;
+
     public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
         if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
             throw new IllegalArgumentException("Illegal PDU");
         }
 
-        geographicalScope = (pdu[0] & 0xc0) >> 6;
-        messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
-        updateNumber = pdu[1] & 0x0f;
-        messageIdentifier = (pdu[2] << 8) | pdu[3];
-        dataCodingScheme = pdu[4];
+        if (pdu.length <= PDU_LENGTH_ETWS) {
+            format = FORMAT_ETWS_PRIMARY;
+            geographicalScope = -1; //not applicable
+            messageCode = -1;
+            updateNumber = -1;
+            messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff);
+            dataCodingScheme = -1;
+            pageIndex = -1;
+            nrOfPages = -1;
+            etwsEmergencyUserAlert = (pdu[4] & 0x1) != 0;
+            etwsPopup = (pdu[5] & 0x80) != 0;
+            etwsWarningType = (pdu[4] & 0xfe) >> 1;
+        } else if (pdu.length <= PDU_LENGTH_GSM) {
+            // GSM pdus are no more than 88 bytes
+            format = FORMAT_GSM;
+            geographicalScope = (pdu[0] & 0xc0) >> 6;
+            messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
+            updateNumber = pdu[1] & 0x0f;
+            messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff);
+            dataCodingScheme = pdu[4] & 0xff;
 
-        // Check for invalid page parameter
-        int pageIndex = (pdu[5] & 0xf0) >> 4;
-        int nrOfPages = pdu[5] & 0x0f;
+            // Check for invalid page parameter
+            int pageIndex = (pdu[5] & 0xf0) >> 4;
+            int nrOfPages = pdu[5] & 0x0f;
 
-        if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+            if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+                pageIndex = 1;
+                nrOfPages = 1;
+            }
+
+            this.pageIndex = pageIndex;
+            this.nrOfPages = nrOfPages;
+            etwsEmergencyUserAlert = false;
+            etwsPopup = false;
+            etwsWarningType = -1;
+        } else {
+            // UMTS pdus are always at least 90 bytes since the payload includes
+            // a number-of-pages octet and also one length octet per page
+            format = FORMAT_UMTS;
+
+            int messageType = pdu[0];
+
+            if (messageType != MESSAGE_TYPE_CBS_MESSAGE) {
+                throw new IllegalArgumentException("Unsupported message type " + messageType);
+            }
+
+            messageIdentifier = ((pdu[1] & 0xff) << 8) | pdu[2] & 0xff;
+            geographicalScope = (pdu[3] & 0xc0) >> 6;
+            messageCode = ((pdu[3] & 0x3f) << 4) | ((pdu[4] & 0xf0) >> 4);
+            updateNumber = pdu[4] & 0x0f;
+            dataCodingScheme = pdu[5] & 0xff;
+
+            // We will always consider a UMTS message as having one single page
+            // since there's only one instance of the header, even though the
+            // actual payload may contain several pages.
             pageIndex = 1;
             nrOfPages = 1;
+            etwsEmergencyUserAlert = false;
+            etwsPopup = false;
+            etwsWarningType = -1;
         }
+    }
 
-        this.pageIndex = pageIndex;
-        this.nrOfPages = nrOfPages;
+    /**
+     * Return whether the specified message ID is an emergency (PWS) message type.
+     * This method is static and takes an argument so that it can be used by
+     * CellBroadcastReceiver, which stores message ID's in SQLite rather than PDU.
+     * @param id the message identifier to check
+     * @return true if the message is emergency type; false otherwise
+     */
+    public static boolean isEmergencyMessage(int id) {
+        return id >= MESSAGE_ID_PWS_FIRST_IDENTIFIER && id <= MESSAGE_ID_PWS_LAST_IDENTIFIER;
+    }
+
+    /**
+     * Return whether the specified message ID is an ETWS emergency message type.
+     * This method is static and takes an argument so that it can be used by
+     * CellBroadcastReceiver, which stores message ID's in SQLite rather than PDU.
+     * @param id the message identifier to check
+     * @return true if the message is ETWS emergency type; false otherwise
+     */
+    public static boolean isEtwsMessage(int id) {
+        return (id & MESSAGE_ID_ETWS_TYPE_MASK) == MESSAGE_ID_ETWS_TYPE;
+    }
+
+    /**
+     * Return whether the specified message ID is a CMAS emergency message type.
+     * This method is static and takes an argument so that it can be used by
+     * CellBroadcastReceiver, which stores message ID's in SQLite rather than PDU.
+     * @param id the message identifier to check
+     * @return true if the message is CMAS emergency type; false otherwise
+     */
+    public static boolean isCmasMessage(int id) {
+        return id >= MESSAGE_ID_CMAS_FIRST_IDENTIFIER && id <= MESSAGE_ID_CMAS_LAST_IDENTIFIER;
+    }
+
+    /**
+     * Return whether the specified message code indicates an ETWS popup alert.
+     * This method is static and takes an argument so that it can be used by
+     * CellBroadcastReceiver, which stores message codes in SQLite rather than PDU.
+     * This method assumes that the message ID has already been checked for ETWS type.
+     *
+     * @param messageCode the message code to check
+     * @return true if the message code indicates a popup alert should be displayed
+     */
+    public static boolean isEtwsPopupAlert(int messageCode) {
+        return (messageCode & MESSAGE_CODE_ETWS_ACTIVATE_POPUP) != 0;
+    }
+
+    /**
+     * Return whether the specified message code indicates an ETWS emergency user alert.
+     * This method is static and takes an argument so that it can be used by
+     * CellBroadcastReceiver, which stores message codes in SQLite rather than PDU.
+     * This method assumes that the message ID has already been checked for ETWS type.
+     *
+     * @param messageCode the message code to check
+     * @return true if the message code indicates an emergency user alert
+     */
+    public static boolean isEtwsEmergencyUserAlert(int messageCode) {
+        return (messageCode & MESSAGE_CODE_ETWS_EMERGENCY_USER_ALERT) != 0;
+    }
+
+    @Override
+    public String toString() {
+        return "SmsCbHeader{GS=" + geographicalScope + ", messageCode=0x" +
+                Integer.toHexString(messageCode) + ", updateNumber=" + updateNumber +
+                ", messageIdentifier=0x" + Integer.toHexString(messageIdentifier) +
+                ", DCS=0x" + Integer.toHexString(dataCodingScheme) +
+                ", page " + pageIndex + " of " + nrOfPages + '}';
     }
 }
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
index 7136ea0..417aac4 100644
--- a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
@@ -18,6 +18,7 @@
 
 import android.telephony.SmsCbMessage;
 import android.test.AndroidTestCase;
+import android.util.Log;
 
 /**
  * Test cases for basic SmsCbMessage operations
@@ -69,6 +70,36 @@
         doTestGeographicalScopeValue(pdu, (byte)0xC0, SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE);
     }
 
+    public void testGetGeographicalScopeUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected geographical scope decoded",
+                SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE, msg.getGeographicalScope());
+    }
+
     public void testGetMessageBody7Bit() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -92,6 +123,83 @@
                 msg.getMessageBody());
     }
 
+    public void testGetMessageBody7BitUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A GSM default alphabet message with carriage return padding",
+                msg.getMessageBody());
+    }
+
+    public void testGetMessageBody7BitMultipageUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x01, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+                (byte)0x02,
+
+                (byte)0xC6, (byte)0xB4, (byte)0x7C, (byte)0x4E, (byte)0x07, (byte)0xC1,
+                (byte)0xC3, (byte)0xE7, (byte)0xF2, (byte)0xAA, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A,
+                (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34,
+                (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x0A,
+
+                (byte)0xD3, (byte)0xF2, (byte)0xF8, (byte)0xED, (byte)0x26, (byte)0x83,
+                (byte)0xE0, (byte)0xE1, (byte)0x73, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A,
+                (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34,
+                (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x0A
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected multipage 7-bit string decoded",
+                "First page+Second page",
+                msg.getMessageBody());
+    }
+
     public void testGetMessageBody7BitFull() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -117,6 +225,38 @@
                 msg.getMessageBody());
     }
 
+    public void testGetMessageBody7BitFullUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xC4, (byte)0xE5, (byte)0xB4,
+                (byte)0xFB, (byte)0x0C, (byte)0x2A, (byte)0xE3, (byte)0xC3, (byte)0x63,
+                (byte)0x3A, (byte)0x3B, (byte)0x0F, (byte)0xCA, (byte)0xCD, (byte)0x40,
+                (byte)0x63, (byte)0x74, (byte)0x58, (byte)0x1E, (byte)0x1E, (byte)0xD3,
+                (byte)0xCB, (byte)0xF2, (byte)0x39, (byte)0x88, (byte)0xFD, (byte)0x76,
+                (byte)0x9F, (byte)0x59, (byte)0xA0, (byte)0x76, (byte)0x39, (byte)0xEC,
+                (byte)0x4E, (byte)0xBB, (byte)0xCF, (byte)0x20, (byte)0x3A, (byte)0xBA,
+                (byte)0x2C, (byte)0x2F, (byte)0x83, (byte)0xD2, (byte)0x73, (byte)0x90,
+                (byte)0xFB, (byte)0x0D, (byte)0x82, (byte)0x87, (byte)0xC9, (byte)0xE4,
+                (byte)0xB4, (byte)0xFB, (byte)0x1C, (byte)0x02,
+
+                (byte)0x52
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals(
+                "Unexpected 7-bit string decoded",
+                "A GSM default alphabet message being exactly 93 characters long, " +
+                "meaning there is no padding!",
+                msg.getMessageBody());
+    }
+
     public void testGetMessageBody7BitWithLanguage() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x04, (byte)0x11, (byte)0x41,
@@ -167,6 +307,38 @@
         assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
     }
 
+    public void testGetMessageBody7BitWithLanguageInBodyUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x10,
+
+                (byte)0x01,
+
+                (byte)0x73, (byte)0x7B, (byte)0x23, (byte)0x08, (byte)0x3A, (byte)0x4E,
+                (byte)0x9B, (byte)0x20, (byte)0x72, (byte)0xD9, (byte)0x1C, (byte)0xAE,
+                (byte)0xB3, (byte)0xE9, (byte)0xA0, (byte)0x30, (byte)0x1B, (byte)0x8E,
+                (byte)0x0E, (byte)0x8B, (byte)0xCB, (byte)0x74, (byte)0x50, (byte)0xBB,
+                (byte)0x3C, (byte)0x9F, (byte)0x87, (byte)0xCF, (byte)0x65, (byte)0xD0,
+                (byte)0x3D, (byte)0x4D, (byte)0x47, (byte)0x83, (byte)0xC6, (byte)0x61,
+                (byte)0xB9, (byte)0x3C, (byte)0x1D, (byte)0x3E, (byte)0x97, (byte)0x41,
+                (byte)0xF2, (byte)0x32, (byte)0xBD, (byte)0x2E, (byte)0x77, (byte)0x83,
+                (byte)0xE0, (byte)0x61, (byte)0x32, (byte)0x39, (byte)0xED, (byte)0x3E,
+                (byte)0x37, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x37
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A GSM default alphabet message with carriage return padding",
+                msg.getMessageBody());
+
+        assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
+    }
+
     public void testGetMessageBody8Bit() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x44, (byte)0x11, (byte)0x41,
@@ -210,6 +382,81 @@
                 "A UCS2 message containing a \u0434 character", msg.getMessageBody());
     }
 
+    public void testGetMessageBodyUcs2Umts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x48,
+
+                (byte)0x01,
+
+                (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55,
+                (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32,
+                (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x6D, (byte)0x00, (byte)0x65,
+                (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x20,
+                (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6E,
+                (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x69,
+                (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E,
+                (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x20, (byte)0x04, (byte)0x34, (byte)0x00, (byte)0x20,
+                (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x63,
+                (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x72,
+                (byte)0x00, (byte)0x0D, (byte)0x00, (byte)0x0D,
+
+                (byte)0x4E
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+    }
+
+    public void testGetMessageBodyUcs2MultipageUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x48,
+
+                (byte)0x02,
+
+                (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x41,
+                (byte)0x00, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+
+                (byte)0x06,
+
+                (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x42, (byte)0x00, (byte)0x42,
+                (byte)0x00, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+                (byte)0x0D, (byte)0x0D, (byte)0x0D, (byte)0x0D,
+
+                (byte)0x06
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected multipage UCS2 string decoded",
+                "AAABBB", msg.getMessageBody());
+    }
+
     public void testGetMessageBodyUcs2WithLanguageInBody() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x11, (byte)0x11, (byte)0x78,
@@ -234,6 +481,37 @@
         assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
     }
 
+    public void testGetMessageBodyUcs2WithLanguageInBodyUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x11,
+
+                (byte)0x01,
+
+                (byte)0x78, (byte)0x3C, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20,
+                (byte)0x00, (byte)0x55, (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53,
+                (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x6D,
+                (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73,
+                (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65,
+                (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F,
+                (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69,
+                (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20,
+                (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x20, (byte)0x04, (byte)0x34,
+                (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68,
+                (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61,
+                (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65,
+                (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x0D,
+
+                (byte)0x50
+        };
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected 7-bit string decoded",
+                "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+
+        assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
+    }
+
     public void testGetMessageIdentifier() {
         byte[] pdu = {
                 (byte)0xC0, (byte)0x00, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -256,6 +534,35 @@
         assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
     }
 
+    public void testGetMessageIdentifierUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x30, (byte)0x39, (byte)0x2A, (byte)0xA5, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
+    }
+
     public void testGetMessageCode() {
         byte[] pdu = {
                 (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -278,6 +585,35 @@
         assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
     }
 
+    public void testGetMessageCodeUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x30, (byte)0x39, (byte)0x2A, (byte)0xA5, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
+    }
+
     public void testGetUpdateNumber() {
         byte[] pdu = {
                 (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
@@ -299,4 +635,78 @@
 
         assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
     }
+
+    public void testGetUpdateNumberUmts() {
+        byte[] pdu = {
+                (byte)0x01, (byte)0x30, (byte)0x39, (byte)0x2A, (byte)0xA5, (byte)0x40,
+
+                (byte)0x01,
+
+                (byte)0x41, (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91,
+                (byte)0xCB, (byte)0xE6, (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07,
+                (byte)0x85, (byte)0xD9, (byte)0x70, (byte)0x74, (byte)0x58, (byte)0x5C,
+                (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5, (byte)0xF9, (byte)0x3C,
+                (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69, (byte)0x3A,
+                (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+                (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9,
+                (byte)0x75, (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93,
+                (byte)0xC9, (byte)0x69, (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68,
+                (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3, (byte)0xD1,
+                (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46, (byte)0xA3,
+                (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D, (byte)0x46,
+                (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+                (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00,
+
+                (byte)0x34
+        };
+
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+        assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
+    }
+
+    /* ETWS Test message including header */
+    private static final byte[] etwsMessageNormal = IccUtils.hexStringToBytes("000011001101" +
+            "0D0A5BAE57CE770C531790E85C716CBF3044573065B930675730" +
+            "9707767A751F30025F37304463FA308C306B5099304830664E0B30553044FF086C178C615E81FF09" +
+            "0000000000000000000000000000");
+
+    private static final byte[] etwsMessageCancel = IccUtils.hexStringToBytes("000011001101" +
+            "0D0A5148307B3069002800310030003A0035" +
+            "00320029306E7DCA602557309707901F5831309253D66D883057307E3059FF086C178C615E81FF09" +
+            "00000000000000000000000000000000000000000000");
+
+    private static final byte[] etwsMessageTest = IccUtils.hexStringToBytes("000011031101" +
+            "0D0A5BAE57CE770C531790E85C716CBF3044" +
+            "573065B9306757309707300263FA308C306B5099304830664E0B30553044FF086C178C615E81FF09" +
+            "00000000000000000000000000000000000000000000");
+
+    // FIXME: add example of ETWS primary notification PDU
+
+    public void testEtwsMessageNormal() {
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(etwsMessageNormal);
+        Log.d("GsmSmsCbTest", msg.toString());
+        assertEquals("GS mismatch", 0, msg.getGeographicalScope());
+        assertEquals("message code mismatch", 0, msg.getMessageCode());
+        assertEquals("update number mismatch", 0, msg.getUpdateNumber());
+        assertEquals("message ID mismatch", 0x1100, msg.getMessageIdentifier());
+    }
+
+    public void testEtwsMessageCancel() {
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(etwsMessageCancel);
+        Log.d("GsmSmsCbTest", msg.toString());
+        assertEquals("GS mismatch", 0, msg.getGeographicalScope());
+        assertEquals("message code mismatch", 0, msg.getMessageCode());
+        assertEquals("update number mismatch", 0, msg.getUpdateNumber());
+        assertEquals("message ID mismatch", 0x1100, msg.getMessageIdentifier());
+    }
+
+    public void testEtwsMessageTest() {
+        SmsCbMessage msg = SmsCbMessage.createFromPdu(etwsMessageTest);
+        Log.d("GsmSmsCbTest", msg.toString());
+        assertEquals("GS mismatch", 0, msg.getGeographicalScope());
+        assertEquals("message code mismatch", 0, msg.getMessageCode());
+        assertEquals("update number mismatch", 0, msg.getUpdateNumber());
+        assertEquals("message ID mismatch", 0x1103, msg.getMessageIdentifier());
+    }
 }
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/IntRangeManagerTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/IntRangeManagerTest.java
new file mode 100644
index 0000000..79dca39
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/IntRangeManagerTest.java
@@ -0,0 +1,374 @@
+/*
+ * 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 com.android.internal.telephony;
+
+import android.test.AndroidTestCase;
+
+import com.android.internal.telephony.gsm.SmsBroadcastConfigInfo;
+
+import java.util.ArrayList;
+
+/**
+ * Test cases for the IntRangeManager class.
+ */
+public class IntRangeManagerTest extends AndroidTestCase {
+
+    private static final int SMS_CB_CODE_SCHEME_MIN = 0;
+    private static final int SMS_CB_CODE_SCHEME_MAX = 255;
+
+    private static final int FLAG_START_UPDATE_CALLED   = 0x01;
+    private static final int FLAG_ADD_RANGE_CALLED      = 0x02;
+    private static final int FLAG_FINISH_UPDATE_CALLED  = 0x04;
+
+    private static final int ALL_FLAGS_SET = FLAG_START_UPDATE_CALLED | FLAG_ADD_RANGE_CALLED |
+            FLAG_FINISH_UPDATE_CALLED;
+
+    /** Dummy IntRangeManager for testing. */
+    class TestIntRangeManager extends IntRangeManager {
+        ArrayList<SmsBroadcastConfigInfo> mConfigList =
+                new ArrayList<SmsBroadcastConfigInfo>();
+
+        int flags;
+        boolean finishUpdateReturnValue = true;
+
+        /**
+         * Called when the list of enabled ranges has changed. This will be
+         * followed by zero or more calls to {@link #addRange} followed by
+         * a call to {@link #finishUpdate}.
+         */
+        protected void startUpdate() {
+            mConfigList.clear();
+            flags |= FLAG_START_UPDATE_CALLED;
+        }
+
+        /**
+         * Called after {@link #startUpdate} to indicate a range of enabled
+         * values.
+         * @param startId the first id included in the range
+         * @param endId the last id included in the range
+         */
+        protected void addRange(int startId, int endId, boolean selected) {
+            mConfigList.add(new SmsBroadcastConfigInfo(startId, endId,
+                        SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected));
+            flags |= FLAG_ADD_RANGE_CALLED;
+        }
+
+        /**
+         * Called to indicate the end of a range update started by the
+         * previous call to {@link #startUpdate}.
+         */
+        protected boolean finishUpdate() {
+            flags |= FLAG_FINISH_UPDATE_CALLED;
+            return finishUpdateReturnValue;
+        }
+
+        /** Reset the object for the next test case. */
+        void reset() {
+            flags = 0;
+            mConfigList.clear();
+        }
+    }
+
+    public void testEmptyRangeManager() {
+        TestIntRangeManager testManager = new TestIntRangeManager();
+        assertEquals("expecting empty configlist", 0, testManager.mConfigList.size());
+    }
+
+    private void checkConfigInfo(SmsBroadcastConfigInfo info, int fromServiceId,
+            int toServiceId, int fromCodeScheme, int toCodeScheme, boolean selected) {
+        assertEquals("fromServiceId", fromServiceId, info.getFromServiceId());
+        assertEquals("toServiceId", toServiceId, info.getToServiceId());
+        assertEquals("fromCodeScheme", fromCodeScheme, info.getFromCodeScheme());
+        assertEquals("toCodeScheme", toCodeScheme, info.getToCodeScheme());
+        assertEquals("selected", selected, info.isSelected());
+    }
+
+    public void testAddSingleChannel() {
+        TestIntRangeManager testManager = new TestIntRangeManager();
+        assertEquals("flags before test", 0, testManager.flags);
+        assertTrue("enabling range", testManager.enableRange(123, 123, "client1"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 123, 123, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 123, 123, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+    }
+
+    public void testRemoveSingleChannel() {
+        TestIntRangeManager testManager = new TestIntRangeManager();
+        assertTrue("enabling range", testManager.enableRange(123, 123, "client1"));
+        assertEquals("flags after enable", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        testManager.reset();
+        assertTrue("disabling range", testManager.disableRange(123, 123, "client1"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 123, 123, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", FLAG_START_UPDATE_CALLED | FLAG_FINISH_UPDATE_CALLED,
+                testManager.flags);
+        assertEquals("configlist size", 0, testManager.mConfigList.size());
+    }
+
+    public void testRemoveBadChannel() {
+        TestIntRangeManager testManager = new TestIntRangeManager();
+        assertFalse("disabling missing range", testManager.disableRange(123, 123, "client1"));
+        assertEquals("flags after test", 0, testManager.flags);
+        assertEquals("configlist size", 0, testManager.mConfigList.size());
+    }
+
+    public void testAddTwoChannels() {
+        TestIntRangeManager testManager = new TestIntRangeManager();
+        assertEquals("flags before test", 0, testManager.flags);
+        assertTrue("enabling range 1", testManager.enableRange(100, 120, "client1"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 100, 120, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("enabling range 2", testManager.enableRange(200, 250, "client2"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 200, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 2, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 100, 120, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(1), 200, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+    }
+
+    public void testOverlappingChannels() {
+        TestIntRangeManager testManager = new TestIntRangeManager();
+        assertEquals("flags before test", 0, testManager.flags);
+        assertTrue("enabling range 1", testManager.enableRange(100, 200, "client1"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 100, 200, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("enabling range 2", testManager.enableRange(150, 250, "client2"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 201, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 100, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("disabling range 1", testManager.disableRange(100, 200, "client1"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 100, 149, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("disabling range 2", testManager.disableRange(150, 250, "client2"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 150, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", FLAG_START_UPDATE_CALLED | FLAG_FINISH_UPDATE_CALLED,
+                testManager.flags);
+        assertEquals("configlist size", 0, testManager.mConfigList.size());
+    }
+
+    public void testOverlappingChannels2() {
+        TestIntRangeManager testManager = new TestIntRangeManager();
+        assertEquals("flags before test", 0, testManager.flags);
+        assertTrue("enabling range 1", testManager.enableRange(100, 200, "client1"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 100, 200, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("enabling range 2", testManager.enableRange(150, 250, "client2"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 201, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("disabling range 2", testManager.disableRange(150, 250, "client2"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 201, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 100, 200, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("disabling range 1", testManager.disableRange(100, 200, "client1"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 100, 200, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+    }
+
+    public void testMultipleOverlappingChannels() {
+        TestIntRangeManager testManager = new TestIntRangeManager();
+        assertEquals("flags before test", 0, testManager.flags);
+        assertTrue("enabling range 1", testManager.enableRange(67, 9999, "client1"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 67, 9999, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("enabling range 2", testManager.enableRange(150, 250, "client2"));
+        assertEquals("flags after test", 0, testManager.flags);
+        assertEquals("configlist size", 0, testManager.mConfigList.size());
+        testManager.reset();
+        assertTrue("enabling range 3", testManager.enableRange(25, 75, "client3"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 25, 66, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("enabling range 4", testManager.enableRange(12, 500, "client4"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 12, 24, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("enabling range 5", testManager.enableRange(8000, 9998, "client5"));
+        assertEquals("flags after test", 0, testManager.flags);
+        assertEquals("configlist size", 0, testManager.mConfigList.size());
+        testManager.reset();
+        assertTrue("enabling range 6", testManager.enableRange(50000, 65535, "client6"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 50000, 65535, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 2, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 12, 9999, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(1), 50000, 65535, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("disabling range 1", testManager.disableRange(67, 9999, "client1"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 2, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 501, 7999, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        checkConfigInfo(testManager.mConfigList.get(1), 9999, 9999, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 3, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 12, 500, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(1), 8000, 9998, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(2), 50000, 65535, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("disabling range 4", testManager.disableRange(12, 500, "client4"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 3, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 12, 24, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        checkConfigInfo(testManager.mConfigList.get(1), 76, 149, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        checkConfigInfo(testManager.mConfigList.get(2), 251, 500, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 4, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 25, 75, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(1), 150, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(2), 8000, 9998, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(3), 50000, 65535, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("disabling range 5", testManager.disableRange(8000, 9998, "client5"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 8000, 9998, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 3, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 25, 75, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(1), 150, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(2), 50000, 65535, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("disabling range 6", testManager.disableRange(50000, 65535, "client6"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 50000, 65535, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 2, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 25, 75, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        checkConfigInfo(testManager.mConfigList.get(1), 150, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("disabling range 2", testManager.disableRange(150, 250, "client2"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 150, 250, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 25, 75, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, true);
+        testManager.reset();
+        assertTrue("disabling range 3", testManager.disableRange(25, 75, "client3"));
+        assertEquals("flags after test", ALL_FLAGS_SET, testManager.flags);
+        assertEquals("configlist size", 1, testManager.mConfigList.size());
+        checkConfigInfo(testManager.mConfigList.get(0), 25, 75, SMS_CB_CODE_SCHEME_MIN,
+                SMS_CB_CODE_SCHEME_MAX, false);
+        testManager.reset();
+        assertTrue("updating ranges", testManager.updateRanges());
+        assertEquals("flags after test", FLAG_START_UPDATE_CALLED | FLAG_FINISH_UPDATE_CALLED,
+                testManager.flags);
+        assertEquals("configlist size", 0, testManager.mConfigList.size());
+    }
+}
diff --git a/tests/BiDiTests/res/layout/frame_layout_ltr.xml b/tests/BiDiTests/res/layout/frame_layout_ltr.xml
index bd324e3..61fd06e 100644
--- a/tests/BiDiTests/res/layout/frame_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/frame_layout_ltr.xml
@@ -18,7 +18,7 @@
     android:id="@+id/frame_layout_ltr"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr"
+    android:layoutDirection="ltr"
     android:background="#FF000000">
 
         <FrameLayout
@@ -52,28 +52,28 @@
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|before"
+            android:layout_gravity="top|start"
             android:background="#FFFFFFFF">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|after"
+            android:layout_gravity="top|end"
             android:background="#FFFFFF00">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|before"
+            android:layout_gravity="bottom|start"
             android:background="#FFFFFFFF">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|after"
+            android:layout_gravity="bottom|end"
             android:background="#FFFFFF00">
         </FrameLayout>
 
diff --git a/tests/BiDiTests/res/layout/frame_layout_rtl.xml b/tests/BiDiTests/res/layout/frame_layout_rtl.xml
index 814f5e1..598b41a 100644
--- a/tests/BiDiTests/res/layout/frame_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/frame_layout_rtl.xml
@@ -18,7 +18,7 @@
     android:id="@+id/frame_layout_ltr"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl"
+    android:layoutDirection="rtl"
     android:background="#FF000000">
 
         <FrameLayout
@@ -52,28 +52,28 @@
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|before"
+            android:layout_gravity="top|start"
             android:background="#FFFFFFFF">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|after"
+            android:layout_gravity="top|end"
             android:background="#FFFFFF00">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|before"
+            android:layout_gravity="bottom|start"
             android:background="#FFFFFFFF">
         </FrameLayout>
 
         <FrameLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|after"
+            android:layout_gravity="bottom|end"
             android:background="#FFFFFF00">
         </FrameLayout>
 
diff --git a/tests/BiDiTests/res/layout/linear_layout_ltr.xml b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
index c5a8d47..d4386f2 100644
--- a/tests/BiDiTests/res/layout/linear_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/linear_layout_ltr.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr">
+    android:layoutDirection="ltr">
 
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="horizontal"
@@ -51,7 +51,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -78,7 +78,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -105,7 +105,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -132,7 +132,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -159,7 +159,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -186,7 +186,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
diff --git a/tests/BiDiTests/res/layout/linear_layout_rtl.xml b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
index 1494fec..9d07263 100644
--- a/tests/BiDiTests/res/layout/linear_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/linear_layout_rtl.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl">
+    android:layoutDirection="rtl">
 
     <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:orientation="horizontal"
@@ -51,7 +51,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -78,7 +78,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -105,7 +105,7 @@
         android:orientation="horizontal"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -132,7 +132,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -159,7 +159,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
@@ -186,7 +186,7 @@
         android:orientation="vertical"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
        <Button android:layout_height="wrap_content"
                android:layout_width="wrap_content"
diff --git a/tests/BiDiTests/res/layout/relative_layout_ltr.xml b/tests/BiDiTests/res/layout/relative_layout_ltr.xml
index 53ae7c3..d789707 100644
--- a/tests/BiDiTests/res/layout/relative_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_ltr.xml
@@ -18,7 +18,7 @@
     android:id="@+id/frame_layout_ltr"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr"
+    android:layoutDirection="ltr"
     android:background="#FF000000">
 
         <RelativeLayout
@@ -52,28 +52,28 @@
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|before"
+            android:layout_gravity="top|start"
             android:background="#FFFFFFFF">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|after"
+            android:layout_gravity="top|end"
             android:background="#FFFFFF00">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|before"
+            android:layout_gravity="bottom|start"
             android:background="#FFFFFFFF">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|after"
+            android:layout_gravity="bottom|end"
             android:background="#FFFFFF00">
         </RelativeLayout>
 
diff --git a/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml b/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml
index 93d1b4b..a13ef8b 100644
--- a/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_ltr_2.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr">
+    android:layoutDirection="ltr">
 
     <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="fill_parent"
@@ -58,7 +58,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="ltr">
+            android:layoutDirection="ltr">
 
         <TextView android:id="@+id/label_2"
                 android:layout_width="fill_parent"
@@ -91,7 +91,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="inherit">
+            android:layoutDirection="inherit">
 
         <TextView android:id="@+id/label_3"
                 android:layout_width="fill_parent"
@@ -124,7 +124,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="rtl">
+            android:layoutDirection="rtl">
 
         <TextView android:id="@+id/label_4"
                 android:layout_width="fill_parent"
diff --git a/tests/BiDiTests/res/layout/relative_layout_rtl.xml b/tests/BiDiTests/res/layout/relative_layout_rtl.xml
index 4b87752..5808924 100644
--- a/tests/BiDiTests/res/layout/relative_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_rtl.xml
@@ -18,7 +18,7 @@
     android:id="@+id/frame_layout_rtl"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl"
+    android:layoutDirection="rtl"
     android:background="#FF000000">
 
         <RelativeLayout
@@ -52,28 +52,28 @@
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|before"
+            android:layout_gravity="top|start"
             android:background="#FFFFFFFF">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="top|after"
+            android:layout_gravity="top|end"
             android:background="#FFFFFF00">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|before"
+            android:layout_gravity="bottom|start"
             android:background="#FFFFFFFF">
         </RelativeLayout>
 
         <RelativeLayout
             android:layout_width="100dp"
             android:layout_height="100dp"
-            android:layout_gravity="bottom|after"
+            android:layout_gravity="bottom|end"
             android:background="#FFFFFF00">
         </RelativeLayout>
 
diff --git a/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml b/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml
index 8418e72..1a6b3d5 100644
--- a/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml
+++ b/tests/BiDiTests/res/layout/relative_layout_rtl_2.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl">
+    android:layoutDirection="rtl">
 
     <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="fill_parent"
@@ -58,7 +58,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="ltr">
+            android:layoutDirection="ltr">
 
         <TextView android:id="@+id/label_2"
                 android:layout_width="fill_parent"
@@ -91,7 +91,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="inherit">
+            android:layoutDirection="inherit">
 
         <TextView android:id="@+id/label_3"
                 android:layout_width="fill_parent"
@@ -124,7 +124,7 @@
             android:layout_height="wrap_content"
             android:background="#FF000000"
             android:padding="10px"
-            android:horizontalDirection="rtl">
+            android:layoutDirection="rtl">
 
         <TextView android:id="@+id/label_4"
                 android:layout_width="fill_parent"
diff --git a/tests/BiDiTests/res/layout/table_layout_ltr.xml b/tests/BiDiTests/res/layout/table_layout_ltr.xml
index f44de8e..8e1891e 100644
--- a/tests/BiDiTests/res/layout/table_layout_ltr.xml
+++ b/tests/BiDiTests/res/layout/table_layout_ltr.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="ltr">
+    android:layoutDirection="ltr">
 
     <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="wrap_content"
@@ -72,7 +72,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
@@ -120,7 +120,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
@@ -168,7 +168,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
diff --git a/tests/BiDiTests/res/layout/table_layout_rtl.xml b/tests/BiDiTests/res/layout/table_layout_rtl.xml
index 84270ba..bd664e4 100644
--- a/tests/BiDiTests/res/layout/table_layout_rtl.xml
+++ b/tests/BiDiTests/res/layout/table_layout_rtl.xml
@@ -19,7 +19,7 @@
     android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:horizontalDirection="rtl">
+    android:layoutDirection="rtl">
 
     <TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
         android:layout_width="wrap_content"
@@ -72,7 +72,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="inherit">
+        android:layoutDirection="inherit">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
@@ -120,7 +120,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="ltr">
+        android:layoutDirection="ltr">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
@@ -168,7 +168,7 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:stretchColumns="1,2"
-        android:horizontalDirection="rtl">
+        android:layoutDirection="rtl">
 
         <TableRow>
             <Button android:layout_height="wrap_content"
diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml
index 2aeb9e1..d067e7d 100644
--- a/tests/BiDiTests/res/values/strings.xml
+++ b/tests/BiDiTests/res/values/strings.xml
@@ -19,9 +19,9 @@
     <string name="button3_text">Button3</string>
     <string name="button4_text">Button4</string>
     <string name="button_right_text">Right</string>
-    <string name="button_after_text">After</string>
+    <string name="button_after_text">End</string>
     <string name="button_left_text">Left</string>
-    <string name="button_before_text">Before</string>
+    <string name="button_before_text">Start</string>
     <string name="button_requestlayout_text">Request Layout</string>
     <string name="textview_text">This is a text for a TextView</string>
     <string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string>
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java b/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java
index c6f390e..505c83d 100755
--- a/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java
+++ b/tests/GridLayoutTest/src/com/android/test/layout/AlignmentTest.java
@@ -31,10 +31,10 @@
 
 public class AlignmentTest  extends Activity {
 
-    public static final String[] HORIZONTAL_NAMES = new String[]{"LEFT", "center", "east", "fill"};
-    public static final Alignment[] HORIZONTAL_ALIGNMENTS = new Alignment[]{LEFT, CENTER, RIGHT, FILL};
-    public static final String[] VERTICAL_NAMES = new String[]{"north", "center", "baseline", "south", "fill"};
-    public static final Alignment[] VERTICAL_ALIGNMENTS = new Alignment[]{TOP, CENTER, BASELINE, BOTTOM, FILL};
+    public static final String[] HORIZONTAL_NAMES = {"LEFT", "center", "east", "fill"};
+    public static final Alignment[] HORIZONTAL_ALIGNMENTS = {LEFT, CENTER, RIGHT, FILL};
+    public static final String[] VERTICAL_NAMES = {"north", "center", "baseline", "south", "fill"};
+    public static final Alignment[] VERTICAL_ALIGNMENTS = {TOP, CENTER, BASELINE, BOTTOM, FILL};
     private static Context CONTEXT;
 
     public static interface ViewFactory {
@@ -72,7 +72,8 @@
         }
     };
 
-    public static final ViewFactory[] FACTORIES = new ViewFactory[]{BUTTON_FACTORY, LABEL_FACTORY, TEXT_FIELD_FACTORY};
+    public static final ViewFactory[] FACTORIES =
+                            {BUTTON_FACTORY, LABEL_FACTORY, TEXT_FIELD_FACTORY};
 
     public static ViewGroup create(Context context1) {
         CONTEXT = context1;
@@ -86,7 +87,9 @@
                 Group rowGroup = new Group(i, va);
                 Group colGroup = new Group(j, ha);
                 LayoutParams layoutParams = new LayoutParams(rowGroup, colGroup);
-                container.addView(FACTORIES[(i + j) % FACTORIES.length].create(VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20), layoutParams);
+                String name = VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j];
+                ViewFactory factory = FACTORIES[(i + j) % FACTORIES.length];
+                container.addView(factory.create(name, 20), layoutParams);
             }
         }
 
@@ -105,7 +108,8 @@
             GridLayout p = (GridLayout) v.getParent();
             p.layout(0, 0, 1000 + (i % 2), 500 + (i % 2));
         }
-        System.out.println("Time: " + (float) (System.currentTimeMillis() - start) / N * 1000 + "mics");
+        float time = (float) (System.currentTimeMillis() - start) / N * 1000;
+        System.out.println("Time: " + time + "mics");
     }
 
     protected void onCreate(Bundle savedInstanceState) {
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java b/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java
index b4451e8..2eecb8a 100644
--- a/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java
+++ b/tests/GridLayoutTest/src/com/android/test/layout/GridLayoutTest.java
@@ -37,7 +37,6 @@
                 GridLayout.Group colGroup = new GridLayout.Group(UNDEFINED, null);
                 GridLayout.LayoutParams lp = new GridLayout.LayoutParams(rowGroup, colGroup);
                 lp.setGravity(va | ha);
-//                View v = create(VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20);
                 View v = create(context, VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20);
                 container.addView(v, lp);
             }
diff --git a/tests/GridLayoutTest/src/com/android/test/layout/LinearLayoutTest.java b/tests/GridLayoutTest/src/com/android/test/layout/LinearLayoutTest.java
index fbd1239..c7f4665 100644
--- a/tests/GridLayoutTest/src/com/android/test/layout/LinearLayoutTest.java
+++ b/tests/GridLayoutTest/src/com/android/test/layout/LinearLayoutTest.java
@@ -22,20 +22,19 @@
 import android.widget.LinearLayout;
 
 import static android.widget.LinearLayout.*;
+import static android.widget.LinearLayout.LayoutParams.*;
 
 public class LinearLayoutTest extends AbstractLayoutTest {
     public ViewGroup create(Context context) {
         LinearLayout container = new LinearLayout(context);
         container.setOrientation(LinearLayout.VERTICAL);
-//        container.setUseDefaultMargins(true);
 
         for (int i = 0; i < VERTICAL_ALIGNMENTS.length; i++) {
             int va = VERTICAL_ALIGNMENTS[i];
             for (int j = 0; j < HORIZONTAL_ALIGNMENTS.length; j++) {
                 int ha = HORIZONTAL_ALIGNMENTS[j];
-                LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+                LayoutParams lp = new LayoutParams(WRAP_CONTENT, WRAP_CONTENT);
                 lp.gravity = va | ha;
-//                View v = create(VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20);
                 View v = create(context, VERTICAL_NAMES[i] + "-" + HORIZONTAL_NAMES[j], 20);
                 container.addView(v, lp);
             }
diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/globe.png b/tests/RenderScriptTests/PerfTest/res/drawable/globe.png
new file mode 100755
index 0000000..f9d6172
--- /dev/null
+++ b/tests/RenderScriptTests/PerfTest/res/drawable/globe.png
Binary files differ
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
index 5377f12..3ba6ba4 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java
@@ -113,16 +113,20 @@
     private Allocation mTexOpaque;
     private Allocation mTexTransparent;
     private Allocation mTexChecker;
+    private Allocation mTexGlobe;
 
     private Mesh m10by10Mesh;
     private Mesh m100by100Mesh;
     private Mesh mWbyHMesh;
     private Mesh mTorus;
+    private Mesh mSingleMesh;
 
     Font mFontSans;
     Font mFontSerif;
     private Allocation mTextAlloc;
 
+    private ScriptField_TexAllocs_s mTextureAllocs;
+
     private ScriptC_rsbench mScript;
 
     private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options();
@@ -253,6 +257,27 @@
         return tmb.create(true);
     }
 
+    /**
+     * Create a mesh with a single quad for the given width and height.
+     */
+    private Mesh getSingleMesh(float width, float height) {
+        Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS,
+                                           2, Mesh.TriangleMeshBuilder.TEXTURE_0);
+        float xOffset = width/2;
+        float yOffset = height/2;
+        tmb.setTexture(0, 0);
+        tmb.addVertex(-1.0f * xOffset, -1.0f * yOffset);
+        tmb.setTexture(1, 0);
+        tmb.addVertex(xOffset, -1.0f * yOffset);
+        tmb.setTexture(1, 1);
+        tmb.addVertex(xOffset, yOffset);
+        tmb.setTexture(0, 1);
+        tmb.addVertex(-1.0f * xOffset, yOffset);
+        tmb.addTriangle(0, 3, 1);
+        tmb.addTriangle(1, 3, 2);
+        return tmb.create(true);
+    }
+
     private void initProgramStore() {
         // Use stock the stock program store object
         mProgStoreBlendNoneDepth = ProgramStore.BLEND_NONE_DEPTH_TEST(mRS);
@@ -392,11 +417,13 @@
         mTexOpaque = loadTextureRGB(R.drawable.data);
         mTexTransparent = loadTextureARGB(R.drawable.leaf);
         mTexChecker = loadTextureRGB(R.drawable.checker);
+        mTexGlobe = loadTextureRGB(R.drawable.globe);
 
         mScript.set_gTexTorus(mTexTorus);
         mScript.set_gTexOpaque(mTexOpaque);
         mScript.set_gTexTransparent(mTexTransparent);
         mScript.set_gTexChecker(mTexChecker);
+        mScript.set_gTexGlobe(mTexGlobe);
     }
 
     private void initFonts() {
@@ -418,6 +445,8 @@
         mScript.set_g100by100Mesh(m100by100Mesh);
         mWbyHMesh= getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, mBenchmarkDimX/4, mBenchmarkDimY/4);
         mScript.set_gWbyHMesh(mWbyHMesh);
+        mSingleMesh = getSingleMesh(50, 50);
+        mScript.set_gSingleMesh(mSingleMesh);
 
         FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus);
         FileA3D.IndexEntry entry = model.getIndexEntry(0);
@@ -517,6 +546,16 @@
                                            Allocation.USAGE_GRAPHICS_RENDER_TARGET);
         mScript.set_gRenderBufferDepth(offscreen);
 
+
+        mTextureAllocs = new ScriptField_TexAllocs_s(mRS, 100);
+        for (int i = 0; i < 100; i++) {
+            ScriptField_TexAllocs_s.Item texElem = new ScriptField_TexAllocs_s.Item();
+            texElem.texture = loadTextureRGB(R.drawable.globe);
+            mTextureAllocs.set(texElem, i, false);
+        }
+        mTextureAllocs.copyAll();
+        mScript.bind_gTexList100(mTextureAllocs);
+
         mRS.bindRootScript(mScript);
     }
 }
diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
index e7f5cd4..b6572fb 100644
--- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
+++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs
@@ -23,7 +23,7 @@
 const int RS_MSG_TEST_DONE = 100;
 const int RS_MSG_RESULTS_READY = 101;
 
-const int gMaxModes = 25;
+const int gMaxModes = 27;
 int gMaxLoops;
 
 // Allocation to send test names back to java
@@ -44,11 +44,19 @@
 rs_allocation gTexTorus;
 rs_allocation gTexTransparent;
 rs_allocation gTexChecker;
+rs_allocation gTexGlobe;
+
+typedef struct TexAllocs_s {
+    rs_allocation texture;
+} TexAllocs;
+
+TexAllocs *gTexList100;
 
 rs_mesh g10by10Mesh;
 rs_mesh g100by100Mesh;
 rs_mesh gWbyHMesh;
 rs_mesh gTorusMesh;
+rs_mesh gSingleMesh;
 
 rs_font gFontSans;
 rs_font gFontSerif;
@@ -142,7 +150,7 @@
 }
 
 static void bindProgramVertexOrtho() {
-    // Default vertex sahder
+    // Default vertex shader
     rsgBindProgramVertex(gProgVertex);
     // Setup the projection matrix
     rs_matrix4x4 proj;
@@ -198,6 +206,33 @@
     }
 }
 
+// Display sample images in a mesh with different texture
+static void displayMeshWithMultiTexture(int meshMode) {
+    bindProgramVertexOrtho();
+
+    // Fragment shader with texture
+    rsgBindProgramStore(gProgStoreBlendAlpha);
+    rsgBindProgramFragment(gProgFragmentTexture);
+    rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp);
+
+    int meshCount = (int)pow(10.0f, (float)(meshMode + 1));
+
+    float yPos = 0;
+    for (int y = 0; y < meshCount; y++) {
+        yPos = (y + 1) * 50;
+        float xPos = 0;
+        for (int x = 0; x < meshCount; x++) {
+            xPos = (x + 1) * 50;
+            rs_matrix4x4 matrix;
+            rsMatrixLoadTranslate(&matrix, xPos, yPos, 0);
+            rsgProgramVertexLoadModelMatrix(&matrix);
+            int i = (x + y * meshCount) % 100;
+            rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].texture);
+            rsgDrawMesh(gSingleMesh);
+        }
+    }
+}
+
 static float gTorusRotation = 0;
 static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) {
     if (buffer == 0) {
@@ -497,6 +532,8 @@
     "Geo test 25.6k heavy fragment heavy vertex",
     "Geo test 51.2k heavy fragment heavy vertex",
     "Geo test 204.8k small tries heavy fragment heavy vertex",
+    "Mesh with 10 by 10 texture",
+    "Mesh with 100 by 100 texture",
 };
 
 void getTestName(int testIndex) {
@@ -589,6 +626,12 @@
     case 24:
         displayPixelLightSamples(8, true);
         break;
+    case 25:
+        displayMeshWithMultiTexture(0);
+        break;
+    case 26:
+        displayMeshWithMultiTexture(1);
+        break;
     }
 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 922cd4c..8e3ed93 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -425,7 +425,7 @@
         AffineTransform matrixTx = matrixDelegate.getAffineTransform();
 
         // combine them so that the given matrix is applied after.
-        currentTx.preConcatenate(matrixTx);
+        currentTx.concatenate(matrixTx);
 
         // give it to the graphics2D as a new matrix replacing all previous transform
         snapshot.setTransform(currentTx);
@@ -717,7 +717,7 @@
     /*package*/ static void native_drawCircle(int nativeCanvas,
             float cx, float cy, float radius, int paint) {
         native_drawOval(nativeCanvas,
-                new RectF(cx - radius, cy - radius, radius*2, radius*2),
+                new RectF(cx - radius, cy - radius, radius, radius),
                 paint);
     }
 
diff --git a/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java
new file mode 100644
index 0000000..afbe97c0
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/HandlerThread_Delegate.java
@@ -0,0 +1,80 @@
+/*
+ * 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.os;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Delegate overriding selected methods of android.os.HandlerThread
+ *
+ * Through the layoutlib_create tool, selected methods of Handler have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ *
+ */
+public class HandlerThread_Delegate {
+
+    private static Map<BridgeContext, List<HandlerThread>> sThreads =
+            new HashMap<BridgeContext, List<HandlerThread>>();
+
+    public static void cleanUp(BridgeContext context) {
+        List<HandlerThread> list = sThreads.get(context);
+        if (list != null) {
+            for (HandlerThread thread : list) {
+                thread.quit();
+            }
+
+            list.clear();
+            sThreads.remove(context);
+        }
+    }
+
+    // -------- Delegate methods
+
+    @LayoutlibDelegate
+    /*package*/ static void run(HandlerThread theThread) {
+        // record the thread so that it can be quit() on clean up.
+        BridgeContext context = RenderAction.getCurrentContext();
+        List<HandlerThread> list = sThreads.get(context);
+        if (list == null) {
+            list = new ArrayList<HandlerThread>();
+            sThreads.put(context, list);
+        }
+
+        list.add(theThread);
+
+        // ---- START DEFAULT IMPLEMENTATION.
+
+        theThread.mTid = Process.myTid();
+        Looper.prepare();
+        synchronized (theThread) {
+            theThread.mLooper = Looper.myLooper();
+            theThread.notifyAll();
+        }
+        Process.setThreadPriority(theThread.mPriority);
+        theThread.onLooperPrepared();
+        Looper.loop();
+        theThread.mTid = -1;
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 0f3cf571..3ef3288 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -22,7 +22,10 @@
 import org.xmlpull.v1.XmlPullParser;
 import org.xmlpull.v1.XmlPullParserException;
 
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
 import android.util.AttributeSet;
+import android.util.Xml;
 
 import java.io.IOException;
 
@@ -35,6 +38,8 @@
  */
 public class LayoutInflater_Delegate {
 
+    public static boolean sIsInInclude = false;
+
     /**
      * Recursive method used to descend down the xml hierarchy and instantiate
      * views, instantiate their children, and then call onFinishInflate().
@@ -94,4 +99,128 @@
             }
         }
     }
+
+    @LayoutlibDelegate
+    public static void parseInclude(
+            LayoutInflater thisInflater,
+            XmlPullParser parser, View parent, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+
+        int type;
+
+        if (parent instanceof ViewGroup) {
+            final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
+            if (layout == 0) {
+                final String value = attrs.getAttributeValue(null, "layout");
+                if (value == null) {
+                    throw new InflateException("You must specifiy a layout in the"
+                            + " include tag: <include layout=\"@layout/layoutID\" />");
+                } else {
+                    throw new InflateException("You must specifiy a valid layout "
+                            + "reference. The layout ID " + value + " is not valid.");
+                }
+            } else {
+                final XmlResourceParser childParser =
+                    thisInflater.getContext().getResources().getLayout(layout);
+
+                try {
+                    final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
+
+                    while ((type = childParser.next()) != XmlPullParser.START_TAG &&
+                            type != XmlPullParser.END_DOCUMENT) {
+                        // Empty.
+                    }
+
+                    if (type != XmlPullParser.START_TAG) {
+                        throw new InflateException(childParser.getPositionDescription() +
+                                ": No start tag found!");
+                    }
+
+                    final String childName = childParser.getName();
+
+                    if (LayoutInflater.TAG_MERGE.equals(childName)) {
+                        // Inflate all children.
+                        thisInflater.rInflate(childParser, parent, childAttrs, false);
+                    } else {
+                        final View view = thisInflater.createViewFromTag(parent, childName, childAttrs);
+                        final ViewGroup group = (ViewGroup) parent;
+
+                        // We try to load the layout params set in the <include /> tag. If
+                        // they don't exist, we will rely on the layout params set in the
+                        // included XML file.
+                        // During a layoutparams generation, a runtime exception is thrown
+                        // if either layout_width or layout_height is missing. We catch
+                        // this exception and set localParams accordingly: true means we
+                        // successfully loaded layout params from the <include /> tag,
+                        // false means we need to rely on the included layout params.
+                        ViewGroup.LayoutParams params = null;
+                        try {
+                            // ---- START CHANGES
+                            sIsInInclude = true;
+                            // ---- END CHANGES
+
+                            params = group.generateLayoutParams(attrs);
+
+                        } catch (RuntimeException e) {
+                            // ---- START CHANGES
+                            sIsInInclude = false;
+                            // ---- END CHANGES
+
+                            params = group.generateLayoutParams(childAttrs);
+                        } finally {
+                            // ---- START CHANGES
+                            sIsInInclude = false;
+                            // ---- END CHANGES
+
+                            if (params != null) {
+                                view.setLayoutParams(params);
+                            }
+                        }
+
+                        // Inflate all children.
+                        thisInflater.rInflate(childParser, view, childAttrs, true);
+
+                        // Attempt to override the included layout's android:id with the
+                        // one set on the <include /> tag itself.
+                        TypedArray a = thisInflater.mContext.obtainStyledAttributes(attrs,
+                            com.android.internal.R.styleable.View, 0, 0);
+                        int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
+                        // While we're at it, let's try to override android:visibility.
+                        int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
+                        a.recycle();
+
+                        if (id != View.NO_ID) {
+                            view.setId(id);
+                        }
+
+                        switch (visibility) {
+                            case 0:
+                                view.setVisibility(View.VISIBLE);
+                                break;
+                            case 1:
+                                view.setVisibility(View.INVISIBLE);
+                                break;
+                            case 2:
+                                view.setVisibility(View.GONE);
+                                break;
+                        }
+
+                        group.addView(view);
+                    }
+                } finally {
+                    childParser.close();
+                }
+            }
+        } else {
+            throw new InflateException("<include /> can only be used inside of a ViewGroup");
+        }
+
+        final int currentDepth = parser.getDepth();
+        while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
+            // Empty
+        }
+    }
+
+
 }
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 144ec42..3ba3257 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -196,7 +196,7 @@
                 Capability.UNBOUND_RENDERING,
                 Capability.CUSTOM_BACKGROUND_COLOR,
                 Capability.RENDER,
-                //Capability.LAYOUT_ONLY, // disable to run on ADT 10.0 which doesn't include this.
+                Capability.LAYOUT_ONLY,
                 Capability.EMBEDDED_LAYOUT,
                 Capability.VIEW_MANIPULATION,
                 Capability.PLAY_ANIMATION,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index e536028..4fa924d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -71,7 +71,6 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.HashMap;
@@ -342,7 +341,7 @@
                 try {
                     KXmlParser parser = new KXmlParser();
                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                    parser.setInput(new FileReader(xml));
+                    parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
 
                     // set the resource ref to have correct view cookies
                     mBridgeInflater.setResourceReference(resource);
@@ -514,14 +513,13 @@
         BridgeTypedArray ta = ((BridgeResources) mSystemResources).newTypeArray(attrs.length,
                 isPlatformFile);
 
-        // resolve the defStyleAttr value into a IStyleResourceValue
-        StyleResourceValue defStyleValues = null;
-
         // look for a custom style.
         String customStyle = null;
         if (set != null) {
             customStyle = set.getAttributeValue(null /* namespace*/, "style");
         }
+
+        StyleResourceValue customStyleValues = null;
         if (customStyle != null) {
             ResourceValue item = mRenderResources.findResValue(customStyle,
                     false /*forceFrameworkOnly*/);
@@ -530,75 +528,76 @@
             item = mRenderResources.resolveResValue(item);
 
             if (item instanceof StyleResourceValue) {
-                defStyleValues = (StyleResourceValue)item;
+                customStyleValues = (StyleResourceValue)item;
             }
         }
 
-        if (defStyleValues == null) {
-            if (defStyleAttr != 0) {
-                // get the name from the int.
-                String defStyleName = searchAttr(defStyleAttr);
+        // resolve the defStyleAttr value into a IStyleResourceValue
+        StyleResourceValue defStyleValues = null;
 
-                if (defaultPropMap != null) {
-                    defaultPropMap.put("style", defStyleName);
+        if (defStyleAttr != 0) {
+            // get the name from the int.
+            String defStyleName = searchAttr(defStyleAttr);
+
+            if (defaultPropMap != null) {
+                defaultPropMap.put("style", defStyleName);
+            }
+
+            // look for the style in the current theme, and its parent:
+            ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
+
+            if (item != null) {
+                // item is a reference to a style entry. Search for it.
+                item = mRenderResources.findResValue(item.getValue(),
+                        false /*forceFrameworkOnly*/);
+
+                if (item instanceof StyleResourceValue) {
+                    defStyleValues = (StyleResourceValue)item;
                 }
+            } else {
+                Bridge.getLog().error(null,
+                        String.format(
+                                "Failed to find style '%s' in current theme", defStyleName),
+                        null /*data*/);
+            }
+        } else if (defStyleRes != 0) {
+            Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
+            if (value == null) {
+                value = mProjectCallback.resolveResourceId(defStyleRes);
+            }
 
-                // look for the style in the current theme, and its parent:
-                ResourceValue item = mRenderResources.findItemInTheme(defStyleName);
-
-                if (item != null) {
-                    // item is a reference to a style entry. Search for it.
-                    item = mRenderResources.findResValue(item.getValue(),
-                            false /*forceFrameworkOnly*/);
-
-                    if (item instanceof StyleResourceValue) {
-                        defStyleValues = (StyleResourceValue)item;
-                    }
-                } else {
-                    Bridge.getLog().error(null,
-                            String.format(
-                                    "Failed to find style '%s' in current theme", defStyleName),
-                            null /*data*/);
-                }
-            } else if (defStyleRes != 0) {
-                Pair<ResourceType, String> value = Bridge.resolveResourceId(defStyleRes);
-                if (value == null) {
-                    value = mProjectCallback.resolveResourceId(defStyleRes);
-                }
-
-                if (value != null) {
-                    if (value.getFirst() == ResourceType.STYLE) {
-                        // look for the style in the current theme, and its parent:
-                        ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
-                        if (item != null) {
-                            if (item instanceof StyleResourceValue) {
-                                if (defaultPropMap != null) {
-                                    defaultPropMap.put("style", item.getName());
-                                }
-
-                                defStyleValues = (StyleResourceValue)item;
+            if (value != null) {
+                if (value.getFirst() == ResourceType.STYLE) {
+                    // look for the style in the current theme, and its parent:
+                    ResourceValue item = mRenderResources.findItemInTheme(value.getSecond());
+                    if (item != null) {
+                        if (item instanceof StyleResourceValue) {
+                            if (defaultPropMap != null) {
+                                defaultPropMap.put("style", item.getName());
                             }
-                        } else {
-                            Bridge.getLog().error(null,
-                                    String.format(
-                                            "Style with id 0x%x (resolved to '%s') does not exist.",
-                                            defStyleRes, value.getSecond()),
-                                    null /*data*/);
+
+                            defStyleValues = (StyleResourceValue)item;
                         }
                     } else {
                         Bridge.getLog().error(null,
                                 String.format(
-                                        "Resouce id 0x%x is not of type STYLE (instead %s)",
-                                        defStyleRes, value.getFirst().toString()),
+                                        "Style with id 0x%x (resolved to '%s') does not exist.",
+                                        defStyleRes, value.getSecond()),
                                 null /*data*/);
                     }
                 } else {
                     Bridge.getLog().error(null,
                             String.format(
-                                    "Failed to find style with id 0x%x in current theme",
-                                    defStyleRes),
+                                    "Resouce id 0x%x is not of type STYLE (instead %s)",
+                                    defStyleRes, value.getFirst().toString()),
                             null /*data*/);
                 }
+            } else {
+                Bridge.getLog().error(null,
+                        String.format(
+                                "Failed to find style with id 0x%x in current theme",
+                                defStyleRes),
+                        null /*data*/);
             }
         }
 
@@ -623,8 +622,13 @@
                 if (value == null) {
                     ResourceValue resValue = null;
 
-                    // look for the value in the defStyle first (and its parent if needed)
-                    if (defStyleValues != null) {
+                    // look for the value in the custom style first (and its parent if needed)
+                    if (customStyleValues != null) {
+                        resValue = mRenderResources.findItemInStyle(customStyleValues, name);
+                    }
+
+                    // then look for the value in the default Style (and its parent if needed)
+                    if (resValue == null && defStyleValues != null) {
                         resValue = mRenderResources.findItemInStyle(defStyleValues, name);
                     }
 
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
index d6bbebd..7c90a31 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java
@@ -36,7 +36,7 @@
 import android.view.ViewGroup;
 
 import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
 
 /**
  * Custom implementation of {@link LayoutInflater} to handle custom views.
@@ -177,7 +177,7 @@
                     try {
                         KXmlParser parser = new KXmlParser();
                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                        parser.setInput(new FileReader(f));
+                        parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$
 
                         BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
                                 parser, bridgeContext, false);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
index 273e493..345f053 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java
@@ -46,7 +46,6 @@
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileReader;
 import java.io.InputStream;
 
 /**
@@ -244,7 +243,7 @@
                         // give that to our XmlBlockParser
                         parser = new KXmlParser();
                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                        parser.setInput(new FileReader(xml));
+                        parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
                     }
                 }
 
@@ -282,7 +281,7 @@
                     // give that to our XmlBlockParser
                     parser = new KXmlParser();
                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                    parser.setInput(new FileReader(xml));
+                    parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
 
                     return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
                 }
@@ -501,7 +500,7 @@
                     try {
                         KXmlParser parser = new KXmlParser();
                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                        parser.setInput(new FileReader(f));
+                        parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
                         return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
                     } catch (XmlPullParserException e) {
@@ -536,7 +535,7 @@
         try {
             KXmlParser parser = new KXmlParser();
             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-            parser.setInput(new FileReader(f));
+            parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
             return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
         } catch (XmlPullParserException e) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
index b9f769f..d4600a1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java
@@ -36,10 +36,11 @@
 import android.graphics.drawable.Drawable;
 import android.util.DisplayMetrics;
 import android.util.TypedValue;
+import android.view.LayoutInflater_Delegate;
 import android.view.ViewGroup.LayoutParams;
 
 import java.io.File;
-import java.io.FileReader;
+import java.io.FileInputStream;
 import java.util.Arrays;
 import java.util.Map;
 
@@ -315,7 +316,7 @@
             try {
                 KXmlParser parser = new KXmlParser();
                 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                parser.setInput(new FileReader(f));
+                parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
                 BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
                         parser, mContext, resValue.isFramework());
@@ -471,40 +472,23 @@
      */
     @Override
     public int getDimensionPixelSize(int index, int defValue) {
-        if (mResourceData[index] == null) {
+        try {
+            return getDimension(index);
+        } catch (RuntimeException e) {
+            if (mResourceData[index] != null) {
+                String s = mResourceData[index].getValue();
+
+                if (s != null) {
+                    // looks like we were unable to resolve the dimension value
+                    Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                            String.format(
+                                "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+                                s, mNames[index]), null /*data*/);
+                }
+            }
+
             return defValue;
         }
-
-        String s = mResourceData[index].getValue();
-
-        if (s == null) {
-            return defValue;
-        } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
-                s.equals(BridgeConstants.FILL_PARENT)) {
-            return LayoutParams.MATCH_PARENT;
-        } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
-            return LayoutParams.WRAP_CONTENT;
-        } else if (RenderResources.REFERENCE_NULL.equals(s)) {
-            return defValue;
-        }
-
-        if (ResourceHelper.stringToFloat(s, mValue)) {
-            float f = mValue.getDimension(mBridgeResources.mMetrics);
-
-            final int res = (int)(f+0.5f);
-            if (res != 0) return res;
-            if (f == 0) return 0;
-            if (f > 0) return 1;
-            return defValue; // this is basically unreachable.
-        }
-
-        // looks like we were unable to resolve the dimension value
-        Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
-                String.format(
-                    "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
-                    s, mNames[index]), null /*data*/);
-
-        return defValue;
     }
 
     /**
@@ -521,7 +505,20 @@
      */
     @Override
     public int getLayoutDimension(int index, String name) {
-        return getDimensionPixelSize(index, 0);
+        try {
+            // this will throw an exception
+            return getDimension(index);
+        } catch (RuntimeException e) {
+
+            if (LayoutInflater_Delegate.sIsInInclude) {
+                throw new RuntimeException();
+            }
+
+            Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+                    "You must supply a " + name + " attribute.", null);
+
+            return 0;
+        }
     }
 
     @Override
@@ -529,6 +526,36 @@
         return getDimensionPixelSize(index, defValue);
     }
 
+    private int getDimension(int index) {
+        if (mResourceData[index] == null) {
+            throw new RuntimeException();
+        }
+
+        String s = mResourceData[index].getValue();
+
+        if (s == null) {
+            throw new RuntimeException();
+        } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
+                s.equals(BridgeConstants.FILL_PARENT)) {
+            return LayoutParams.MATCH_PARENT;
+        } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+            return LayoutParams.WRAP_CONTENT;
+        } else if (RenderResources.REFERENCE_NULL.equals(s)) {
+            throw new RuntimeException();
+        }
+
+        if (ResourceHelper.stringToFloat(s, mValue)) {
+            float f = mValue.getDimension(mBridgeResources.mMetrics);
+
+            final int res = (int)(f+0.5f);
+            if (res != 0) return res;
+            if (f == 0) return 0;
+            if (f > 0) return 1;
+        }
+
+        throw new RuntimeException();
+    }
+
     /**
      * Retrieve a fractional unit attribute at <var>index</var>.
      *
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 0c4b0d3..060e6ee 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -73,7 +73,7 @@
         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
         parser.setInput(
                 getClass().getResourceAsStream(layoutPath),
-                "UTF8");
+                "UTF8"); //$NON-NLS-1$
 
         BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
                 parser, (BridgeContext) context, false /*platformFile*/);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index 8e80c21..6194f5d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -29,6 +29,7 @@
 import com.android.layoutlib.bridge.android.BridgeContext;
 import com.android.resources.ResourceType;
 
+import android.os.HandlerThread_Delegate;
 import android.util.DisplayMetrics;
 
 import java.util.concurrent.TimeUnit;
@@ -228,6 +229,10 @@
     private void tearDown() {
         // Make sure to remove static references, otherwise we could not unload the lib
         mContext.disposeResources();
+
+        // quit HandlerThread created during this session.
+        HandlerThread_Delegate.cleanUp(sCurrentContext);
+
         sCurrentContext = null;
 
         Bridge.setLog(null);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 96ab30f..e5efa4e 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -44,7 +44,6 @@
 
 import java.io.File;
 import java.io.FileInputStream;
-import java.io.FileReader;
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.MalformedURLException;
@@ -124,7 +123,7 @@
                     // providing an XmlPullParser
                     KXmlParser parser = new KXmlParser();
                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                    parser.setInput(new FileReader(f));
+                    parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
                     BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
                             parser, context, resValue.isFramework());
@@ -206,7 +205,7 @@
                     // let the framework inflate the Drawable from the XML file.
                     KXmlParser parser = new KXmlParser();
                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
-                    parser.setInput(new FileReader(f));
+                    parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
 
                     BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
                             parser, context, value.isFramework());
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 3252fb49..70d5446 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -44,7 +44,7 @@
 
         InputStream input = this.getClass().getClassLoader().getResourceAsStream(
             "com/android/layoutlib/testdata/layout1.xml");
-        parser.setInput(input, null /*encoding*/);
+        parser.setInput(input, "UTF-8"); //$NON-NLS-1$
 
         assertEquals(XmlPullParser.START_DOCUMENT, parser.next());
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index eff6bbc..5c60318 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -99,8 +99,10 @@
         "android.content.res.Resources$Theme#resolveAttribute",
         "android.graphics.BitmapFactory#finishDecode",
         "android.os.Handler#sendMessageAtTime",
+        "android.os.HandlerThread#run",
         "android.os.Build#getString",
         "android.view.LayoutInflater#rInflate",
+        "android.view.LayoutInflater#parseInclude",
         "android.view.View#isInEditMode",
         "com.android.internal.util.XmlUtils#convertValueToInt",
         // TODO: comment out once DelegateClass is working
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index dc66989..afa696c 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -82,6 +82,7 @@
     private WifiScanProcess mWifiScanProcess;
     private WifiManager.WifiLock mWifiLock;
     private boolean mWifiOnly;
+    private IntervalMeasurementProcess mIntervalMeasurementProcess;
 
     private MyExecutor mExecutor;
 
@@ -96,6 +97,7 @@
     private ConnectivityReceiver mConnectivityReceiver;
     private boolean mWifiEnabled;
     private SipWakeLock mMyWakeLock;
+    private int mKeepAliveInterval;
 
     /**
      * Starts the SIP service. Do nothing if the SIP API is not supported on the
@@ -441,12 +443,14 @@
 
             if (connected) {
                 mLocalIp = determineLocalIp();
+                mKeepAliveInterval = -1;
                 for (SipSessionGroupExt group : mSipGroups.values()) {
                     group.onConnectivityChanged(true);
                 }
                 if (isWifi && (mWifiLock != null)) stopWifiScanner();
             } else {
                 mMyWakeLock.reset(); // in case there's a leak
+                stopPortMappingMeasurement();
                 if (isWifi && (mWifiLock != null)) startWifiScanner();
             }
         } catch (SipException e) {
@@ -454,6 +458,18 @@
         }
     }
 
+    private void stopPortMappingMeasurement() {
+        if (mIntervalMeasurementProcess != null) {
+            mIntervalMeasurementProcess.stop();
+            mIntervalMeasurementProcess = null;
+        }
+    }
+
+    private void startPortMappingLifetimeMeasurement(SipSessionGroup group) {
+        mIntervalMeasurementProcess = new IntervalMeasurementProcess(group);
+        mIntervalMeasurementProcess.start();
+    }
+
     private synchronized void addPendingSession(ISipSession session) {
         try {
             cleanUpPendingSessions();
@@ -687,6 +703,93 @@
         }
     }
 
+    private class IntervalMeasurementProcess extends SipSessionAdapter
+            implements Runnable {
+        private static final String TAG = "\\INTERVAL/";
+        private static final int MAX_INTERVAL = 120; // seconds
+        private static final int MIN_INTERVAL = SHORT_EXPIRY_TIME;
+        private static final int PASS_THRESHOLD = 6;
+        private SipSessionGroupExt mGroup;
+        private SipSessionGroup.SipSessionImpl mSession;
+        private boolean mRunning;
+        private int mMinInterval = 10;
+        private int mMaxInterval = MAX_INTERVAL;
+        private int mInterval = MAX_INTERVAL / 2;
+        private int mPassCounter = 0;
+        private WakeupTimer mTimer = new WakeupTimer(mContext);
+
+
+        public IntervalMeasurementProcess(SipSessionGroup group) {
+            try {
+                mGroup =  new SipSessionGroupExt(
+                        group.getLocalProfile(), null, null);
+                mSession = (SipSessionGroup.SipSessionImpl)
+                        mGroup.createSession(this);
+            } catch (Exception e) {
+                Log.w(TAG, "start interval measurement error: " + e);
+            }
+        }
+
+        public void start() {
+            if (mRunning) return;
+            mRunning = true;
+            mTimer.set(mInterval * 1000, this);
+            if (DEBUGV) Log.v(TAG, "start interval measurement");
+            run();
+        }
+
+        public void stop() {
+            mRunning = false;
+            mTimer.cancel(this);
+        }
+
+        private void restart() {
+            mTimer.cancel(this);
+            mTimer.set(mInterval * 1000, this);
+        }
+
+        private void calculateNewInterval() {
+            if (!mSession.isReRegisterRequired()) {
+                if (++mPassCounter != PASS_THRESHOLD) return;
+                // update the interval, since the current interval is good to
+                // keep the port mapping.
+                mKeepAliveInterval = mMinInterval = mInterval;
+            } else {
+                // Since the rport is changed, shorten the interval.
+                mSession.clearReRegisterRequired();
+                mMaxInterval = mInterval;
+            }
+            if ((mMaxInterval - mMinInterval) < MIN_INTERVAL) {
+                // update mKeepAliveInterval and stop measurement.
+                stop();
+                mKeepAliveInterval = mMinInterval;
+                if (DEBUGV) Log.v(TAG, "measured interval: " + mKeepAliveInterval);
+            } else {
+                // calculate the new interval and continue.
+                mInterval = (mMaxInterval + mMinInterval) / 2;
+                mPassCounter = 0;
+                if (DEBUGV) {
+                    Log.v(TAG, " current interval: " + mKeepAliveInterval
+                            + "test new interval: " + mInterval);
+                }
+                restart();
+            }
+        }
+
+        public void run() {
+            synchronized (SipService.this) {
+                if (!mRunning) return;
+                try {
+                    mSession.sendKeepAlive();
+                    calculateNewInterval();
+                } catch (Throwable t) {
+                    stop();
+                    Log.w(TAG, "interval measurement error: " + t);
+                }
+            }
+        }
+    }
+
     // KeepAliveProcess is controlled by AutoRegistrationProcess.
     // All methods will be invoked in sync with SipService.this.
     private class KeepAliveProcess implements Runnable {
@@ -694,6 +797,7 @@
         private static final int INTERVAL = 10;
         private SipSessionGroup.SipSessionImpl mSession;
         private boolean mRunning = false;
+        private int mInterval = INTERVAL;
 
         public KeepAliveProcess(SipSessionGroup.SipSessionImpl session) {
             mSession = session;
@@ -705,6 +809,12 @@
             mTimer.set(INTERVAL * 1000, this);
         }
 
+        private void restart(int duration) {
+            if (DEBUG) Log.d(TAG, "Refresh NAT port mapping " + duration + "s later.");
+            mTimer.cancel(this);
+            mTimer.set(duration * 1000, this);
+        }
+
         // timeout handler
         public void run() {
             synchronized (SipService.this) {
@@ -721,6 +831,10 @@
                         mMyWakeLock.acquire(mSession);
                         mSession.register(EXPIRY_TIME);
                     }
+                    if (mKeepAliveInterval > mInterval) {
+                        mInterval = mKeepAliveInterval;
+                        restart(mInterval);
+                    }
                 } catch (Throwable t) {
                     Log.w(TAG, "keepalive error: " + t);
                 }
@@ -761,6 +875,17 @@
                 // return right away if no active network connection.
                 if (mSession == null) return;
 
+                synchronized (SipService.this) {
+                    if (isBehindNAT(mLocalIp)
+                            && (mIntervalMeasurementProcess == null)
+                            && (mKeepAliveInterval == -1)) {
+                        // Start keep-alive interval measurement, here we allow
+                        // the first profile only as the target service provider
+                        // to measure the life time of NAT port mapping.
+                        startPortMappingLifetimeMeasurement(group);
+                    }
+                }
+
                 // start unregistration to clear up old registration at server
                 // TODO: when rfc5626 is deployed, use reg-id and sip.instance
                 // in registration to avoid adding duplicate entries to server
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index aa616a9..b5f1a17 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -397,7 +397,7 @@
 
         // for registration
         boolean mReRegisterFlag = false;
-        int mRPort;
+        int mRPort = 0;
 
         // lightweight timer
         class SessionTimer {
@@ -447,7 +447,6 @@
             mState = SipSession.State.READY_TO_CALL;
             mInviteReceived = null;
             mPeerSessionDescription = null;
-            mRPort = 0;
             mAuthenticationRetryCount = 0;
 
             if (mDialog != null) mDialog.delete();
diff --git a/wifi/java/android/net/wifi/WifiConfigStore.java b/wifi/java/android/net/wifi/WifiConfigStore.java
index 7f9fc31..d83b968 100644
--- a/wifi/java/android/net/wifi/WifiConfigStore.java
+++ b/wifi/java/android/net/wifi/WifiConfigStore.java
@@ -478,6 +478,21 @@
         }
     }
 
+    /**
+     * clear IP configuration for a given network id
+     */
+    static void clearIpConfiguration(int netId) {
+        synchronized (sConfiguredNetworks) {
+            WifiConfiguration config = sConfiguredNetworks.get(netId);
+            if (config != null && config.linkProperties != null) {
+                // Clear everything except proxy
+                ProxyProperties proxy = config.linkProperties.getHttpProxy();
+                config.linkProperties.clear();
+                config.linkProperties.setHttpProxy(proxy);
+            }
+        }
+    }
+
 
     /**
      * Fetch the proxy properties for a given network id
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index bd6da64..5307070 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1456,10 +1456,13 @@
 
         /* Clear network properties */
         mLinkProperties.clear();
+        /* Clear IP settings if the network used DHCP */
+        if (!WifiConfigStore.isUsingStaticIp(mLastNetworkId)) {
+            WifiConfigStore.clearIpConfiguration(mLastNetworkId);
+        }
 
         mLastBssid= null;
         mLastNetworkId = WifiConfiguration.INVALID_NETWORK_ID;
-
     }
 
     void handlePreDhcpSetup() {